Search Unity

My shader generates height maps for terrain chunks, can it calculate slope as well?

Discussion in 'Shaders' started by TheGabelle, Apr 25, 2017.

  1. TheGabelle

    TheGabelle

    Joined:
    Aug 23, 2013
    Posts:
    242
    I'm completely new to shaders.
    I have a noise-generating frag shader that I'm using to create height maps for terrains. Noise is calculated by pixel location + offset. The noise value fills the Red channel: float4(noise value, 1, 1, 1)

    Is it possible for me to pass the shader the height value of the terrain (ex: 1000f) so it can also calculate the slope for each point between 0..1? Right now I just blit a full RGBAFloat png and would like to use another color channel to carry the slope calculations.
    Something like: float4(noise value, slope value, 1, 1)



    Current Slope Method:
    I'm calculating the slope in a C# script that finds the delta of each heightmap pixel's cardinal neighbors * terrain height, turning it into a vector3(dx, 2f, dy), normalizing it, grabbing the Vector3.Angle(deltaV3, vector3.up), and then dividing the results by 90f to get a slope value between 0..1. The script is far too slow, even when threaded, and doesn't handle edge cases well.

    More info:
    I'm using a modified shader from this asset to create heightmaps for terrains. The shader has been converted to a frag shader, as discussed in the last page or so of the thread.

    I can attach the shader if needed.
     
  2. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
  3. TheGabelle

    TheGabelle

    Joined:
    Aug 23, 2013
    Posts:
    242
    The shader I'm using is producing ridge multifractal, so I'm assuming I need to look at the Fbm derivatives halfway down the page you linked. I can't make heads or tails of it.
    His code:
    Code (CSharp):
    1. // returns 3D fbm and its 3 derivatives
    2. vec4 fbm( in vec3 x, int octaves )
    3. {
    4.     float f = 1.98;  // could be 2.0
    5.     float s = 0.49;  // could be 0.5
    6.     float a = 0.0;
    7.     float b = 0.5;
    8.     vec3  d = vec3(0.0);
    9.     mat3  m = mat3(1.0,0.0,0.0,
    10.                    0.0,1.0,0.0,
    11.                    0.0,0.0,1.0);
    12.     for( int i=0; i < octaves; i++ )
    13.     {
    14.         vec4 n = noised(x);
    15.         a += b*n.x;          // accumulate values      
    16.         d += b*m*n.yzw;      // accumulate derivatives
    17.         b *= s;
    18.         x = f*m3*x;
    19.         m = f*m3i*m;
    20.     }
    21.     return vec4( a, d );
    22. }
     
  4. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Ridged multifractal is just regular perlin with a abs() I think?
    So aside from the zero value it should the reversed slope?
     
  5. TheGabelle

    TheGabelle

    Joined:
    Aug 23, 2013
    Posts:
    242
    Since the noise generation will only happen once per world save, I think I'll just brute it the way I described above. Naturally I assumed I could ±1 or ±1/width the texcoords to get the neighboring samples, but all attempts failed. How exactly do I access the neighbors in a procedural shader? The texture2d images are created at runtime with a resolution of 129 x 129, but the resolution could change.


    Current Shader:

    Code (CSharp):
    1.  
    2. Shader "Modified3DPerlinFrag" {
    3.  
    4.     Properties {
    5.         _Octaves     ("Octaves"    , Float)  = 8
    6.         _Frequency   ("Frequency"  , Float)  = 1
    7.         _Amplitude   ("Amplitude"  , Float)  = 1
    8.         _Lacunarity  ("Lacunarity" , Float)  = 1.92
    9.         _Persistence ("Persistence", Float)  = 0.8
    10.         _Offset      ("Offset"     , Vector) = (0, 0, 0, 0)
    11.         _RidgeOffset ("Ridge Offset", Float) = 1.0
    12. //        _HeightScalar  ("Height Scalar" , Float)  = 1000
    13. //        _QuadScalar  ("Quad Scalar" , Float)  = 1.8
    14.     }
    15.  
    16.  
    17.     SubShader {
    18.  
    19.  
    20.         Tags {
    21.             "RenderType"  = "Opaque"
    22.             "PreviewType" = "Plane"
    23.         }
    24.         LOD 100
    25.  
    26.  
    27.         Cull     Off
    28.         Lighting Off
    29.         ZWrite   Off
    30.         Fog { Mode Off }
    31.         Blend One Zero
    32.  
    33.  
    34.         Pass {
    35.  
    36.             CGPROGRAM
    37.  
    38.             #pragma vertex   vert            
    39.             #pragma fragment frag
    40.  
    41.             struct vertInput {
    42.                 float4 pos       : POSITION;
    43.                 float2 texcoord  : TEXCOORD0;
    44. //                float4 color     : COLOR;
    45.             };
    46.  
    47.             struct vertOutput {
    48.                 float4 pos      : SV_POSITION;
    49.                 half2  texcoord    : TEXCOORD0;
    50. //                fixed4 color    : COLOR;
    51.             };
    52.  
    53.             vertOutput vert (vertInput input) {
    54.                 vertOutput o;
    55.  
    56.                 o.pos      = UnityObjectToClipPos (input.pos);
    57.                 o.texcoord = input.texcoord;
    58. //                o.color    = input.color;
    59.  
    60.                 return o;
    61.             }
    62.  
    63.            void FAST32_hash_3D(     float3 gridcell,
    64.                                 out float4 lowz_hash_0,
    65.                                 out float4 lowz_hash_1,
    66.                                 out float4 lowz_hash_2,
    67.                                 out float4 highz_hash_0,
    68.                                 out float4 highz_hash_1,
    69.                                 out float4 highz_hash_2    )        //    generates 3 random numbers for each of the 8 cell corners
    70.         {
    71.  
    72.             const float2 OFFSET = float2( 50.0, 161.0 );
    73.             const float DOMAIN = 69.0;
    74.             const float3 SOMELARGEFLOATS = float3( 635.298681, 682.357502, 668.926525 );
    75.             const float3 ZINC = float3( 48.500388, 65.294118, 63.934599 );
    76.        
    77.             //    truncate the domain
    78.             gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;
    79.             float3 gridcell_inc1 = step( gridcell, float3( DOMAIN - 1.5, DOMAIN - 1.5, DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );
    80.        
    81.             //    calculate the noise
    82.             float4 P = float4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;
    83.             P *= P;
    84.             P = P.xzxz * P.yyww;
    85.             float3 lowz_mod = float3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );
    86.             float3 highz_mod = float3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );
    87.             lowz_hash_0 = frac( P * lowz_mod.xxxx );
    88.             highz_hash_0 = frac( P * highz_mod.xxxx );
    89.             lowz_hash_1 = frac( P * lowz_mod.yyyy );
    90.             highz_hash_1 = frac( P * highz_mod.yyyy );
    91.             lowz_hash_2 = frac( P * lowz_mod.zzzz );
    92.             highz_hash_2 = frac( P * highz_mod.zzzz );
    93.         }
    94.  
    95.         float3 Interpolation_C2( float3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }
    96.  
    97.         float Perlin3D( float3 P )
    98.         {
    99.             //    establish our grid cell and unit position
    100.             float3 Pi = floor(P);
    101.             float3 Pf = P - Pi;
    102.             float3 Pf_min1 = Pf - 1.0;
    103.  
    104.             float4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1;
    105.             FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );
    106.        
    107.             //    calculate the gradients
    108.             float4 grad_x0 = hashx0 - 0.49999;
    109.             float4 grad_y0 = hashy0 - 0.49999;
    110.             float4 grad_z0 = hashz0 - 0.49999;
    111.             float4 grad_x1 = hashx1 - 0.49999;
    112.             float4 grad_y1 = hashy1 - 0.49999;
    113.             float4 grad_z1 = hashz1 - 0.49999;
    114.             float4 grad_results_0 = rsqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 );
    115.             float4 grad_results_1 = rsqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 );
    116.        
    117.             //    Classic Perlin Interpolation
    118.             float3 blend = Interpolation_C2( Pf );
    119.             float4 res0 = lerp( grad_results_0, grad_results_1, blend.z );
    120.             float2 res1 = lerp( res0.xy, res0.zw, blend.y );
    121.             float final = lerp( res1.x, res1.y, blend.x );
    122.             final *= 1.1547005383792515290182975610039;        //    (optionally) scale things to a strict -1.0->1.0 range    *= 1.0/sqrt(0.75)
    123.             return final;
    124.         }
    125.  
    126.         float PerlinRidged(float3 p, int octaves, float3 offset, float frequency, float amplitude, float lacunarity, float persistence, float ridgeOffset)
    127.         {
    128.             float sum = 0;
    129.             for (int i = 0; i < octaves; i++)
    130.             {
    131.                 float h = 0;
    132.                 h = 0.5 * (ridgeOffset - abs(4*Perlin3D((p + offset) * frequency)));
    133.                 sum += h*amplitude;
    134.                 frequency *= lacunarity;
    135.                 amplitude *= persistence;
    136.             }
    137.             return sum;
    138.         }
    139.  
    140.             fixed  _Octaves;
    141.             float  _Frequency;
    142.             float  _Amplitude;
    143.             float  _Lacunarity;
    144.             float  _Persistence;
    145.             float3 _Offset;
    146.             float  _CutoutThreshold;
    147.             float  _RidgeOffset;
    148.             float  _HeightScalar;
    149.             float  _QuadScalar;
    150.  
    151.             half4 frag (vertOutput output) : COLOR {
    152.                 float ResultHeight = PerlinRidged (float3 (output.texcoord.x, output.texcoord.y, 0), _Octaves, _Offset, _Frequency, _Amplitude, _Lacunarity, _Persistence, _RidgeOffset);
    153.  
    154.                  ResultHeight = ResultHeight * 0.5;// + 0.5;
    155.                  return float4 (ResultHeight, 0, 0, 1);
    156.             }
    157.  
    158.             ENDCG
    159.         }
    160.     }
    161. }