UnityTips 之着色器编译器傻吗3 重复的计算需要提取吗

2023-05-17  本文已影响0人  暴走TA

简介: 前段时间用ASE做效果的时候,为了优化把一些常用的ndotv ndotl这种的提前计算出来,然后进行传值,本意是为了减少重复计算,提升效率,但是导致一个问题,不同function为了公用这些值,不得不暴露很多的input。突发奇想,想看看着色器的编译器对于这种重复书写的计算有优化没
unity版本:20222.1.7fc1

准备测试用的着色器

为了减少额外的干扰,先做一个最简单的,逻辑最少的shader,我们暂且将它叫做 CompileTester 吧,提供了两个方法 ndotl1 和 ndotl2 ,他们中都包含了 一个一样的点积运算

Shader "CompileTester"
{
    Properties
    {
        _Color0("Color 0", Color) = (0,0,0,0)
    }

    SubShader
    {
        HLSLINCLUDE
        #pragma target 3.0
        #pragma prefer_hlslcc gles
        ENDHLSL
                //float4 uv1:TEXCOORD0;
                //half4 uv2:TEXCOORD1;
                //half2 uv3:TEXCOORD2;
        Pass
        {   
            Name "Forward"
            Tags { "LightMode"="UniversalForward" }
        
            HLSLPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            float4 _Color0;

            half ndotl1(half3 normal,half3 lightdir)
            {
                half d=dot(normal,lightdir);
                return d;
            }
            half ndot2(half3 normal,half3 lightdir)
            {
                half3 newNor=normal;
                return normalize(dot(newNor,lightdir));
            }

            struct VertexInput
            {
                float4 vertex : POSITION;
                float4 uv1:TEXCOORD0;
                half4 uv2:TEXCOORD1;
                half4 uv3:TEXCOORD2;
    
            };
            struct VertexOutput
            {
                float4 clipPos : SV_POSITION;
                float4 uv1:TEXCOORD0;
                half4 uv2:TEXCOORD1;
                half2 uv3:TEXCOORD2;
            };

            VertexOutput vert ( VertexInput v )
            {
                VertexOutput o ;
                o.clipPos= TransformWorldToHClip( TransformObjectToWorld( v.vertex.xyz ) );
                o.uv1=v.uv1;
                half2 uv=v.uv2.xy;
                o.uv3=v.uv3.xy;
                return o;
            }
            half3 _wnormal;
            half3 _lightdir;
            half4 frag ( VertexOutput IN  ) : SV_Target
            {   
                half3 normal=half3(1,1,1);
                half3 lightdir=half3(2,1,2);
                return ndotl1(_wnormal,_lightdir)+ndot2(_wnormal,_lightdir);
                return _Color0;
            }
            ENDHLSL
        }
    }
}

ndotl1 和 ndotl2 的差异

             half ndotl1(half3 normal,half3 lightdir)
            {
                half d=dot(normal,lightdir); // 将点击结果赋予一个临时变量,然后将其返回
                return d;
            }
            half ndot2(half3 normal,half3 lightdir)
            {
                half3 newNor=normal;  //将法线变量传递给一个临时变脸,然后用临时变量参与运算
                return normalize(dot(newNor,lightdir)); // 直接对点积结果进行归一化,然后直接返回
            }

实际产生的编译结果

    void main()
    {
        u_xlat16_0 = dot(_wnormal.xyz, _lightdir.xyz); //将点积操作提取了出来,赋值给一个临时变量
        u_xlat16_1 = u_xlat16_0 * u_xlat16_0; //接着的三步进行归一化
        u_xlat16_1 = inversesqrt(u_xlat16_1);
        SV_Target0 = vec4(u_xlat16_0) * vec4(u_xlat16_1) + vec4(u_xlat16_0);//归一化并凑了一个 mad 操作,
        return;
    }

实验结果

编译器还是比较 niubility 的,我可以回去大胆的取消掉统一预计算了,当然如果有的地方需要保持计算结果一致,并且为了便于计算方式一次修改便可到处生效还是需要提前计算并统一保存的

上一篇下一篇

猜你喜欢

热点阅读