Search Unity

Raymarched ocean from Shadertoy

Discussion in 'Shaders' started by ekergraphics, Mar 18, 2017.

  1. ekergraphics

    ekergraphics

    Joined:
    Feb 22, 2017
    Posts:
    257
    So I came across this amazing Shadertoy ocean shader, which by sheer luck led me to find a Unity implementation, which works in VR no less:



    There is great documentation from the person who did the port here:

    https://www.shadertoy.com/view/llsXD2

    Now, I know next to nothing about shaders, but still managed to add color selection to this shader with a bit of internet searching. However, the sky color is still weird, because I don't think I've managed to figure it out completely yet. Also, can anyone who knows shaders a bit better tell me why the reflections of other objects go completely crazy only with certain colors:

    Code (CSharp):
    1. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
    2. // ORIGINAL FROM HERE: https://www.reddit.com/r/oculus/comments/3hjl4e/raymarched_ocean_on_vive_in_unity_5_on_980_ti/cu80qfs/
    3.  
    4. Shader "Custom/RayMarchSkyboxSeascape" {
    5.     Properties {
    6.             _UpVector ("Up Vector", Vector) = (0.0,1.0,0.0)
    7.             _RightVector ("Up Vector", Vector) = (1.0,0.0,0.0)
    8.             _ForwardVector ("Up Vector", Vector) = (0.0,0.0,1.0)
    9.             _MyWorldPosition ("My World Location", Vector) = (0.0,0.0,0.0)
    10.             _MyWorldCameraLook ("My World Camera Look", Vector) = (0.0,0.0,0.0)
    11.             _SeaBaseColor ("Sea Base Color", Color) = (0.1,0.19,0.22,1.0)
    12.             _SeaWaterColor ("Sea Water Color", Color) = (0.8,0.9,0.6, 1.0)
    13.             _SkyColor ("Sky Color", Color) = (0.0,0.0,1.0,1.0)
    14.     }
    15.     SubShader {
    16.         Pass {
    17.             CGPROGRAM
    18.      
    19.             #include "UnityCG.cginc"
    20.             #pragma vertex vert
    21.             #pragma fragment frag
    22.      
    23.             #pragma target 3.0
    24.      
    25.             float4 _UpVector;
    26.             float4 _RightVector;
    27.             float4 _ForwardVector;
    28.             float3 _MyWorldPosition;
    29.             float3 _MyWorldCameraLookVector;
    30.             float3 _MyWorldCameraLook;
    31.             fixed4 _SeaBaseColor;
    32.             fixed4 _SeaWaterColor;
    33.             fixed4 _SkyColor;
    34.      
    35.             // Quality metric between 0 and 1 that is computed at the start of frag shader based on the angle
    36.             // between the world look direction and the world ray for that pixel
    37.             float fovQuality = 1.0;
    38.      
    39.             static const int NUM_STEPS = 5;
    40.             static const float PI         = 3.1415;
    41.             static const float EPSILON    = 1e-3;
    42.             float EPSILON_NRM    = 0.01 / 1024;
    43.  
    44.             // sky
    45.             static const float SKY_X = _SkyColor.rgb.r;
    46.             static const float SKY_Y = _SkyColor.rgb.g;
    47.             static const float SKY_Z = _SkyColor.rgb.b;
    48.  
    49.             // sea
    50.             static const int ITER_GEOMETRY_LOW  = 2;
    51.             static const int ITER_GEOMETRY_HIGH = 3;
    52.      
    53.             static const int ITER_FRAGMENT_LOW  = 4;
    54.             static const int ITER_FRAGMENT_HIGH = 5;
    55.      
    56.             static const float SEA_HEIGHT = 0.6;
    57.             static const float SEA_CHOPPY = 4.0;
    58.             static const float SEA_SPEED = 0.8;
    59.             static const float SEA_FREQ = 0.16;
    60.             static const float3 SEA_BASE = _SeaBaseColor.rgb;
    61.             static const float3 SEA_WATER_COLOR = _SeaWaterColor.rgb;
    62.             float SEA_TIME = 0;
    63.             static const float2x2 octave_m = float2x2(1.6,1.2,-1.2,1.6);
    64.  
    65.             float fract(float x) {
    66.                 return x - floor(x);
    67.             }
    68.      
    69.             float2 fract(float2 x) {
    70.                 return x - floor(x);
    71.             }
    72.      
    73.             float hash( float2 p ) {
    74.                 float h = dot(p,float2(127.1,311.7));
    75.                 return fract(sin(h)*43758.5453123);
    76.             }
    77.      
    78.             // TESTED
    79.             float noise( in float2 p ) {
    80.                 float2 i = floor( p );
    81.                 float2 f = fract( p );
    82.                 float2 u = f*f*(3.0-2.0*f);
    83.          
    84.  
    85.                 //return fract(p).x;
    86.          
    87.                 return -1.0+2.0*lerp( lerp( hash( i + float2(0.0,0.0) ),
    88.                                  hash( i + float2(1.0,0.0) ), u.x),
    89.                             lerp( hash( i + float2(0.0,1.0) ),
    90.                                  hash( i + float2(1.0,1.0) ), u.x), u.y);
    91.             }
    92.  
    93.             // lighting
    94.             float diffuse(float3 n,float3 l,float p) {
    95.                 return pow(dot(n,l) * 0.4 + 0.6,p);
    96.             }
    97.             float specular(float3 n,float3 l,float3 e,float s) {
    98.                 float nrm = (s + 8.0) / (3.1415 * 8.0);
    99.                 return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
    100.             }
    101.  
    102.             // sky
    103.             float3 getSkyColor(float3 e) {
    104.                 //return _SkyColor.rgb;
    105.                 e.y = max(e.y,0.0);
    106.                 float3 ret;
    107.                ret.x = max((1.0-e.y), SKY_X);
    108.                ret.y = max((1.0-e.y), SKY_Y);
    109.                ret.z = max((1.0-e.y), SKY_Z);
    110.                 return ret;
    111.             }
    112.  
    113.             /*
    114.                 ret.x = pow(1.0-e.y,2.0);
    115.                 ret.y = 1.0-e.y;
    116.                 ret.z = 0.6+(1.0-e.y)*0.4;
    117.             */
    118.  
    119.  
    120.             // TESTED
    121.             // sea
    122.             float sea_octave(float2 uv, float choppy) {
    123.                 uv += noise(uv);
    124.                 float2 wv = 1.0-abs(sin(uv));
    125.                 float2 swv = abs(cos(uv));
    126.                 wv = lerp(wv,swv,wv);
    127.                 return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
    128.             }
    129.      
    130.             float map(float3 p) {
    131.                 float freq = SEA_FREQ;
    132.                 float amp = SEA_HEIGHT;
    133.                 float choppy = SEA_CHOPPY;
    134.                 float2 uv = p.xz; uv.x *= 0.75;
    135.  
    136.                 // CHECKPOINT - TESTED TO HERE
    137.                 //return uv.x + uv.y;
    138.                // return p.y;
    139.  
    140.                 float d = 0.0, h = 0.0;
    141.                // p = float3(0.0, 0.0, 0.0);
    142.                 //return 0.4f;
    143.                 // int iters = ITER_GEOMETRY_LOW + ceil(fovQuality * (ITER_GEOMETRY_HIGH - ITER_GEOMETRY_LOW));
    144.                 for(int i = 0; i < ITER_GEOMETRY_HIGH; i++) { // ITER_GEOMETRY
    145.                     //d = 19.0; // remove
    146.              
    147.                     //if (i == 2) {
    148.                     //return 0.4f;
    149.                          
    150.                     d = sea_octave((uv+SEA_TIME)*freq,choppy);
    151.                     d += sea_octave((uv-SEA_TIME)*freq,choppy);
    152.  
    153.                     h += d * amp;
    154.                        
    155.                     uv = mul(octave_m, uv);
    156.              
    157.                     freq *= 1.9;
    158.                     amp *= 0.22;
    159.                     choppy = lerp(choppy,1.0,0.2);
    160.                 }
    161.  
    162.                 return p.y - h;
    163.             }
    164.  
    165.             float map_detailed(float3 p) {
    166.                 float freq = SEA_FREQ;
    167.                 float amp = SEA_HEIGHT;
    168.                 float choppy = SEA_CHOPPY;
    169.                 float2 uv = p.xz;
    170.                 uv.x *= 0.75;
    171.          
    172.                 float d, h = 0.0;
    173.          
    174.                 int iters = ITER_FRAGMENT_LOW + ceil(fovQuality * (ITER_FRAGMENT_HIGH - ITER_FRAGMENT_LOW));
    175.                 for(int i = 0; i < iters; i++) {  
    176.                     d = sea_octave((uv+SEA_TIME)*freq,choppy);
    177.                     d += sea_octave((uv-SEA_TIME)*freq,choppy);
    178.                     h += d * amp;  
    179.                     uv = mul(octave_m, uv);
    180.                     freq *= 1.9;
    181.                     amp *= 0.22;
    182.                     choppy = lerp(choppy,1.0,0.2);
    183.                 }
    184.                 return p.y - h;
    185.             }
    186.      
    187.             float3 getSeaColor(float3 p, float3 n, float3 l, float3 eye, float3 dist) {
    188.                 float fresnel = 1.0 - max(dot(n,-eye),0.0);
    189.                 fresnel = pow(fresnel,3.0) * 0.65;
    190.              
    191.                 float3 reflected = getSkyColor(reflect(eye,n));
    192.                 float3 refractted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;
    193.          
    194.                 float3 color_ = lerp(refractted,reflected,fresnel);
    195.          
    196.                 float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);
    197.                 color_ += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
    198.          
    199.                 float c = specular(n,l,eye,60.0);
    200.                 color_ += float3(c, c, c);
    201.          
    202.                 return color_;
    203.             }
    204.  
    205.             // tracing
    206.             float3 getNormal(float3 p, float eps) {
    207.                 float3 n;
    208.                 n.y = map_detailed(p);
    209.                 n.x = map_detailed(float3(p.x+eps,p.y,p.z)) - n.y;
    210.                 n.z = map_detailed(float3(p.x,p.y,p.z+eps)) - n.y;
    211.                 n.y = eps;
    212.                 return normalize(n);
    213.             }
    214.  
    215.             float heightMapTracing(float3 ori, float3 dir, out float3 p) {
    216.                 p = float3(0.0, 0.0, 0.0);
    217.          
    218.                 float tm = 0.0;
    219.                 float tx = 1000.0;
    220.                 float hx = map(ori + dir * tx);
    221.                 if(hx > 0.0) return tx;
    222.                 float hm = map(ori + dir * tm);
    223.                 float tmid = 0.0;
    224.          
    225.                 for(int i = 0; i < NUM_STEPS; i++) {
    226.                     tmid = lerp(tm,tx, hm/(hm-hx));            
    227.                     p = ori + dir * tmid;            
    228.                     float hmid = map(p);
    229.                     if(hmid < 0.0) {
    230.                         tx = tmid;
    231.                         hx = hmid;
    232.                     } else {
    233.                         tm = tmid;
    234.                         hm = hmid;
    235.                     }
    236.                 }
    237.                 return tmid;
    238.             }
    239.  
    240.      
    241.             struct v2f {
    242.                 float4 position : SV_POSITION;
    243.                 //float2 uv : TEXCOORD0; // stores uv
    244.                 float3 worldSpacePosition : TEXCOORD0;
    245.                 float3 worldSpaceView : TEXCOORD1;
    246.             };
    247.      
    248.             v2f vert(appdata_full i) {
    249.                 SEA_TIME = 0; // TODO: swap above
    250.      
    251.                 v2f o;
    252.                 o.position = mul (UNITY_MATRIX_MVP, i.vertex);
    253.          
    254.                 float4 vertexWorld = mul(unity_ObjectToWorld, i.vertex);
    255.          
    256.                 o.worldSpacePosition = vertexWorld.xyz;
    257.                 o.worldSpaceView = vertexWorld.xyz - _WorldSpaceCameraPos;
    258.                 return o;
    259.             }
    260.  
    261.             fixed4 frag(v2f i) : SV_Target {
    262.                    EPSILON_NRM    = 0.35 / _ScreenParams.x;
    263.                    SEA_TIME = _Time[1] * SEA_SPEED;
    264.      
    265.                 float3 ro = _WorldSpaceCameraPos;
    266.                 //float3 ro = float3(0.0, 3.5, 0.0); // TODO: remove
    267.          
    268.                 float3 rd = normalize(i.worldSpaceView);
    269.      
    270.                 // VR Quality based on angular distance from look direction
    271.                 fovQuality = dot(_MyWorldCameraLook, rd);
    272.                 fovQuality = pow(fovQuality, 6);
    273.                  
    274.                 // tracing
    275.                 float3 p = float3(5.0,233.0,1.0);
    276.                 heightMapTracing(ro,rd,p);
    277.                 float3 dist = p - ro;
    278.          
    279.                 // Increase epsilon for lower quality pixels
    280.                 float fovQualityNormalEpsilonFactor = 1.0 + saturate(0.8 - fovQuality);
    281.                 fovQualityNormalEpsilonFactor = dot(fovQualityNormalEpsilonFactor, fovQualityNormalEpsilonFactor);
    282.          
    283.                 fovQualityNormalEpsilonFactor = 1.0;
    284.                 float distToPixelWorldSquared = dot(dist, dist);
    285.          
    286.                 float3 n = getNormal(p, distToPixelWorldSquared * EPSILON_NRM * fovQualityNormalEpsilonFactor);
    287.          
    288.                 float3 light = normalize(float3(sin(SEA_TIME * 0.03f),1.0,cos(SEA_TIME * 0.03f)));
    289.                  
    290.                 //return float4(p.x, p.y, p.z, 1.0);
    291.                      
    292.                 // color
    293.                 float3 color_ = lerp(
    294.                     getSkyColor(rd),
    295.                     getSeaColor(p,n,light,rd,dist),
    296.                     pow(smoothstep(0.0,-0.05,rd.y),0.3));
    297.                  
    298.  
    299.                 //float3 c = getSkyColor(rd);
    300.                 //return float4(c.x, c.y, c.z, 1.0);
    301.          
    302.                 //return float4(p.x, p.y, p.z, 1.0);
    303.                 //float2 f = p.xy;
    304.                 //float n2 = noise(f);
    305.                 //return float4(n2, n2, n2, 1.0);
    306.                 //float2 n3 = float2(5.1, 4.3);
    307.                 //float n2 = n3 - floor (n3);
    308.                 //float n2 = noise(n3);
    309.          
    310.                 //p = ro + rd * 50.0;
    311.        
    312.                 //float j = map(ro + rd * 50.0);
    313.                 //float j = map_detailed(ro + rd * 50.0);
    314.                //float j = sea_octave((ro + rd * 50.0).xz, 4.0);
    315.                 //return float4(j, j, j, 1.0);
    316.               //  return float4(noise(rd.xy), noise(rd.xz), noise(rd.yz), 1.0);
    317.                // return float4(rd.x, rd.y, rd.z, 1.0);
    318.                                  
    319.                 float3 finalColor = pow(color_,float3(0.75, 0.75, 0.75));
    320.          
    321.                 // !! Visualize field of view quality falloff raw
    322.                 // finalColor = float3(fovQuality, fovQuality, fovQuality);
    323.          
    324.                 // !! Visualize field of view quality falloff blended
    325.                 // finalColor = lerp(finalColor, float3(1.0f, 0.0f, 0.0f), fovQuality);
    326.          
    327.                 return float4(finalColor.x, finalColor.y, finalColor.z, 1.0);
    328.  
    329.             }
    330.  
    331.             ENDCG
    332.         }
    333.     }
    334. }
    EDIT: Also, if anyone knows perhaps how to ignore the sky completely so you could apply this material to something other than a skybox, that would be really nice... getting some clouds and sun into the scene... but then I guess it would need to receive light information from the skybox which is probably quite a rewrite...
     
    Last edited: Mar 19, 2017
    EyezLee likes this.