Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Creating a backface-culling rim-shader based on the Particles/Add shader

Discussion in 'Shaders' started by AndrewGrayGames, Sep 27, 2014.

  1. AndrewGrayGames

    AndrewGrayGames

    Joined:
    Nov 19, 2009
    Posts:
    3,821
    For my current little side-project, I've hit a case where I need to create a modified version of an existing shader.

    First, here's what I've got. It's fugly.
    ugly magic cube.png

    I need to implement two effects on this:
    1. First, I need to cull all those back-faces, such that only the patterns on the visible faces appear.
    2. Next, I need to add a rim shader to this, to give it a luminescent, magic, sort of unreal effect.
    The current shader is the standard Particle/Additive shader that ships with Unity. I want to keep the look, just enhance it and get rid of details I don't need.

    I'm using Unity Free, and in the world of shader programming, I am a super-noob. How would I go about implementing such a shader?
     
  2. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,589
    You can control which faces are visible using the Cull keyword in shaderlab.

    I recommend to take a look at the Rim Lighting surface shader example Unity provides. Once you understand how rim works, you should be able to adapt it for your CG particle shader needs.
     
    AndrewGrayGames likes this.
  3. AndrewGrayGames

    AndrewGrayGames

    Joined:
    Nov 19, 2009
    Posts:
    3,821
    So, I could use some additional guidance. This is what I've got so far:

    Code (csharp):
    1. Shader "Particles/Rim Additive" {
    2.     Properties {
    3.        _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
    4.        _MainTex ("Particle Texture", 2D) = "white" {}
    5.        _BumpMap ("Bump Map", 2D) = "bump" {}
    6.        _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
    7.    
    8.        _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
    9.        _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0
    10.     }
    11.  
    12.     Category {
    13.        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    14.        Blend SrcAlpha One
    15.        AlphaTest Greater .01
    16.        ColorMask RGB
    17.        Cull Back Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
    18.        BindChannels {
    19.            Bind "Color", color
    20.            Bind "Vertex", vertex
    21.            Bind "TexCoord", texcoord
    22.        }
    23.  
    24.        // ---- Fragment program cards
    25.        SubShader {
    26.            Pass {
    27.                CGPROGRAM
    28.                #pragma vertex vert
    29.                #pragma fragment frag
    30.                #pragma fragmentoption ARB_precision_hint_fastest
    31.                #pragma surface surf Lambert
    32.                #pragma multi_compile_particles
    33.  
    34.                #include "UnityCG.cginc"
    35.  
    36.                struct appdata_t {
    37.                    float4 vertex : POSITION;
    38.                    fixed4 color : COLOR;
    39.                    float2 texcoord : TEXCOORD0;
    40.                };
    41.  
    42.                struct v2f {
    43.                    float4 vertex : POSITION;
    44.                    fixed4 color : COLOR;
    45.                    float2 texcoord : TEXCOORD0;
    46.                    #ifdef SOFTPARTICLES_ON
    47.                    float4 projPos : TEXCOORD1;
    48.                    #endif
    49.                };
    50.            
    51.                struct Input {
    52.                     float2 uv_MainTex;
    53.                     float2 uv_BumpMap;
    54.                     float3 viewDir;
    55.                 };
    56.            
    57.                 sampler2D _MainTex;
    58.                sampler2D _BumpMap;
    59.                sampler2D _CameraDepthTexture;
    60.            
    61.                fixed4 _TintColor;
    62.                float4 _RimColor;
    63.                float4 _MainTex_ST;
    64.                float _RimPower;
    65.                float _InvFade;
    66.  
    67.                v2f vert (appdata_t v)
    68.                {
    69.                    v2f o;
    70.                    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    71.                    #ifdef SOFTPARTICLES_ON
    72.                    o.projPos = ComputeScreenPos (o.vertex);
    73.                    COMPUTE_EYEDEPTH(o.projPos.z);
    74.                    #endif
    75.                    o.color = v.color;
    76.                    o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
    77.                    return o;
    78.                }
    79.  
    80.                fixed4 frag (v2f i) : COLOR
    81.                {
    82.                    #ifdef SOFTPARTICLES_ON
    83.                    float sceneZ = LinearEyeDepth (UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos))));
    84.                    float partZ = i.projPos.z;
    85.                    float fade = saturate (_InvFade * (sceneZ-partZ));
    86.                    i.color.a *= fade;
    87.                    #endif
    88.  
    89.                    return 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord);
    90.                }
    91.            
    92.                 void surf (Input IN, inout SurfaceOutput o) {
    93.                     o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    94.                     o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
    95.                     half rim = 1.0 - saturate(normalize(IN.viewDir)));
    96.                     o.Emission = _RimColor.rgb * pow (rim, _RimPower);
    97.                 }
    98.            
    99.                ENDCG
    100.            }
    101.        }
    102.  
    103.        // ---- Dual texture cards
    104.        SubShader {
    105.            Pass {
    106.                SetTexture [_MainTex] {
    107.                    constantColor [_TintColor]
    108.                    combine constant * primary
    109.                }
    110.                SetTexture [_MainTex] {
    111.                    combine texture * previous DOUBLE
    112.                }
    113.            }
    114.        }
    115.  
    116.        // ---- Single texture cards (does not do color tint)
    117.        SubShader {
    118.            Pass {
    119.                SetTexture [_MainTex] {
    120.                    combine texture * primary
    121.                }
    122.            }
    123.        }
    124.     }
    125. }
    I'm getting three errors:
    • #pragma surface does not allow specifying other programs at line 27
    • Parse error: syntax error at line 28
    • Syntax error at line 95
    What I started with was culling the backfaces, which worked well. The next step I tried doing was merging the contents of the Rim Shader as shown on the Unity Manual with the Particles/Additive shader.

    I think I see how the Rim Shader works; it's simply adding the Rim Color more strongly to parts that are facing more perpendicular to the player's viewport.

    How should I go about making sure that the translucent/emissive nature of the particle shader mixes with the rim highlights of the Rim shader? Obviously I'm not doing it right, but I don't have the knowledge to do what I want.

    EDIT: I'm also trying this from the other direction - modifying a Rim Shader into an Additive Rim Shader - and I'm having no luck, now because the texture isn't properly appearing, and the glowiness you get with the additive shader isn't happening. At least this version isn't breaking, but I need help figuring out how to get closer to something that resembles my screenshot above, but with less ugly.

    Code (csharp):
    1. Shader "Particles/Rim Additive" {
    2.     Properties {
    3.         _TintColor ("Tint Color", Color) = (1.0,1.0,1.0,0.5)
    4.  
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.  
    7.         _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
    8.         _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0
    9.     }
    10.  
    11.     SubShader {
    12.         Tags {
    13.             "RenderType" = "Transparent"
    14.         }
    15.    
    16.         CGPROGRAM
    17.             #pragma surface surf Lambert alpha finalcolor:mycolor
    18.             #pragma target 2.0
    19.  
    20.             struct Input {
    21.                 float2 uv_MainTex;
    22.                 float3 viewDir;
    23.             };
    24.  
    25.             float4 _TintColor;
    26.             sampler2D _MainTex;
    27.             float4 _RimColor;
    28.             float _RimPower;
    29.          
    30.             void mycolor (Input IN, SurfaceOutput o, inout fixed4 color)
    31.             {
    32.                 color *= _TintColor;
    33.             }
    34.  
    35.             void surf (Input IN, inout SurfaceOutput o) {
    36.                 o.Albedo = dot( tex2D (_MainTex, IN.uv_MainTex).rgba, _TintColor);
    37.                 o.Alpha = dot( tex2D(_MainTex, IN.uv_MainTex).rgba, _TintColor);
    38.              
    39.                 half rim = 1.0 - saturate(o.Normal);
    40.                 o.Emission = dot(tex2D(_MainTex, IN.uv_MainTex).rgba, (_RimColor.rgb * pow(rim, _RimPower)));
    41.             }
    42.         ENDCG
    43.     }
    44.  
    45.     Fallback "Diffuse"
    46. }
    47.  
    I really have no clue what I'm doing, and the documentation on how to write a shader completely sucks. Please, someone help.
     
    Last edited: Sep 27, 2014