Search Unity

How to use procedural texture in a vertex shader

Discussion in 'Shaders' started by Brad-Newman, Feb 14, 2019.

  1. Brad-Newman

    Brad-Newman

    Joined:
    Feb 7, 2013
    Posts:
    185
    What I'm trying to do essentially is:
    1. Calculate a procedural displacement texture.
    2. Use this texture to displace vertices in the vertex shader.

    So far I'm able to generate the texture in a fragment shader, wondering the best way to then get this texture data into a vertex shader to do the vertex displacement. Would it be a multi pass shader, would you need a render texture?

    Here is my current code:
    Code (CSharp):
    1. Shader "Custom/ProceduralDisplacement"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _Color ("Color", Color) = (1,1,1,1)  
    7.         _SmoothStepX("Smooth Step X", Float) = 0
    8.         _SmoothStepY("Smooth Step Y", Float) = 1
    9.     }
    10.  
    11.  
    12.     SubShader
    13.     {
    14.         Tags { "RenderType"="Opaque" }
    15.         LOD 100
    16.        
    17.         Pass
    18.         {
    19.             Tags {"LightMode"="ForwardBase"}
    20.  
    21.             CGPROGRAM
    22.             #pragma vertex vert
    23.             #pragma fragment frag
    24.             #pragma target 3.0
    25.             #pragma multi_compile_fog
    26.             #include "UnityCG.cginc"
    27.             #include "UnityLightingCommon.cginc"
    28.  
    29.             struct appdata
    30.             {
    31.                 float4 vertex : POSITION;
    32.                 float3 normal : NORMAL;
    33.                 float2 uv : TEXCOORD0;
    34.             };
    35.  
    36.             struct v2f
    37.             {
    38.                 float4 vertex : SV_POSITION;
    39.                 float2 uv : TEXCOORD0;
    40.                 fixed4 diff : COLOR0;
    41.                 UNITY_FOG_COORDS(1)
    42.             };
    43.  
    44.             sampler2D _MainTex;
    45.             float4 _MainTex_ST;
    46.             fixed4 _Color;
    47.             float _SmoothStepX;
    48.             float _SmoothStepY;
    49.  
    50.             float2 random2(float2 p)
    51.             {
    52.                 return frac(sin(float2(dot(p,float2(127.1,311.7)),dot(p,float2(269.5,183.3))))*43758.5453);
    53.             }
    54.  
    55.             v2f vert (appdata v)
    56.             {
    57.                 v2f o;
    58.  
    59.                 o.vertex = UnityObjectToClipPos(v.vertex);
    60.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    61.                                
    62.                 //Lighting
    63.                 half3 worldNormal = UnityObjectToWorldNormal(v.normal);
    64.                 half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
    65.                 o.diff = nl * _LightColor0;
    66.                
    67.                 UNITY_TRANSFER_FOG(o,o.vertex);
    68.                 return o;
    69.             }
    70.            
    71.             fixed3 GenerateDisplacementTex(float2 iuvs)
    72.             {
    73.                 fixed3 displacementColor = fixed3(0.0,0.0,0.0);
    74.                 fixed2 intdispUV = floor(iuvs);
    75.                 fixed2 fracdispUV = frac(iuvs);
    76.                 float m_dist = 1.0; //min distance
    77.  
    78.                 for (int y = -1; y <= 1; y++)
    79.                 {
    80.                     for (int x = -1; x <= 1; x++)
    81.                     {
    82.                         // Neighbor place in the grid
    83.                         fixed2 neighbor = fixed2(float(x),float(y));
    84.  
    85.                         // Random position from current + neighbor place in the grid
    86.                         fixed2 randPoint = random2(intdispUV + neighbor);
    87.  
    88.                         // Animate the point
    89.                         randPoint = (0.5 + 0.5*sin(_Time.y + 6.5 * randPoint));
    90.  
    91.                         // Vector between the pixel and the point
    92.                         fixed2 diff = neighbor + randPoint - fracdispUV;
    93.  
    94.                         // Distance to the point
    95.                         float dist = length(diff);
    96.  
    97.                         // Keep the closer distance
    98.                         m_dist = min(m_dist, dist);
    99.                     }
    100.                 }
    101.  
    102.                 displacementColor += smoothstep(_SmoothStepX,_SmoothStepY,1.0 - m_dist);
    103.                 return displacementColor;
    104.             }
    105.  
    106.             fixed4 frag (v2f i) : SV_Target
    107.             {
    108.                 fixed4 col = tex2D(_MainTex, i.uv) * _Color;
    109.  
    110.                 col.rgb *= GenerateDisplacementTex(i.uv);
    111.                    
    112.                 col *= i.diff;
    113.                 UNITY_APPLY_FOG(i.fogCoord, col);
    114.                 return col;
    115.             }
    116.             ENDCG
    117.         }
    118.     }
    119. }
    120.  
     
    Last edited: Feb 14, 2019
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Call GenerateDisplacementTex() in the vertex shader.
     
  3. Brad-Newman

    Brad-Newman

    Joined:
    Feb 7, 2013
    Posts:
    185
    Yeah, I was considering that. Since I'm using it in frag for color I was hoping to not have to call GenerateDisplacementTex() twice in vert and frag..

    Here's the updated code:
    Code (CSharp):
    1. Shader "Custom/ProceduralDisplacement"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _Color ("Color", Color) = (1,1,1,1)
    7.         _SmoothStepX("Smooth Step X", Float) = 0
    8.         _SmoothStepY("Smooth Step Y", Float) = 1
    9.         _Displacement("Displacement", Float) = 10
    10.         _DisplacementTileX("Displacement Tile X", Float) = 1
    11.         _DisplacementTileY("Displacement Tile Y", Float) = 1
    12.     }
    13.     SubShader
    14.     {
    15.         Tags { "RenderType"="Opaque" }
    16.         LOD 100
    17.      
    18.         Pass
    19.         {
    20.             Tags {"LightMode"="ForwardBase"}
    21.             CGPROGRAM
    22.             #pragma vertex vert
    23.             #pragma fragment frag
    24.             #pragma target 3.0
    25.             #pragma multi_compile_fog
    26.             #include "UnityCG.cginc"
    27.             #include "UnityLightingCommon.cginc"
    28.             struct appdata
    29.             {
    30.                 float4 vertex : POSITION;
    31.                 float3 normal : NORMAL;
    32.                 float2 uv : TEXCOORD0;
    33.             };
    34.             struct v2f
    35.             {
    36.                 float4 vertex : SV_POSITION;
    37.                 float2 uv : TEXCOORD0;
    38.                 fixed4 diff : COLOR0;
    39.                 UNITY_FOG_COORDS(1)
    40.             };
    41.             sampler2D _MainTex;
    42.             float4 _MainTex_ST;
    43.             fixed4 _Color;
    44.             float _SmoothStepX;
    45.             float _SmoothStepY;
    46.             float _Displacement;
    47.             float _DisplacementTileX;
    48.             float _DisplacementTileY;
    49.  
    50.             float2 random2(float2 p)
    51.             {
    52.                 return frac(sin(float2(dot(p,float2(127.1,311.7)),dot(p,float2(269.5,183.3))))*43758.5453);
    53.             }
    54.             fixed3 GenerateDisplacementTex(float2 iuvs)
    55.             {
    56.                 fixed3 displacementColor = fixed3(0.0,0.0,0.0);
    57.                 iuvs = float2(iuvs.x * _DisplacementTileX, iuvs.y * _DisplacementTileY);
    58.                 fixed2 intdispUV = floor(iuvs);
    59.                 fixed2 fracdispUV = frac(iuvs);
    60.                 float m_dist = 1.0; //min distance
    61.                 for (int y = -1; y <= 1; y++)
    62.                 {
    63.                     for (int x = -1; x <= 1; x++)
    64.                     {
    65.                         // Neighbor place in the grid
    66.                         fixed2 neighbor = fixed2(float(x),float(y));
    67.                         // Random position from current + neighbor place in the grid
    68.                         fixed2 randPoint = random2(intdispUV + neighbor);
    69.                         // Animate the point
    70.                         randPoint = (0.5 + 0.5*sin(_Time.y + 6.5 * randPoint));
    71.                         // Vector between the pixel and the point
    72.                         fixed2 diff = neighbor + randPoint - fracdispUV;
    73.                         // Distance to the point
    74.                         float dist = length(diff);
    75.                         // Keep the closer distance
    76.                         m_dist = min(m_dist, dist);
    77.                     }
    78.                 }
    79.                 displacementColor += smoothstep(_SmoothStepX,_SmoothStepY,1.0 - m_dist);
    80.                 return displacementColor;
    81.             }
    82.  
    83.             v2f vert (appdata v)
    84.             {
    85.                 v2f o;
    86.                 //Displacement
    87.                 fixed3 displacementColor = GenerateDisplacementTex(v.uv);
    88.                 v.vertex.xyz += (v.normal * displacementColor) * _Displacement;
    89.  
    90.                 o.vertex = UnityObjectToClipPos(v.vertex);
    91.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    92.              
    93.                 //Lighting
    94.                 half3 worldNormal = UnityObjectToWorldNormal(v.normal);
    95.                 half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
    96.                 o.diff = nl * _LightColor0;
    97.              
    98.                 UNITY_TRANSFER_FOG(o,o.vertex);
    99.                 return o;
    100.             }
    101.          
    102.             fixed4 frag (v2f i) : SV_Target
    103.             {
    104.                 fixed4 col = tex2D(_MainTex, i.uv) * _Color;
    105.                 col.rgb *= GenerateDisplacementTex(i.uv);
    106.                  
    107.                 col *= i.diff;
    108.                 UNITY_APPLY_FOG(i.fogCoord, col);
    109.                 return col;
    110.             }
    111.             ENDCG
    112.         }
    113.     }
    114. }
     
    Last edited: Feb 14, 2019
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    The fragment shader runs after the vertex shader. There's no way to pass data from the fragment to the vertex, only the other way around. And the vertex function only runs on each vertex, so unless your mesh has a very high vertex density there's not a lot of point in passing what you generated in the vertex to the fragment.

    You could certainly render out the pattern to a render texture using something like a CustomRenderTexture and use that instead, but there's no guarantee that's any faster. It depends on how many pixels your object covers vs what resolution you render out at, and if the cost of rendering to and reading from the render texture is less than just calling the function in the shader like this.
     
  5. Brad-Newman

    Brad-Newman

    Joined:
    Feb 7, 2013
    Posts:
    185
    Thanks, appreciate the explanation.