Writing Surface Shaders 表面着色器写法
本文档主要是对Unity官方手册的个人理解与总结(其实以翻译记录为主:>)
仅作为个人学习使用,不得作为商业用途,欢迎转载,并请注明出处。
文章中涉及到的操作都是基于Unity2018.2版本
参考链接:https://docs.unity3d.com/Manual/SL-SurfaceShaders.html
Writing shaders that interact with lighting is complex. There are different light types, different shadow options, different rendering paths (forward and deferred rendering), and the shader should somehow handle all that complexity.
与灯光互动的着色写法是复杂的。有不同的光类型,不同的阴影选项,不同的渲染路径(向前和延迟渲染),着色器应该以某种方式处理所有的复杂性。
Surface Shaders in Unity is a code generation approach that makes it much easier to write lit shaders than using low level vertex/pixel shader programs. Note that there are no custom languages, magic or ninjas involved in Surface Shaders; it just generates all the repetitive code that would have to be written by hand. You still write shader code in HLSL.
Unity中的Surface着色器是一种代码生成方法,它比使用低级的顶点(vertex)/像素(pixel)着色程序更容易编写光照着色。请注意,在表面着色器中没有定制语言、魔术或忍者;它只是可生成所有需要手工编写的重复代码。你仍然可以在HLSL中编写shader 代码。
For some examples, take a look at Surface Shader Examples and Surface Shader Custom Lighting Examples.
在一些例子中,我们来看看表面着色器的例子和表面着色器自定义的光照例子。
How it works
You define a “surface function” that takes any UVs or data you need as input, and fills in output structure SurfaceOutput. SurfaceOutput basically describes properties of the surface (it’s albedo color, normal, emission, specularity etc.). You write this code in HLSL.
你定义了一个“表面函数”,它接受你需要的任何UVs或数据作为输入,并填充输出结构的surface输出。表面输出基本上描述了表面的性质(它是反射率(albedo)、法线、自发光、高光等)。相当于你在HLSL中写的代码。
Surface Shader compiler then figures out what inputs are needed, what outputs are filled and so on, and generates actual vertex&pixel shaders, as well as rendering passes to handle forward and deferred rendering.
然后,Surface着色器会计算出需要什么输入,什么输出被填满等等,并生成实际的顶点和像素着色器,以及渲染通道来处理前向和延迟渲染。
Standard output structure of surface shaders is this:
表面着色器的标准输出结构如下:
struct SurfaceOutput
{
fixed3 Albedo; // diffuse color
fixed3 Normal; // tangent space normal, if written
fixed3 Emission;
half Specular; // specular power in 0..1 range
fixed Gloss; // specular intensity
fixed Alpha; // alpha for transparencies
};
In Unity 5, surface shaders can also use physically based lighting models. Built-in Standard and StandardSpecular lighting models (see below) use these output structures respectively:
在Unity 5中,表面着色器也可以使用基于物理的照明模型。内置的标准和标准的照明模型(见下文)分别使用这些输出结构:
struct SurfaceOutputStandard
{
fixed3 Albedo; // base (diffuse or specular) color
fixed3 Normal; // tangent space normal, if written
half3 Emission;
half Metallic; // 0=non-metal, 1=metal
half Smoothness; // 0=rough, 1=smooth
half Occlusion; // occlusion (default 1)
fixed Alpha; // alpha for transparencies
};
struct SurfaceOutputStandardSpecular
{
fixed3 Albedo; // diffuse color
fixed3 Specular; // specular color
fixed3 Normal; // tangent space normal, if written
half3 Emission;
half Smoothness; // 0=rough, 1=smooth
half Occlusion; // occlusion (default 1)
fixed Alpha; // alpha for transparencies
};
Samples
See Surface Shader Examples, Surface Shader Custom Lighting Examples and Surface Shader Tessellation pages.
Surface Shader compile directives
Surface shader is placed inside CGPROGRAM…ENDCG block, just like any other shader. The differences are:
表面着色器被放置在CGPROGRAM…ENDCG块中,就像任何其他的着色器一样。差异是:
- It must be placed inside SubShader block, not inside Pass. Surface shader will compile into multiple passes itself.
它必须被放置在SubShader块中,而不是在内部Pass中。表面着色器将被编译成多个通道本身。 - It uses #pragma surface … directive to indicate it’s a surface shader.
它使用的是#pragma surface… 代表它是一个表面着色器。
The #pragma surface directive is:
#pragma surface surfaceFunction lightModel [optionalparams]
Required parameters 参数
- surfaceFunction - which Cg function has surface shader code. The function should have the form of void surf (Input IN, inout SurfaceOutput o), where Input is a structure you have defined. Input should contain any texture coordinates and extra automatic variables needed by surface function.
surfaceFunction:有表面着色代码的Cg函数。该函数应该具有void surf的形式(Input IN, inout SurfaceOutput o),其中输入是您定义的结构。输入应该包含任何纹理坐标和表面功能所需的额外自动变量。 - lightModel - lighting model to use. Built-in ones are physically based Standard and StandardSpecular, as well as simple non-physically based Lambert (diffuse) and BlinnPhong (specular). See Custom Lighting Models page for how to write your own.
光照模型:使用的照明模型。内置的是基于物理的Standard和StandardSpecular,以及简单的非物理的兰伯特(漫射)和BlinnPhong(镜面)。参见自定义照明模型页面,了解如何编写自己的模型。- Standard: lighting model uses SurfaceOutputStandard output struct, and matches the Standard (metallic workflow) shader in Unity. 输出结构是SurfaceOutputStandard,相当于内置的Standard(金属工作流) Shader。
- StandardSpecular: lighting model uses SurfaceOutputStandardSpecular output struct, and matches the Standard (specular setup) shader in Unity. 输出结构是SurfaceOutputStandardSpecular,相当于内置的Standard(高光设置) Shader。
- Lambert and BlinnPhong lighting models are not physically based (coming from Unity 4.x), but the shaders using them can be faster to render on low-end hardware.
兰伯特和BlinnPhong照明模型不是基于物理的(来自Unity 4.x),但是使用它们的着色器可以更快地在低端硬件上呈现。
Optional parameters
Transparency and alpha testing is controlled by alpha and alphatest directives. Transparency can typically be of two kinds: traditional alpha blending (used for fading objects out) or more physically plausible “premultiplied blending” (which allows semitransparent surfaces to retain proper specular reflections). Enabling semitransparency makes the generated surface shader code contain blending commands; whereas enabling alpha cutout will do a fragment discard in the generated pixel shader, based on the given variable.
Transparency 和alpha testing是由alpha和alphatest的指令控制的。透明通常有两种类型:传统的alpha混合(用于淡出物体),或者更多的物理上看似合理的“预混合”(这使得半透明的表面能够保留适当的镜面反射)。启用半透明使生成的表面着色代码包含混合命令;而启用alpha cutout将根据给定的变量在生成的像素着色器中执行剔除。
- alpha or alpha:auto - Will pick fade-transparency (same as alpha:fade) for simple lighting functions, and premultiplied transparency (same as alpha:premul) for physically based lighting functions. 对于简单的照明功能,将选择 淡出-透明(同于 alpha:fade),并为基于物理的照明功能选择预混合透明(同于alpha:premul)。
- alpha:blend - Enable alpha blending. 启用 alpha 融合
- alpha:fade - Enable traditional fade-transparency. 启用传统的淡出-透明
- alpha:premul - Enable premultiplied alpha transparency.启用预混合alpha透明
- alphatest:VariableName - Enable alpha cutout transparency. Cutoff value is in a float variable with ariableName. You’ll likely also want to use addshadow directive to generate proper shadow caster pass.
启用alpha剔除的透明。剔除值是一个浮点变量的变量名。您可能还希望使用addshadow指令来生成适当的阴影投射pass。 - keepalpha - By default opaque surface shaders write 1.0 (white) into alpha channel, no matter what’s output in the Alpha of output struct or what’s returned by the lighting function. Using this option allows keeping lighting function’s alpha value even for opaque surface shaders. 默认情况下,不透明的表面着色器将1.0(白色)写进Alpha通道,不管输出结构的Alpha值是多少,或者是由光照函数返回的结果。使用这个选项,即使对于不透明的表面着色器,也可以保持照明功能的alpha值。
- decal:add - Additive decal shader (e.g. terrain AddPass). This is meant for objects that lie atop of other surfaces, and use additive blending. See Surface Shader Examples。 加法贴花着色器(例如,地形AddPass)。这是指位于其他表面之上的物体,并使用加法混合。
- decal:blend - Semitransparent decal shader. This is meant for objects that lie atop of other surfaces, and use alpha blending. See Surface Shader Examples 半透明的贴花着色器。这是指位于其他表面之上的物体,并使用alpha混合。参见表面着色器示例
Custom modifier functions can be used to alter or compute incoming vertex data, or to alter final computed fragment color.
Custom modifier 函数可以用来修改或计算传入的顶点数据,或者改变最终计算的片段颜色。
- vertex:VertexFunction - Custom vertex modification function. This function is invoked at start of generated vertex shader, and can modify or compute per-vertex data. See Surface Shader Examples. 自定义顶点修改函数。这个函数在生成的顶点着色器开始时被调用,并且可以修改或计算每个顶点数据。参见表面着色器示例。
- finalcolor:ColorFunction - Custom final color modification function. See Surface Shader Examples.定制最后的颜色修改函数。
- finalgbuffer:ColorFunction - Custom deferred path for altering gbuffer content. 延迟渲染自定义改变gbuffer的内容。
- finalprepass:ColorFunction - Custom prepass base path. 基于传统延迟渲染路径自定义 prepass。
Shadows and Tessellation - additional directives can be given to control how shadows and tessellation is handled.可以提供额外的指令来控制阴影和曲面细分的处理方式。
- addshadow - Generate a shadow caster pass. Commonly used with custom vertex modification, so that shadow casting also gets any procedural vertex animation. Often shaders don’t need any special shadows handling, as they can just use shadow caster pass from their fallback. 生成一个阴影投射通道。通常用于自定义顶点修改,因此阴影投射也会得到任何程序化顶点动画。通常着色器不需要任何特殊的阴影处理,因为它们可以从它们的回退中使用阴影投射器。
- fullforwardshadows - Support all light shadow types in Forward rendering path. By default shaders only support shadows from one directional light in forward rendering (to save on internal shader variant count). If you need point or spot light shadows in forward rendering, use this directive.
在前向渲染路径中支持所有的光阴影类型。在默认情况下,阴影只支持向前渲染的一个方向光的阴影(以节省内部着色器变体计数)。如果你需要在正向渲染中使用点或聚光灯的阴影,请使用这个指令。 - tessellate:TessFunction - use DX11 GPU tessellation; the function computes tessellation factors. See Surface Shader Tessellation for details. 使用DX11 GPU曲面细分;该函数计算曲面细分因子。详情请参阅表面着色器曲面细分。
Code generation options - by default generated surface shader code tries to handle all possible lighting/shadowing/lightmap scenarios. However in some cases you know you won’t need some of them, and it is possible to adjust generated code to skip them. This can result in smaller shaders that are faster to load.
代码生成选项:默认情况下,生成的表面着色器代码试图处理所有可能的光照/阴影/lightmap等场景。然而,在某些情况下,您知道您不需要其中的一些,并且可以调整生成的代码来跳过它们。这可能导致更小的着色器更快地加载。
- exclude_path:deferred, exclude_path:forward, exclude_path:prepass - Do not generate passes for given rendering path (Deferred Shading, Forward and Legacy Deferred respectively). 不要为给定的渲染路径生成通道(分别为延迟、前向、传统延迟)
- noshadow - Disables all shadow receiving support in this shader. 着色器中禁用所有的阴影接收支持。
- noambient - Do not apply any ambient lighting or light probes. 不要使用任何环境光照或光照探测器。
- novertexlights - Do not apply any light probes or per-vertex lights in Forward rendering. 不要在正向渲染中应用任何光照探测器或顶点光照。
- nolightmap - Disables all lightmapping support in this shader. 禁用所有的光照图支持
- nodynlightmap - Disables runtime dynamic global illumination support in this shader. 禁用运行时GI支持
- nodirlightmap - Disables directional lightmaps support in this shader. 禁用定向光照图支持
- nofog - Disables all built-in Fog support.禁用雾支持
- nometa - Does not generate a “meta” pass (that’s used by lightmapping & dynamic global illumination to extract surface information). 不会产生一个“元”通道(这是由光照图和动态全局照明来提取表面信息)。
- noforwardadd - Disables Forward rendering additive pass. This makes the shader support one full directional light, with all other lights computed per-vertex/SH. Makes shaders smaller as well. 禁用前向渲染additive通道。这使得着色器支持一个全方向的光,所有其他的灯都是按每一个vertex/SH(球面调和)计算的。使着色器变得更小。
- nolppv - Disables Light Probe Proxy Volume support in this shader. 禁用光照探针代理体积
- noshadowmask - Disables Shadowmask support for this shader (both Shadowmask
and Distance Shadowmask). 禁用Shadowmask(Shadowmask和Distance Shadowmask)。
Miscellaneous options 其他选项
- softvegetation - Makes the surface shader only be rendered when Soft Vegetation is on. 软植-表面着色器只在软植被出现时渲染
- interpolateview - Compute view direction in the vertex shader and interpolate it; instead of computing it in the pixel shader. This can make the pixel shader faster, but uses up one more texture interpolator.视角插值-在顶点着色器中计算视角方向并对其进行插值;而不是在像素着色器中计算它。这可以使像素着色器更快,但又多使用了一个纹理插值器。
- halfasview - Pass half-direction vector into the lighting function instead of view-direction. Half-direction will be computed and normalized per vertex. This is faster, but not entirely correct. 半视角-将半视角方向向量传递到光照函数中,而不是视角方向。在每个顶点半视角方向将被计算并归一化。这是更快的,但不是完全正确的。
- approxview - Removed in Unity 5.0. Use interpolateview instead. 近似视角-Unity 5.0被移除。用interpolateview 替代。
- dualforward - Use dual lightmaps in forward rendering path. 双前向 - 在前向渲染路径中用双光照图。
- dithercrossfade - Makes the surface shader support dithering effects. You can then apply this shader to GameObjects that use an LOD Group component configured for cross-fade transition mode. 同时淡出淡入- 使表面着色器支持抖动效果。然后,您可以将这个着色器应用到有LOD组组件并为同时淡出淡入过渡做了配置的GameObjects 。
To see what exactly is different from using different options above, it can be helpful to use “Show Generated Code” button in the Shader Inspector.
要了解与使用上面的不同选项有什么不同,在Shader检查器中使用“显示生成代码”按钮是很有帮助的。
Surface Shader input structure 表面着色器输入结构体
The input structure Input generally has any texture coordinates needed by the shader. Texture coordinates must be named “uv” followed by texture name (or start it with “uv2” to use second texture coordinate set).
输入结构体输入通常具有着色器所需要的纹理坐标。纹理坐标必须被命名为“uv”,然后是纹理名称(或者用“uv2”开始使用第二个纹理坐标集)。
Additional values that can be put into Input structure:
可以放入输入结构体的附加值:
- float3 viewDir - contains view direction, for computing Parallax effects, rim lighting etc 包含视图方向,用于计算视差效果,边缘照明等
- float4 with COLOR semantic - contains interpolated per-vertex color.包含每个顶点色的内插
- float4 screenPos - contains screen space position for reflection or screenspace effects. Note that this is not suitable for GrabPass; you need to compute custom UV yourself using ComputeGrabScreenPos function.包含用于反射或屏幕空间效果的屏幕空间位置。请注意,这不适用于GrabPass;您需要使用ComputeGrabScreenPos函数来计算定制UV。
- float3 worldPos - contains world space position. 包含世界空间位置。
- float3 worldRefl - contains world reflection vector if surface shader does not write to o.Normal. See Reflect-Diffuse shader for example. 如果表面着色器未写 o.Normal,则包含世界反射向量。比如反射-漫反射的着色器。
- float3 worldNormal - contains world normal vector if surface shader does not write to o.Normal.如果表面着色器未写 o.Normal,则包含世界法向量。
- float3 worldRefl; INTERNAL_DATA - contains world reflection vector if surface shader writes to o.Normal. To get the reflection vector based on per-pixel normal map, use WorldReflectionVector (IN, o.Normal). See Reflect-Bumped shader for example. 包含世界反射向量如果表面着色器写入 o.Normal。为了得到基于每个像素法线贴图的反射向量,使用WorldReflectionVector(IN, o.Normal)。例如,反射-法线着色器。
- float3 worldNormal; INTERNAL_DATA - contains world normal vector if surface shader writes to o.Normal. To get the normal vector based on per-pixel normal map, use WorldNormalVector (IN, o.Normal). 包含世界法线向量如果表面着色器写入 o.Normal。为了得到基于每个像素法线贴图的法线向量,使用WorldNormalVector (IN, o.Normal)。
Surface Shaders and DirectX 11 HLSL syntax 表面着色器与DX11 HLSL语法
Currently some parts of surface shader compilation pipeline do not understand DirectX 11-specific HLSL syntax, so if you’re using HLSL features like StructuredBuffers, RWTextures and other non-DX9 syntax, you have to wrap it into a DX11-only preprocessor macro.
目前,部分表面着色器编译管线并不兼容DirectX 11特定的HLSL语法,因此,如果您使用的是HLSL特性,比如结构体缓冲区、RW纹理和其他非DX9语法,那么您必须将其包装成一个仅DX11的预处理器宏。
See Platform Specific Differences and Shading Language pages for details.