Search Unity

Multiple Overlay Textures Shader (Card components on a card)

Discussion in 'Shaders' started by xtimus, Jul 6, 2017.

  1. xtimus

    xtimus

    Joined:
    Jun 16, 2014
    Posts:
    11
    Hi I am working on a card game and I am new to shaders.
    I have a card which has Mana Cost, Name, Type, Description, etc. Some are text but parts of the art will change based on the type of card. Right now I just have each card component in different sprites with their position slightly higher than the order. This works for now, but to get effects like dissolving entire card I would need all the components onto 1 Mesh.

    What I am wondering is if there is a way to just use shader code combined with a card script so that it writes each component's textures onto the coordinates layered on top of each other instead of just using different sprites positioned on top of each other.

    Is it possible to do all this in 1 pass? Can I do multiple vertex and fragment pragma in 1 pass or do I need a separate pass for each different texture I am overlaying onto the mesh?

    Code (CSharp):
    1. Properties {
    2.         _Color {"Color", Color} = {1.0, 1.0, 1.0, 1.0}
    3.     }
    4.     SubShader {
    5.         Tags {
    6.             "IgnoreProjector" = "True"
    7.             "Queue" = "Transparent"
    8.             "RenderType" = "Transparent"
    9.         }
    10.         // Overlay components of card onto card
    11.         Pass {
    12.             Name "ComponentsOverlay"
    13.            
    14.             CGPORGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.  
    18.             // user defined variables
    19.             uniform float4 _Color;
    20.             uniform sampler2D _OverlayTexture;
    21.             uniform float4 _OverlayTextureColor;
    22.    
    23.             // base input structs
    24.             struct vertexInput {
    25.                 float4 vertex : POSITION;
    26.                 float2 texcoord : TEXCOORD1;
    27.             };
    28.             struct vertexOutput {
    29.                 float4 vertex : SV_POSITION;
    30.                 float2 texcoord : TEXCOORD1;
    31.             };
    32.  
    33.             // vertex function
    34.             vertexOutput vert(vertexInput v) {
    35.                 vertexOutput o;
    36.                 o.vertex = UnityObjectToClipPos(v.vertex + float4(0, 0, 0.001, 0));
    37.                 o.texcoord = v.texcoord;
    38.  
    39.                 return o;
    40.             }
    41.  
    42.             // fragment function
    43.             float4 frag(vertexOutput o) : COLOR
    44.             {
    45.                 // overlay texture
    46.                 float4 overlaytex = tex2D(_OverlayTexture, TRANSFORM_TEX(o.texcoord, _OverlayTexture));
    47.                 overlaytex *= _OverlayTextureColor;
    48.                 if (overlaytex.a < 0.1) discard;
    49.  
    50.                 return overlaytex;
    51.             }
    52.             ENDCG
    53.         }
    54.     }
    55.     FallBack "Diffuse"
     
    Last edited: Jul 6, 2017
  2. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,014
    Hi xtimus,

    why not render each card you use to a texture with all the text and overlay images? Then you can use this texture on a card and do the effects.
     
  3. xtimus

    xtimus

    Joined:
    Jun 16, 2014
    Posts:
    11
    That would be a way to do it, but it would be non-procedural and it would result in excess space. If you are referring to rendertexture, that is also a way I can go about this too. I am just wondering if there's other ways.

    After playing around with the shader code I found I can put multiple textures on top of each other in 1 pass. However, another problem I came across it, it stretches all textures to the size of the UV map. Is there a way to keep the size of textures relative to a reference texture (one texture which is the size of entire card, then all other texture sizes are relative to it)?

    Right now I still think just superimposing each separate component of the card on different sprite layers is the best way to go about this.

    Code (CSharp):
    1. Pass {
    2.             //Name "ComponentsOverlay"
    3.             Blend SrcAlpha OneMinusSrcAlpha
    4.  
    5.             CGPROGRAM
    6.             #pragma vertex vert
    7.             #pragma fragment frag
    8.  
    9.             #include "UnityCG.cginc"
    10.  
    11.             // user defined variables
    12.             uniform sampler2D _BaseTexture;
    13.             uniform float4 _BaseTexture_ST;
    14.             uniform sampler2D _FrameTexture;
    15.             uniform float4 _FrameTexture_ST;
    16.  
    17.             // base input structs
    18.             struct vertexInput {
    19.                 float4 vertex : POSITION;
    20.                 float2 texcoord : TEXCOORD0;
    21.             };
    22.             struct vertexOutput {
    23.                 float4 vertex : SV_POSITION;
    24.                 float2 texcoord : TEXCOORD0;
    25.             };
    26.  
    27.             // vertex function
    28.             vertexOutput vert(vertexInput v) {
    29.                 vertexOutput o;
    30.                 o.vertex = UnityObjectToClipPos(v.vertex + float4(0, 0, 0.001, 0));
    31.                 o.texcoord = v.texcoord;
    32.              
    33.                 return o;
    34.             }
    35.  
    36.             // fragment function
    37.             float4 frag(vertexOutput IN) : COLOR
    38.             {
    39.                 // overlay texture
    40.                
    41.                 float4 baseTex = tex2D(_BaseTexture, TRANSFORM_TEX(IN.texcoord, _BaseTexture));
    42.                 float4 frameTex = tex2D(_FrameTexture, TRANSFORM_TEX(IN.texcoord, _FrameTexture));
    43.  
    44.                 float4 finalTex = lerp(baseTex, frameTex, frameTex.a*1);
    45.  
    46.                 if (finalTex.a < 0.1) discard;
    47.                 return finalTex;
    48.             }
    49.             ENDCG
    50.         }
     
    Last edited: Jul 8, 2017
  4. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    When it comes to fill rate, which is mainly important on mobile platforms, composing the layers in the shader is the best way. The uv you use to sample the texture, is exactly that. But you can of course change that for each texture sample. It's best to do this in the vertex shader to avoid dependent texture lookups.
    Code (csharp):
    1.  
    2. struct vertexInput {
    3.    float4 vertex : POSITION;
    4.    float2 texcoord : TEXCOORD0;
    5. };
    6. struct vertexOutput {
    7.    float4 vertex : SV_POSITION;
    8.    float2 texcoord1 : TEXCOORD0;
    9.    float2 texcoord2 : TEXCOORD1;
    10.    float2 texcoord3 : TEXCOORD2;
    11. };
    12. vertexOutput vert(vertexInput v) {
    13.    vertexOutput o;
    14.    o.vertex = UnityObjectToClipPos(v.vertex + float4(0, 0, 0.001, 0));
    15.    o.texcoord1 = v.texcoord;
    16.    o.texcoord2 = float2(2.0, 2.0) * v.texcoord;
    17.    o.texcoord3 = float2(4.0, 4.0) * v.texcoord;
    18.    return o;
    19. }
    20. float4 frag(vertexOutput IN) : COLOR {
    21.    float4 layer1 = tex2D(_Texture1, IN.texcoord1);
    22.    float4 layer2 = tex2D(_Texture2, IN.texcoord2);
    23.    float4 layer3 = tex2D(_Texture3, IN.texcoord3);
    24.    float4 composed = lerp(layer1, layer2, layer2.a);
    25.    composed = lerp(composed, layer3, layer3.a);
    26.    clip(composed - 0.1);
    27.    return composed;
    28. }
    29.  
    Do set those textures to clamp to prevent them from repeating all over the card.
     
  5. xtimus

    xtimus

    Joined:
    Jun 16, 2014
    Posts:
    11
    Here's another problem. My sprites are in an atlas. I played around with using shader to place textures ontop of each other and I see that I can adjust the scale and position of the textures but since my sprites are in an atlas I can only select the entire atlas as the texture instead of the sprite within the atlas. If I place the atlas ontop of a layer then it shows the other parts of the atlas and covers up what is beneath with the unwanted sprites of the atlas. In the image below I only want to show the left "pole" and not the other yellow stuff.

     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    This is kind of why they're not combined into a single shader to begin with, but it can be solved.

    The short version is you'll need another set of values for each texture to define the min-max UV area for each sprite and skip the lerp when outside the range.