1. We're looking for feedback on Unity Starter Kits! Let us know what you’d like.
    Dismiss Notice
  2. Unity 2017.2 beta is now available for download.
    Dismiss Notice
  3. Unity 2017.1 is now released.
    Dismiss Notice
  4. Introducing the Unity Essentials Packs! Find out more.
    Dismiss Notice
  5. Check out all the fixes for 5.6 on the patch releases page.
    Dismiss Notice
  6. Help us improve the editor usability and artist workflows. Join our discussion to provide your feedback.
    Dismiss Notice

Gradient Shader breaks on second instance

Discussion in 'Shaders' started by georgi-ivanov, Mar 19, 2017.

  1. georgi-ivanov

    georgi-ivanov

    Joined:
    Oct 13, 2013
    Posts:
    2
    Hi guys,

    I'm writing a gradient shader that lerps between 2 colors over time. It's all fine until I introduce a second instance of an object having a material with this shader. It works as expected in the scene view, but in-game something really weird happens - only one color appears. Also, when moving one of the objects down on the Y axis the second color starts to show.

    What am I missing here? Also, I'm pretty new to writing shaders, is there a better approach?

    Here's the shader itself:
    Code (CSharp):
    1. Shader "Shaders/Gradient Shader"
    2. {
    3.   Properties
    4.   {
    5.     _Color1 ("Color 1", Color) = (1, 1, 1, 1)
    6.     _Color2 ("Color 2", Color) = (0, 0, 0, 0)
    7.   }
    8.   SubShader
    9.   {
    10.     Pass
    11.     {
    12.       CGPROGRAM
    13.       #pragma vertex vert
    14.       #pragma fragment frag
    15.      
    16.       #include "UnityCG.cginc"
    17.  
    18.       struct appdata
    19.       {
    20.         float4 vertex : POSITION;
    21.       };
    22.  
    23.       struct v2f
    24.       {
    25.         float4 vertex : SV_POSITION;
    26.         float wave : PSIZE0;
    27.       };
    28.  
    29.       float4 _Color1;
    30.       float4 _Color2;
    31.      
    32.       v2f vert (appdata v)
    33.       {
    34.         v2f o;
    35.         o.vertex = UnityObjectToClipPos(v.vertex);
    36.         o.wave = (v.vertex.y * (sin(_Time[3]) + 2)) * 2;
    37.  
    38.         return o;
    39.       }
    40.      
    41.       fixed4 frag (v2f i) : SV_Target
    42.       {
    43.         float l = clamp(i.wave, 0 , 1);
    44.         fixed4 col = lerp(_Color1, _Color2, l);
    45.         return col;
    46.       }
    47.       ENDCG
    48.     }
    49.   }
    50. }
    51.  

    Here's how things look when I duplicate the cube object in the scene:
    Nooooo.png Screen Shot 2017-03-19 at 15.54.58.png
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    2,951
    This is caused by dynamic batching. When a mesh is batched, either dynamically or statically, Unity combines multiple meshes together into a single larger mesh with the vertex locations positioned in works space. So when you just have one object v.vertex.y = 0 is centered on the object, but as soon as you add the second Unity is batching them and v.vertex.y = 0 is now at the center of the world.

    The options are disable batching on the shader, or don't use the vertex position and instead use a mesh with a custom additional UV channel.
    https://docs.unity3d.com/Manual/SL-SubShaderTags.html

    You can also try using an instanced shader.
    https://docs.unity3d.com/Manual/GPUInstancing.html
     
    georgi-ivanov likes this.
  3. georgi-ivanov

    georgi-ivanov

    Joined:
    Oct 13, 2013
    Posts:
    2
    Setting the DisableBatching tag to True worked great, thanks!