Issue with initializing global variable in Shaderlab

2.7k Views Asked by At

I just realized that a global variable in Unity must be initialized in a function. Being new to shaders, I can't tell if I am doing this the wrong way.

For example, when I define a const variable:

const float PI = 3.14159265359;

then try to write a code that uses in the frag functiion:

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

It doesn't work. I gets a black screen as the result or the expected result wont show up.

The confusing part is that when I re-initialize the global variable (PI) again in the frag function, I get the expected result.

For example, this is what works:

float PI = 3.14159265359;

then try to write a code that uses in the frag function:

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

    float result = PI * otherVariable;
}

I have so many constant global variables that I can't keep re-initiated them.

One complete example of this problem is this Shadertoy code I ported.

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
                   }
    }
}

This shader is attached to a Material that is then attached to the camera with the code below:

[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);
        }
    }
}

It works fine but notice how I had to re-initialize the PI, DEG_TO_RAD, MAX and other global variables...Without doing that it won't work.The screen is simply black and this is not the first shader that has caused this same issue.

Why is this happening?

Am I declaring the variables the wrong way?

1

There are 1 best solutions below

3
On BEST ANSWER

Hi!

Any constants you can write like this:

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

You can create custom library for shaders and use variables and functions as you wish. For example create file with name MyConstants.cginc and put in in your project. Code:

#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

And Using library in your shader

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