0
\$\begingroup\$

I am very new to shaders but can't seem to find an answer to my problem. To put it simple I have created a flag shader, which can apply texture, animates and should also react to light. The problem is now that the shader doesn't work with both texture and light reaction at the same time. It can animate and react to light.

I created two passes, one for the color, texture and animation, and another pass with it reacting to a directional light.

I have tried out different blending options, but nothing seems to be correct.

This is a picture of it currently:

Animated flag(White) not combined with shader that reacts to light (black)

This is my code so far, I apologize for its messiness in advance :

 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/FlagShader1"{ // Shader file path in inspector   
    Properties{              // Properties we will be working with

        _Color("Main Color", Color) = (1,1,1,1) // Property for changing color      
        _MainTex("Main Texture" , 2D) = "white" {} // The tecture we will manipulate
        _Frequency("Frequency", Float) = 1 //The frequency of the flag , which is the number of waves
        _Amplitude("Amplitude",Float) = 1  // the Amplitude of the flag, which is the depth of the waves
        _Speed("Speed", Float) = 1 //Speed property, used to control how fast the flag waves.

    }
    Subshader {
        Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "LightMode" = "ForwardBase"} // "Queue" Determines the rendering order.//"IgnoreProjector" set to true, means that this object will not be affected by projectors. //"RenderType" Categorizes shaders into  several predefined groups

        Pass{ // The number of passes. We only have one pass.
            Name "Animation"

            CGPROGRAM// We go from shaderLab into CG language

            // use "vert" function as the vertex shader
            #pragma vertex vert
            // use "frag" function as the pixel (fragment) shader        
            #pragma fragment frag

            //Global variables
            uniform half4 _Color;
            uniform sampler2D _MainTex;
            uniform float4 _MainTex_ST;
            uniform float _Frequency;
            uniform float _Amplitude;
            uniform float _Speed;

            // vertex shader inputs
            struct vertexInput {
                float4 vertex : POSITION;   
                float4 texcoord : TEXCOORD0; 
            };

            // vertex shader outputs- these go to the fragment shader    
            struct vertexOutput {
                float4 pos : SV_POSITION; 
                float4 texcoord : TEXCOORD0;  
            };

            // vertex shader- this is where the flag is animated
            float4 vertexFlagAnim(float4 vertPos, float2 uv) {
                vertPos.y = vertPos.y + sin((uv.x - (_Time.y * _Speed))*_Frequency) * (_Amplitude* uv.x);
                //the axis we will animate(x) and it is moved with time.y       
                return vertPos;
            }

            // vertex shader. Runs on each vertex of the 3D model.   
            vertexOutput vert(vertexInput v) {
                vertexOutput o;
                v.vertex = vertexFlagAnim(v.vertex, v.texcoord); // The animation
                o.pos = UnityObjectToClipPos(v.vertex); //Transforms a point from object space to the camera’s clip space in homogeneous coordinates.       
                o.texcoord.xy = (v.texcoord.xy *  _MainTex_ST.xy + _MainTex_ST.zw);// Changing the tiling and offset.
                return o;
            }

            half4 frag(vertexOutput i) : COLOR{    
                float4 col = tex2D(_MainTex, i.texcoord) * _Color; // sample texture and return it
                return col;
            }

            ENDCG
       }
       Pass{
            Name "Light"
            Blend Off
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc" //Global variables

            // vertex shader inputs
            struct vertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            // vertex shader outputs- these go to the fragment shader
            struct vertexOutput {    
                float4 vertex : SV_POSITION; //pos
                float3 normal : NORMAL;
            };

            // vertex shader. Runs on each vertex of the 3D model.
            vertexOutput vert(vertexInput v) {
                vertexOutput o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.normal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag(vertexOutput i) : SV_Target     {
                return saturate(dot(normalize(i.normal), _WorldSpaceLightPos0));
            }

            ENDCG
       }
    }
}
\$\endgroup\$
1
\$\begingroup\$

You should think of a single pass of a shader as a complete render of the object:

  • first, the vertex shader decides where the vertices should go within the camera's projection
  • then, the rasterizer/interpolator selects which pixels are covered by the mesh and what blended values of the vertex shader output each one should use
  • lastly, the pixel shader decides what colour to paint each of those pixels onto the screen / frame buffer / render target

Then you're done for that rendering pass. The object has been painted into the frame.

The next pass then runs, from scratch, with no memory of what the previous one did (apart from if you sample the old render target as an input texture to read the image-so-far). All you can do on a subsequent pass is paint over this already-drawn object (or under it, using the z buffer).

What this means for this case is that your second pass can't inject an alternate lighting & shading rule into the pixels drawn by the first, or carry the wavy vertex positions from the first pass into the second one. To combine these two effects, you have to do them both in one pass.

For this case in particular, the easiest way to get Unity to render something with lighting is to use a surface shader. We can specify a vertex function to use to modify the vertex before we draw it:

Shader "Custom/FlagShader" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        // Add flag animation parameters
        _Frequency("Frequency", Float) = 1
        _Amplitude("Amplitude",Float) = 1
        _Speed("Speed", Float) = 1
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Add vertex:vertexMod to use the "vertexMod" function to transform vertices.
        #pragma surface surf Standard fullforwardshadows vertex:vertexMod
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
            float3 worldNormal;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        // Add variables for flag animation.
        uniform float _Frequency;
        uniform float _Amplitude;
        uniform float _Speed;

        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)

        // Modify our vertex position & normal vector for shading.
        void vertexMod(inout appdata_full v) {
            float a = _Amplitude * v.texcoord.x;
            float phase = v.texcoord.x *_Frequency - fmod(_Time.y * _Speed, 2.0f * 3.141592653589f);
            float s = sin(phase);
            float c = cos(phase);

            v.vertex.y += s * a;
            normalize(float3(c * _Frequency * a + s * _Amplitude, _Frequency, 0.0f));           
        }    

        // Let Unity do its normal material & light calculations from here on in.
        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

You'll need a little extra to get it double-sided and shadowcasting, but that's the basic idea.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.