在 Shaderlab 中初始化全局变量的问题

Issue with initializing global variable in Shaderlab

我刚刚意识到Unity中的全局变量必须在函数中初始化。作为着色器的新手,我不知道我是否以错误的方式这样做。

比如我定义一个const变量时:

const float PI = 3.14159265359;

然后尝试编写一个代码,在 frag 函数中使用:

fixed4 frag(v2f i) : SV_Target
{
    float result = PI * otherVariable;
    ///Use the result value...
}

没用。结果出现黑屏,或者预期的结果不会出现。

令人困惑的是,当我在 frag 函数中再次 重新初始化 全局变量 (PI) 时,我得到了预期的结果。

例如,这是有效的:

float PI = 3.14159265359;

然后试着写一段代码在frag函数中使用:

fixed4 frag(v2f i) : SV_Target
{
    //re-initialize
    PI = 3.14159265359;

    float result = PI * otherVariable;
}

我有太多常量全局变量,以至于我无法重新启动它们。

此问题的一个完整示例是 this 我移植的 Shadertoy 代码。

Shader "Unlit/Atmospheric Scattering 2"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
    }
        SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }


            ////////////////////////////////////////////////////////////////////////////////////////////////////////
            //https://www.shadertoy.com/view/lslXDr
            // math const
              float PI = 3.14159265359;
              float DEG_TO_RAD = (3.14159265359 / 180.0);
              float MAX = 10000.0;

              // scatter const
               float K_R = 0.166;
               float K_M = 0.0025;
               float E = 14.3;

               float C_R = float3(0.3, 0.7, 1.0);   // 1 / wavelength ^ 4
               float G_M = -0.85;                   // Mie g

               float R = 1.0;
               float R_INNER = 0.7;
               float SCALE_H = 4.0 / (1.0 - 0.7);
               float SCALE_L = 1.0 / (1.0 - 0.7);

               int NUM_OUT_SCATTER = 10;
               float FNUM_OUT_SCATTER = 10.0;

               int NUM_IN_SCATTER = 10;
               float FNUM_IN_SCATTER = 10.0;

               // angle : pitch, yaw
               float3x3 rot3xy(float2 angle) {
                   float2 c = cos(angle);
                   float2 s = sin(angle);

                   return float3x3(
                       c.y, 0.0, -s.y,
                       s.y * s.x, c.x, c.y * s.x,
                       s.y * c.x, -s.x, c.y * c.x
                   );
               }

               // ray direction
               float3 ray_dir(float fov, float2 size, float2 pos) {
                   float2 xy = pos - size * 0.5;

                   float cot_half_fov = tan((90.0 - fov * 0.5) * DEG_TO_RAD);
                   float z = size.y * 0.5 * cot_half_fov;

                   return normalize(float3(xy, -z));
               }

               // ray intersects sphere
               // e = -b +/- sqrt( b^2 - c )
               float2 ray_vs_sphere(float3 p, float3 dir, float r) {
                   float b = dot(p, dir);
                   float c = dot(p, p) - r * r;

                   float d = b * b - c;
                   if (d < 0.0) {
                       return float2(MAX, -MAX);
                   }
                   d = sqrt(d);

                   return float2(-b - d, -b + d);
               }

               // Mie
               // g : ( -0.75, -0.999 )
               //      3 * ( 1 - g^2 )               1 + c^2
               // F = ----------------- * -------------------------------
               //      2 * ( 2 + g^2 )     ( 1 + g^2 - 2 * g * c )^(3/2)
               float phase_mie(float g, float c, float cc) {
                   float gg = g * g;

                   float a = (1.0 - gg) * (1.0 + cc);

                   float b = 1.0 + gg - 2.0 * g * c;
                   b *= sqrt(b);
                   b *= 2.0 + gg;

                   //b = mul(b,sqrt(b));
                   //b = mul(b,2.0 + gg);

                   return 1.5 * a / b;
               }

               // Reyleigh
               // g : 0
               // F = 3/4 * ( 1 + c^2 )
               float phase_reyleigh(float cc) {
                   return 0.75 * (1.0 + cc);
               }

               float density(float3 p) {
                   return exp(-(length(p) - R_INNER) * SCALE_H);
               }

               float optic(float3 p, float3 q) {
                   float3 step = (q - p) / FNUM_OUT_SCATTER;
                   float3 v = p + step * 0.5;

                   float sum = 0.0;
                   for (int i = 0; i < NUM_OUT_SCATTER; i++) {
                       sum += density(v);
                       v += step;
                   }
                   sum *= length(step) * SCALE_L;
                   //sum = mul(sum,length(step) * SCALE_L);

                   return sum;
               }

               float3 in_scatter(float3 o, float3 dir, float2 e, float3 l) {
                   float len = (e.y - e.x) / FNUM_IN_SCATTER;
                   float3 step = dir * len;
                   float3 p = o + dir * e.x;
                   float3 v = p + dir * (len * 0.5);

                   float3 sum = float3(0.,0.,0.);
                   for (int i = 0; i < NUM_IN_SCATTER; i++) {
                       float2 f = ray_vs_sphere(v, l, R);
                       float3 u = v + l * f.y;

                       float n = (optic(p, v) + optic(v, u)) * (PI * 4.0);

                       sum += density(v) * exp(-n * (K_R * C_R + K_M));

                       v += step;
                   }
                   sum *= len * SCALE_L;
                   //sum = mul(sum,len * SCALE_L);

                   float c = dot(dir, -l);
                   float cc = c * c;

                   return sum * (K_R * C_R * phase_reyleigh(cc) + K_M * phase_mie(G_M, c, cc)) * E;
               }
               ////////////////////////////////////////////////////////////////////////////////////////////////////////


               fixed4 frag(v2f i) : SV_Target
               {
                   //Re-initialize BEGIN
                   // math const
                   PI = 3.14159265359;
                   DEG_TO_RAD = (3.14159265359 / 180.0);
                   MAX = 10000.0;

                   // scatter const
                   K_R = 0.166;
                   K_M = 0.0025;
                   E = 14.3;

                   C_R = float3(0.3, 0.7, 1.0);     // 1 / wavelength ^ 4
                   G_M = -0.85;                 // Mie g

                   R = 1.0;
                   R_INNER = 0.7;
                   SCALE_H = 4.0 / (1.0 - 0.7);
                   SCALE_L = 1.0 / (1.0 - 0.7);

                   NUM_OUT_SCATTER = 10;
                   FNUM_OUT_SCATTER = 10.0;
                   NUM_IN_SCATTER = 10;
                   FNUM_IN_SCATTER = 10.0;
                   //Re-initialize END

                       float4 fragColor = 0;
                       float2 fragCoord = i.vertex.xy;

                       // default ray dir
                       float3 dir = ray_dir(45.0, _ScreenParams.xy, fragCoord.xy);

                       // default ray origin
                       float3 eye = float3(0.0, 0.0, 2.4);

                       // rotate camera
                       float3x3 rot = rot3xy(float2(0.0, _Time.y * 0.5));
                       /* dir = rot * dir;
                        eye = rot * eye;*/

                           dir = mul(rot,dir);
                           eye = mul(rot,eye);

                           // sun light dir
                           float3 l = float3(0, 0, 1);

                           float2 e = ray_vs_sphere(eye, dir, R);
                           if (e.x > e.y) {
                               discard;
                           }

                           float2 f = ray_vs_sphere(eye, dir, R_INNER);
                           e.y = min(e.y, f.x);

                           float3 I = in_scatter(eye, dir, e, l);

                           fragColor = float4(I, 1.0);

                           return fragColor;
                       }
                       ENDCG
                   }
    }
}

