Search Unity

  1. We would like to hear your feedback about Unity and our products. Click here for more information.
    Dismiss Notice

Problems to understand basic shader

Discussion in 'Shaders' started by dkollmann, Apr 29, 2010.

  1. dkollmann


    Jul 25, 2009
    I am currently trying to understand how a certain shader works with little success. I did quite a lot of Cg stuff in the past but somehow I do not understand how this shader works.

    Self-Illumin/VertexLit Shader:

    Code (csharp):
    1. SubShader {
    2.     LOD 100
    3.     Tags { "RenderType"="Opaque" }
    4.     Blend AppSrcAdd AppDstAdd
    5.     Fog { Color [_AddFog] }
    7.     // Ambient pass
    8.     Pass {
    9.         Name "BASE"
    10.         Tags {"LightMode" = "PixelOrNone"}
    11.         Color [_PPLAmbient]
    12.         SetTexture [_BumpMap] {
    13.             constantColor (.5,.5,.5)
    14.             combine constant lerp (texture) previous
    15.         }
    16.         SetTexture [_MainTex] {
    17.             constantColor [_Color]
    18.             Combine texture * previous DOUBLE, texture*constant
    19.         }
    20.     }
    21. }
    The documentation says that when I write this "combine A, B", then A is RGB and B is the alpha channel. In this shader the colour comes in with this line:

    Combine texture * previous DOUBLE, texture*constant

    I wonder how does this tint the texture? According to the documentation this line should only use the alpha of the given colour.

    And why does it multiply with previous?

    Shouldn't the whole blending work like these lines of code?

    mycolor= texture * color;
    finalcolor= float4(previous.rgb + mycolor.rgb * mycolor.a, 1.0);

    Another thing which makes me wonder is how can it be that when I duplicate the main texture SetTexture and modify it to use another colour and texture that I get a result which is black when the main texture is not black, aren't those passes added by the blending so the result can only be brighter?

    It would be great if someone could tell me where my thinking is wrong.

  2. shawn


    Unity Technologies

    Aug 4, 2007
    The tint comes from Color [_PPLAmbient]. It's not really documented anywhere, but _PPLAmbient includes _Color pre-multiplied into it.

    It multiplies with the previous, because that is where it comes up with the self-illumination. The first SetTexture lerps no illumination to full illumination based on the texture's alpha.

    SetTextures don't add to eachother unless you tell them to. Unless you have a previous keyword in the SetTexture somewhere, that SetTexture will just override what color is there.
  3. dkollmann


    Jul 25, 2009
    Okay thanks, that explains a few things.

    I think my mistake was that I assumed that Blend AppSrcAdd AppDstAdd would do the following:

    final= float4(previous.rgb + current.rgb * current.a, 1.0f);

    I thought the alpha would be multiplied into it and the alpha would always be 1.

    But when SetTexture always replaces the previous result why do I set this blending then?

    Thanks for the help.
  4. Daniel_Brauer


    Unity Technologies

    Aug 11, 2006
    Fixed function stuff is weird. Let me see if I can break it down for you. First, blending functions are applied per-Pass, not per-SetTexture. Only the final result of a series of SetTextures will be used for blending.
    Code (csharp):
    1. Blend AppSrcAdd AppDstAdd
    means that the first pass will not be blended, and subsequent passes will be additively blended. Since you only define one pass, it will not be blended, and will simply overwrite whatever is already in the frame buffer.

    Now I'm going to convert the shader into straightforward mathematical operations, step by step. This is all that matters before blending:
    Code (csharp):
    1.     Color [_PPLAmbient]
    2.     SetTexture [_BumpMap] {
    3.         constantColor (.5,.5,.5)
    4.         combine constant lerp (texture) previous
    5.     }
    6.     SetTexture [_MainTex] {
    7.         constantColor [_Color]
    8.         Combine texture * previous DOUBLE, texture*constant
    9.     }
    As Shawn pointed out, _PPLAmbient is just this:
    Code (csharp):
    1. _RenderSettingsAmbient*_Color
    And since there is no other lighting being applied, both "previous" and "primary" in the first SetTexture refer to whatever is defined in Color:
    Code (csharp):
    1.     SetTexture [_BumpMap] {
    2.         constantColor (.5,.5,.5)
    3.         combine constant lerp (texture) (_RenderSettingsAmbient*_Color)
    4.     }
    5.     SetTexture [_MainTex] {
    6.         constantColor [_Color]
    7.         Combine texture * previous DOUBLE, texture*constant
    8.     }
    "constant" is just whatever was defined in constantColor, and the lerp combiner uses only alpha as the lerp parameter:
    Code (csharp):
    1.     _FirstResult = Lerp((.5,.5,.5), (_RenderSettingsAmbient*_Color), _BumpMap.a)
    2.     SetTexture [_MainTex] {
    3.         constantColor [_Color]
    4.         Combine texture * previous DOUBLE, texture*constant
    5.     }
    In the second combiner, "previous" is the result of the first combiner, "DOUBLE" is just times two, and the comma means different combiners are used for RGB and A:
    Code (csharp):
    1.     _FirstResult = Lerp((.5,.5,.5), (_RenderSettingsAmbient*_Color), _BumpMap.a)
    2.     _SecondResult.rgb =  _MainTex.rgb * _FirstResult.rgb * 2
    3.     _SecondResult.a = _MainTex.a * _Color.a
    Since there is no blending, _SecondResult will be the colour of that pixel on the screen.
  5. dkollmann


    Jul 25, 2009
    Thanks for the help I got it working now.