Search Unity

Modifying vertex positions in a surface shader

Discussion in 'Shaders' started by Dudledok, Mar 27, 2015.

  1. Dudledok

    Dudledok

    Joined:
    Oct 24, 2013
    Posts:
    110
    I'm trying to modify shaders to created a horizon effect so that the landscape appears curved based by changing the position based on how far away it is.

    I have done this with the default flare shader (I picked this one essentially at random knowing the flare shader wouldn't be a solid colour so the effect would be easily noticeable) and it works fine. You can check it out below:

    Code (CSharp):
    1. Shader "FX/Flare"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Particle Texture", 2D) = "black" {}
    6.         _QOffset ("Offset", Vector) = (0,0,0,0)
    7.         _Dist ("Distance", Float) = 100.0
    8.     }
    9.     SubShader
    10.     {
    11.         Tags
    12.         {
    13.             "Queue"="Transparent"
    14.             "IgnoreProjector"="True"
    15.             "RenderType"="Transparent"
    16.             "PreviewType"="Plane"
    17.         }
    18.         Cull Off Lighting Off ZWrite Off Ztest Always
    19.         Blend One One
    20.  
    21.         Pass
    22.         {  
    23.             CGPROGRAM
    24.             #pragma vertex vert
    25.             #pragma fragment frag
    26.             #include "UnityCG.cginc"
    27.  
    28.             sampler2D _MainTex;
    29.             fixed4 _TintColor;
    30.             float4 _QOffset;
    31.             float _Dist;
    32.  
    33.             struct v2f
    34.             {
    35.                 float4 vertex : SV_POSITION;
    36.                 fixed4 color : COLOR;
    37.                 float3 viewDir : TEXCOORD1;
    38.                 float2 texcoord : TEXCOORD0;
    39.             };
    40.  
    41.             float4 _MainTex_ST;
    42.          
    43.             v2f vert (appdata_full v)
    44.             {
    45.                 v2f o;
    46.                 float4 vPos = mul (UNITY_MATRIX_MV, v.vertex);
    47.                 float zOff = vPos.z/_Dist;
    48.                 vPos += _QOffset*zOff*zOff;
    49.                 o.vertex = mul (UNITY_MATRIX_P, vPos);
    50.                 o.color = v.color;
    51.                 o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
    52.              
    53.                 return o;
    54.             }
    55.  
    56.             fixed4 frag (v2f i) : SV_Target
    57.             {
    58.                 fixed4 col;
    59.                 fixed4 tex = tex2D(_MainTex, i.texcoord);
    60.                 col.rgb = i.color.rgb * tex.rgb;
    61.                 col.a = tex.a;
    62.                 return col;
    63.             }
    64.             ENDCG
    65.         }
    66.     }
    67. }
    I am now trying to use this same idea with a more useful shader which picks from multiple textures based on the normals (I'm sure you've seen this before). The shader is a surface shader so I have edited it to add vertex modifier function:

    Code (CSharp):
    1. Shader "Custom/Terrain"
    2. {
    3.     Properties
    4.     {
    5.         _SandTex ("Sand", 2D) = "white" {}
    6.         _GravelTex ("Gravel", 2D) = "white" {}
    7.         _RockTex ("Rock", 2D) = "white" {}
    8.         _QOffset ("Offset", Vector) = (0,0,0,0)
    9.         _Dist ("Distance", Float) = 100.0
    10.     }
    11.  
    12.     SubShader
    13.     {
    14.         Tags { "RenderType"="Opaque" }
    15.         LOD 200
    16.         CGPROGRAM
    17.         #pragma surface surf Lambert vertex:vert
    18.  
    19.         struct Input
    20.         {
    21.             float3 worldPos;
    22.             float3 worldNormal;
    23.             float4 color : COLOR;
    24.          
    25.             float4 vertex : SV_POSITION;
    26.             float3 viewDir : TEXCOORD1;
    27.             float2 texcoord : TEXCOORD0;
    28.         };
    29.      
    30.         float4 _QOffset;
    31.         float _Dist;
    32.      
    33.         void vert (inout appdata_full v, out Input o)
    34.         {
    35.             float4 vPos = mul (UNITY_MATRIX_MV, v.vertex);
    36.             float zOff = vPos.z/_Dist;
    37.             vPos += _QOffset*zOff*zOff;
    38.             o.vertex = mul (UNITY_MATRIX_P, vPos);
    39.         }
    40.      
    41.         float4 TriplanarSample(sampler2D tex, float3 worldPosition, float3 projNormal, float scale)
    42.         {
    43.             float4 cZY = tex2D(tex, worldPosition.zy * scale);
    44.             float4 cXZ = tex2D(tex, worldPosition.xz * scale);
    45.             float4 cXY = tex2D(tex, worldPosition.xy * scale);
    46.          
    47.             cXY = lerp(cXY, cXZ, projNormal.y);
    48.             return lerp(cXY, cZY, projNormal.x);
    49.         }
    50.  
    51.         sampler2D _SandTex, _GravelTex, _RockTex;
    52.      
    53.         void surf(Input IN, inout SurfaceOutput o)
    54.         {
    55.             float3 projNormal = saturate(pow(IN.worldNormal * 1.5, 4));
    56.          
    57.             half4 sand = TriplanarSample(_SandTex, IN.worldPos, projNormal, 1.0);
    58.          
    59.             half4 gravel = TriplanarSample(_GravelTex, IN.worldPos, projNormal, 1.0);
    60.          
    61.             half4 rock = TriplanarSample(_RockTex, IN.worldPos, projNormal, 1.0);
    62.          
    63.             float4 controlMap = IN.color;
    64.          
    65.             half4 col = lerp(rock, gravel, controlMap.g);
    66.             col = lerp(col, sand, controlMap.r);
    67.          
    68.             o.Albedo = col.rgb;
    69.             o.Alpha = 1.0;
    70.         }
    71.         ENDCG
    72.     }
    73.     FallBack "Diffuse"
    74. }
    75.  
    The result is unchanged from the original effect of picking the appropriate texture but there's no curvature and having not used surface shaders I'm not sure where I'm going wrong with this.

    If you have any questions just ask!
     
    Last edited: Mar 27, 2015
  2. UnityGuillaume

    UnityGuillaume

    Unity Technologies

    Joined:
    Mar 16, 2015
    Posts:
    123
    Output data in vertex shader of a Surface shader are only for interpolation.
    Unity will actually use the appdata structure to generate the "final" vertex shader (that's why it's inout)

    So just write the result back into v.vertex (writing it in o only allow you to have interpolated vertex pos in the surface shader, Unity won't pick up that)

    You can see an example here : http://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html search for Normal Extrusion

    And maybe add addshadow to your pragma, it tell Unity to generate also a shadow generation pass. If you don't add it, your vertex modification won't appears on the shadows (see here : http://docs.unity3d.com/Manual/SL-SurfaceShaders.html)
     
  3. Dudledok

    Dudledok

    Joined:
    Oct 24, 2013
    Posts:
    110
    Thanks, that makes sense :)
    The changes are radically different to before though so I'll have to investigate that: it doesn't gently curve but rather almost everything isn't visible and what is is stretched.
     
  4. UnityGuillaume

    UnityGuillaume

    Unity Technologies

    Joined:
    Mar 16, 2015
    Posts:
    123
    Oh yes sorry, I should have noted that the output the surface shader expect are in MODEL SPACE. So just multiply back to model space your vertex after modifying it by using UNITY_MATRIX_T_MV
     
    UnityLighting and Dudledok like this.