此着色器附加到 Material,然后使用以下代码附加到相机:

[ExecuteInEditMode]
public class CameraEffect : MonoBehaviour
{
    public Material mat;

    // Called by camera to apply image effect
    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (mat != null)
        {
            Graphics.Blit(source, destination, mat);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}

它工作正常,但请注意我必须如何重新初始化 PIDEG_TO_RADMAX 和其他全局变量...如果不这样做,它不会 work.The 屏幕只是黑色,这不是第一个导致同样问题的着色器。

为什么会这样?

我是否以错误的方式声明了变量?

嗨!

任何常量都可以这样写:

//float my_constant = 104.3;
#define my_constant   104.3f

您可以为着色器创建自定义库,并根据需要使用变量和函数。例如,创建名称为 MyConstants.cginc 的文件并放入您的项目中。代码:

#ifndef MY_CONSTANTS_INCLUDED
#define MY_CONSTANTS_INCLUDED

#define DEG_TO_RAD    0.01745329251994f
#define MAX           10000.0f
#define PI            3.14159265359f

//scatter const
// .....
// .....

float3 ray_dir(float fov, float2 size, float2 pos) {
    float2 xy = pos - size * 0.5;
    float cot_half_fov = tan((90.0 - fov * 0.5) * DEG_TO_RAD);
    float z = size.y * 0.5 * cot_half_fov;
    return normalize(float3(xy, -z));
}
//.... and other methods
#endif

并在着色器中使用库

//.....
#include "UnityCG.cginc"
#include "MyConstants.cginc"
//.....