《Unity着色器圣经》8.0.5 | 内置渲染管线下的阴影贴图优化

目录索引

译文

我们在前面几个小节中实现阴影的过程可以通过使用 Unity 中内置的宏来优化:

  • SHADOW_COORDS(n)
  • TRANSFER_SHADOW(output)
  • SHADOW_ATTENUATION(output)

我们可以用这些宏替换一些原先代码中的一些函数或变量,就像我们在阴影投射的 pass 中所做的那样。

如果我们想要在着色器代码中使用这些宏,就需要在 CGPROGRAM 语义块中添加 #include “AutoLight.cginc” 和 #pragma multi_compile_fwdbase 两行代码,后者负责编译由 ForwardBase 传递的平行光产生的所有光照贴图和阴影变体。

// default color Pass
Pass
{
    Name "Shadow Map Texture"

    Tags { "LightMode"="ForwardBase" }

    CGPROGRAM
    ...
    #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight

    #include "UnityCG.cginc"
    #include "AutoLight.cginc"

在 #pragma 后定义的 nolightmap、nodirlightmap、nodynlightmap 和 novertexlight 对应于可选参数,我们可以通过定义这些参数来添加或删除阴影中的功能。

请注意,如果使用宏来实现阴影贴图,我们必须在用作输入/输出的向量中使用一些预定义的名称,否则代码将会报错。

struct appdata
{
    float4 vertex : POSITION;
    // float2 uv : TEXCOORD0;
    float2 texcoord : TEXCOORD0;
};

通常来说,我们习惯于在使用到 uv 的场合下声明一个语义为 TEXCOORD[n] 的向量 uv。但如果我们使用宏,就必须用 texcoord 替换 uv(如上图所示),否则宏的内部操作将无法读取 UV 坐标来生成阴影贴图。

除了这个命名要求之外,顶点输出结构体中的向量 vertex 必须重命名为 pos,这样宏的内部进程才能写入阴影贴图的 UV 坐标。此外,我们还必须在代码中包含宏 SHADOW_COORDS(n),其中包含了我们传递给片元着色器的 UV 坐标 (o.shadowCoord)。

struct v2f 
{ 
    float2 uv : TEXCOORD0; 
    // 将阴影数据存入TEXCOORD1 
    SHADOW_COORDS(1) // 结尾没有分号 
    // float4 vertex : SV_POSITION; 
    float4 pos : SV_POSITION; 
    … 
};

在阴影贴图纹理 pass 中声明输入和输出后,我们就可以在顶点着色器阶段同步这些值了。

v2f vert (appdata v) 
{ 
    v2f o; 
    o.pos = UnityObjectToClipPos(v.vertex); 
    o.uv = v.texcoord; 
    // 在片元着色器中变换着色器的UV坐标
    TRANSFER_SHADOW(o) // 结尾没有分号

    return o; 
}

TRANSFER_SHADOW(output) 宏与我们在上一小节中编写的 NDCToUV 函数功能相同,计算了阴影纹理的 UV 坐标。现在,我们可以将坐标传入片元着色器。

fixed4 frag (v2f i) : SV_Target 
{ 
    fixed4 col = tex2D(_MainTex, i.uv); 
    // 使用阴影
    fixed shadow = SHADOW_ATTENUATION(i); 
    col.rgb *= shadow; 

    return col; 
}

最后,SHADOW_ATTENUATION(output) 包含了阴影纹理以及投影,该函数可以保存为一维向量,因为纹理投影只使用一个通道(Alpha)。


原文对照

The process of implementing shadows that we saw earlier for color passing, can be optimized through the use of macros included in Unity, these macros refer to:

  • SHADOW_COORDS(n)
  • TRANSFER_SHADOW(output)
  • SHADOW_ATTENUATION(output)

We can replace some functions and/or variables to optimize its process, in the same way, we did in the shadow caster pass.

If we want to use these macros, we will need the file #include “AutoLight.cginc” in our program, in addition to #pragma multi_compile_fwdbase, which is responsible for compiling all the lightmap and shadow variants produced by directional lights for the ForwardBase pass.

// default color Pass
Pass
{
    Name "Shadow Map Texture"

    Tags { "LightMode"="ForwardBase" }

    CGPROGRAM
    ...
    #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight

    #include "UnityCG.cginc"
    #include "AutoLight.cginc"

The variables defined after the #pragma (nolightmap, nodirlightmap, nodynlightmap and novertexlight) correspond to optional parameters that we can define to add or remove functionality in the shadow behavior.

Note that, if we use the macros for the implementation of the shadow map, we must use some predefined names in the vectors that we use as input/output, otherwise our code will generate an error.

struct appdata
{
    float4 vertex : POSITION;
    // float2 uv : TEXCOORD0;
    float2 texcoord : TEXCOORD0;
};

By default, our code includes the vector uv, with its semantics TEXCOORD[n] for the vertex input. On the other hand, if we use the macros, we must replace the word uv with texcoord otherwise the internal operation will not be able to read the UV coordinates to generate the shadow map.

The same thing happens in the case of vertex output. The vertex vector must be renamed pos so that the internal process can write the UV coordinates for the shadow map. Furthermore, we have to include the macro SHADOW_COORDS(n), which includes the UV coordinates (o.shadowCoord) that we pass to the fragment shader stage.

struct v2f 
{ 
    float2 uv : TEXCOORD0; 
    // store the shadow data in TEXCOORD1 
    SHADOW_COORDS(1) // without (;) 
    // float4 vertex : SV_POSITION; 
    float4 pos : SV_POSITION; 
    … 
};

After having declared the inputs and outputs in the “Shadow Map Texture” pass, we can synchronize the values in the vertex shader stage.

v2f vert (appdata v) 
{ 
    v2f o; 
    o.pos = UnityObjectToClipPos(v.vertex); 
    o.uv = v.texcoord; 
    // transfer the shader UV coordinates to the fragment shader 
    TRANSFER_SHADOW(o) // without (;) 

    return o; 
}

The TRANSFER_SHADOW(output) macro is the same as the NDCToUV operation we performed in the previous section. Basically, it calculates the UV coordinates for the shadow texture. Now we can transfer the coordinates to the fragment shader stage.

fixed4 frag (v2f i) : SV_Target 
{ 
    fixed4 col = tex2D(_MainTex, i.uv); 
    // use the shadows 
    fixed shadow = SHADOW_ATTENUATION(i); 
    col.rgb *= shadow; 

    return col; 
}

Finally, SHADOW_ATTENUATION(output) contains the texture and its projection. This function can be saved as a one-dimensional vector, since only one channel (Alpha) will be used in the texture projection.

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容