Search Unity

LIGHT_ATTENUATION and tex2D

Discussion in 'Shaders' started by schmid, May 23, 2008.

  1. schmid

    schmid

    Joined:
    May 22, 2008
    Posts:
    4
    Hi! I've made some shader tests, and I've encountered something I don't understand. I've reduced the problem to this really simple shader below:
    Code (csharp):
    1. Shader "TestShader" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1)
    4.         _MainTex ("Base (RGB)", 2D) = "white" {}
    5.     }
    6.  
    7.     SubShader {
    8.         Pass {
    9.             Tags {"LightMode" = "Pixel"}
    10.  
    11. CGPROGRAM
    12. #pragma vertex vert
    13. #pragma fragment frag
    14. #pragma multi_compile_builtin
    15.  
    16. #include "UnityCG.cginc"
    17. #include "AutoLight.cginc"
    18.  
    19. uniform sampler2D _MainTex;
    20. uniform float4 _MainTex_ST;
    21.  
    22. struct v2f {
    23.     V2F_POS_FOG;
    24.     LIGHTING_COORDS
    25.     float2 uv;
    26. };
    27.  
    28. v2f vert (appdata_base v)
    29. {
    30.     v2f o;
    31.     PositionFog( v.vertex, o.pos, o.fog );
    32.     o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    33.     TRANSFER_VERTEX_TO_FRAGMENT(o);
    34.     return o;
    35. }
    36.  
    37. #define MAGIC_SPELL_THAT_MAKES_TEXTURES_WORK(i) LIGHT_ATTENUATION(i)
    38.  
    39. half4 frag (v2f i) : COLOR
    40. {
    41.     //MAGIC_SPELL_THAT_MAKES_TEXTURES_WORK(i);
    42.  
    43.     return tex2D(_MainTex, i.uv);
    44. }
    45. ENDCG
    46.         }
    47.     }
    48. }
    This does not work. The texture seems to be 'zoomed' and changes with changing point light positions. However, if I uncomment the MAGIC_SPELL_... line, it works perfectly. To me, the LIGHT_ATTENUATION macro just returns a value. Why does it make the tex2D call work right as a side effect?
     

    Attached Files:

  2. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Basically, all outputs of a vertex-to-fragment structure should be written by the vertex program, and used by the fragment program. Otherwise Cg compile won't be able to "match up" them.

    What happens in your case is this:

    Vertex program uses several vertex-to-fragment interpolators to output the lighting coordinates (LIGHTING_COORDS), and also outputs UV coordinate. Let's for simplicity assume that LIGHTING_COORDS is one float4 interpolator. So in this case, lighting data is probably put into TEXCOORD0, and UV is put into TEXCOORD1.

    Now, fragment program uses vertex-to-fragment interpolators. If you don't use lighting data in there, Cg just optimizes that part of interpolators away, and in the end it sees "hey, we only need to read in UV. Let's assign TEXCOORD0 to read that in". Now there's a problem - vertex program outputs some lighting data to TEXCOORD0, while fragment program thinks it's the UV.

    When you actually use the lighting data (which LIGHT_ATTENUATION does), then everything works fine because now both programs agree on the set of used interpolators.

    Now, it is possible to explicitly assign interpolators in the vertex-to-fragment structure, but then generally that should be done for all members of the structure. Which is very hard to do with light combinations, because different light combinations use different amount of interpolators (for example, unshadowed, no-cookie directional light uses zero interpolators, while a shadowed spot light uses three or four I think).

    So in summary: always use all members of vertex-to-fragment stucture.
     
  3. schmid

    schmid

    Joined:
    May 22, 2008
    Posts:
    4
    Thanks. I don't think I would ever have figured that out by myself :).