Skip to content

Instantly share code, notes, and snippets.

@josemorval
Created January 28, 2020 23:14
Show Gist options
  • Save josemorval/d3cf418fc9512d32c3d23e8806da6bf5 to your computer and use it in GitHub Desktop.
Save josemorval/d3cf418fc9512d32c3d23e8806da6bf5 to your computer and use it in GitHub Desktop.

Revisions

  1. josemorval created this gist Jan 28, 2020.
    339 changes: 339 additions & 0 deletions Volumetric rendering
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,339 @@
    // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

    Shader "Unlit/VolumeMarching"
    {
    Properties
    {
    _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
    Tags { "RenderType"="Transparent" "Queue"="Transparent"}
    LOD 100

    Pass
    {

    Blend SrcAlpha OneMinusSrcAlpha

    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    // make fog work
    #pragma multi_compile_fog

    #include "UnityCG.cginc"

    struct appdata
    {
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    };

    struct v2f
    {
    float4 posWorld : TEXCOORD0;
    float4 vertex : SV_POSITION;
    };

    //
    // Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader
    //
    // Original work (webgl-noise) Copyright (C) 2011 Stefan Gustavson
    // Translation and modification was made by Keijiro Takahashi.
    //
    // This shader is based on the webgl-noise GLSL shader. For further details
    // of the original shader, please see the following description from the
    // original source code.
    //

    //
    // GLSL textureless classic 3D noise "cnoise",
    // with an RSL-style periodic variant "pnoise".
    // Author: Stefan Gustavson ([email protected])
    // Version: 2011-10-11
    //
    // Many thanks to Ian McEwan of Ashima Arts for the
    // ideas for permutation and gradient selection.
    //
    // Copyright (c) 2011 Stefan Gustavson. All rights reserved.
    // Distributed under the MIT license. See LICENSE file.
    // https://github.com/ashima/webgl-noise
    //

    float3 mod(float3 x, float3 y)
    {
    return x - y * floor(x / y);
    }

    float3 mod289(float3 x)
    {
    return x - floor(x / 289.0) * 289.0;
    }

    float4 mod289(float4 x)
    {
    return x - floor(x / 289.0) * 289.0;
    }

    float4 permute(float4 x)
    {
    return mod289(((x*34.0)+1.0)*x);
    }

    float4 taylorInvSqrt(float4 r)
    {
    return (float4)1.79284291400159 - r * 0.85373472095314;
    }

    float3 fade(float3 t) {
    return t*t*t*(t*(t*6.0-15.0)+10.0);
    }

    // Classic Perlin noise
    float cnoise(float3 P)
    {
    float3 Pi0 = floor(P); // Integer part for indexing
    float3 Pi1 = Pi0 + (float3)1.0; // Integer part + 1
    Pi0 = mod289(Pi0);
    Pi1 = mod289(Pi1);
    float3 Pf0 = frac(P); // Fractional part for interpolation
    float3 Pf1 = Pf0 - (float3)1.0; // Fractional part - 1.0
    float4 ix = float4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
    float4 iy = float4(Pi0.y, Pi0.y, Pi1.y, Pi1.y);
    float4 iz0 = (float4)Pi0.z;
    float4 iz1 = (float4)Pi1.z;

    float4 ixy = permute(permute(ix) + iy);
    float4 ixy0 = permute(ixy + iz0);
    float4 ixy1 = permute(ixy + iz1);

    float4 gx0 = ixy0 / 7.0;
    float4 gy0 = frac(floor(gx0) / 7.0) - 0.5;
    gx0 = frac(gx0);
    float4 gz0 = (float4)0.5 - abs(gx0) - abs(gy0);
    float4 sz0 = step(gz0, (float4)0.0);
    gx0 -= sz0 * (step((float4)0.0, gx0) - 0.5);
    gy0 -= sz0 * (step((float4)0.0, gy0) - 0.5);

    float4 gx1 = ixy1 / 7.0;
    float4 gy1 = frac(floor(gx1) / 7.0) - 0.5;
    gx1 = frac(gx1);
    float4 gz1 = (float4)0.5 - abs(gx1) - abs(gy1);
    float4 sz1 = step(gz1, (float4)0.0);
    gx1 -= sz1 * (step((float4)0.0, gx1) - 0.5);
    gy1 -= sz1 * (step((float4)0.0, gy1) - 0.5);

    float3 g000 = float3(gx0.x,gy0.x,gz0.x);
    float3 g100 = float3(gx0.y,gy0.y,gz0.y);
    float3 g010 = float3(gx0.z,gy0.z,gz0.z);
    float3 g110 = float3(gx0.w,gy0.w,gz0.w);
    float3 g001 = float3(gx1.x,gy1.x,gz1.x);
    float3 g101 = float3(gx1.y,gy1.y,gz1.y);
    float3 g011 = float3(gx1.z,gy1.z,gz1.z);
    float3 g111 = float3(gx1.w,gy1.w,gz1.w);

    float4 norm0 = taylorInvSqrt(float4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
    g000 *= norm0.x;
    g010 *= norm0.y;
    g100 *= norm0.z;
    g110 *= norm0.w;

    float4 norm1 = taylorInvSqrt(float4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
    g001 *= norm1.x;
    g011 *= norm1.y;
    g101 *= norm1.z;
    g111 *= norm1.w;

    float n000 = dot(g000, Pf0);
    float n100 = dot(g100, float3(Pf1.x, Pf0.y, Pf0.z));
    float n010 = dot(g010, float3(Pf0.x, Pf1.y, Pf0.z));
    float n110 = dot(g110, float3(Pf1.x, Pf1.y, Pf0.z));
    float n001 = dot(g001, float3(Pf0.x, Pf0.y, Pf1.z));
    float n101 = dot(g101, float3(Pf1.x, Pf0.y, Pf1.z));
    float n011 = dot(g011, float3(Pf0.x, Pf1.y, Pf1.z));
    float n111 = dot(g111, Pf1);

    float3 fade_xyz = fade(Pf0);
    float4 n_z = lerp(float4(n000, n100, n010, n110), float4(n001, n101, n011, n111), fade_xyz.z);
    float2 n_yz = lerp(n_z.xy, n_z.zw, fade_xyz.y);
    float n_xyz = lerp(n_yz.x, n_yz.y, fade_xyz.x);
    return 2.2 * n_xyz;
    }

    // Classic Perlin noise, periodic variant
    float pnoise(float3 P, float3 rep)
    {
    float3 Pi0 = mod(floor(P), rep); // Integer part, modulo period
    float3 Pi1 = mod(Pi0 + (float3)1.0, rep); // Integer part + 1, mod period
    Pi0 = mod289(Pi0);
    Pi1 = mod289(Pi1);
    float3 Pf0 = frac(P); // Fractional part for interpolation
    float3 Pf1 = Pf0 - (float3)1.0; // Fractional part - 1.0
    float4 ix = float4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
    float4 iy = float4(Pi0.y, Pi0.y, Pi1.y, Pi1.y);
    float4 iz0 = (float4)Pi0.z;
    float4 iz1 = (float4)Pi1.z;

    float4 ixy = permute(permute(ix) + iy);
    float4 ixy0 = permute(ixy + iz0);
    float4 ixy1 = permute(ixy + iz1);

    float4 gx0 = ixy0 / 7.0;
    float4 gy0 = frac(floor(gx0) / 7.0) - 0.5;
    gx0 = frac(gx0);
    float4 gz0 = (float4)0.5 - abs(gx0) - abs(gy0);
    float4 sz0 = step(gz0, (float4)0.0);
    gx0 -= sz0 * (step((float4)0.0, gx0) - 0.5);
    gy0 -= sz0 * (step((float4)0.0, gy0) - 0.5);

    float4 gx1 = ixy1 / 7.0;
    float4 gy1 = frac(floor(gx1) / 7.0) - 0.5;
    gx1 = frac(gx1);
    float4 gz1 = (float4)0.5 - abs(gx1) - abs(gy1);
    float4 sz1 = step(gz1, (float4)0.0);
    gx1 -= sz1 * (step((float4)0.0, gx1) - 0.5);
    gy1 -= sz1 * (step((float4)0.0, gy1) - 0.5);

    float3 g000 = float3(gx0.x,gy0.x,gz0.x);
    float3 g100 = float3(gx0.y,gy0.y,gz0.y);
    float3 g010 = float3(gx0.z,gy0.z,gz0.z);
    float3 g110 = float3(gx0.w,gy0.w,gz0.w);
    float3 g001 = float3(gx1.x,gy1.x,gz1.x);
    float3 g101 = float3(gx1.y,gy1.y,gz1.y);
    float3 g011 = float3(gx1.z,gy1.z,gz1.z);
    float3 g111 = float3(gx1.w,gy1.w,gz1.w);

    float4 norm0 = taylorInvSqrt(float4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
    g000 *= norm0.x;
    g010 *= norm0.y;
    g100 *= norm0.z;
    g110 *= norm0.w;
    float4 norm1 = taylorInvSqrt(float4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
    g001 *= norm1.x;
    g011 *= norm1.y;
    g101 *= norm1.z;
    g111 *= norm1.w;

    float n000 = dot(g000, Pf0);
    float n100 = dot(g100, float3(Pf1.x, Pf0.y, Pf0.z));
    float n010 = dot(g010, float3(Pf0.x, Pf1.y, Pf0.z));
    float n110 = dot(g110, float3(Pf1.x, Pf1.y, Pf0.z));
    float n001 = dot(g001, float3(Pf0.x, Pf0.y, Pf1.z));
    float n101 = dot(g101, float3(Pf1.x, Pf0.y, Pf1.z));
    float n011 = dot(g011, float3(Pf0.x, Pf1.y, Pf1.z));
    float n111 = dot(g111, Pf1);

    float3 fade_xyz = fade(Pf0);
    float4 n_z = lerp(float4(n000, n100, n010, n110), float4(n001, n101, n011, n111), fade_xyz.z);
    float2 n_yz = lerp(n_z.xy, n_z.zw, fade_xyz.y);
    float n_xyz = lerp(n_yz.x, n_yz.y, fade_xyz.x);
    return 2.2 * n_xyz;
    }


    v2f vert (appdata v)
    {
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.posWorld = mul(unity_ObjectToWorld,v.vertex);
    return o;
    }

    float map(float3 p){
    float t = 1.2*_Time.y;
    float v = 0.5*cnoise(2.1*p+t*float3(0.1,-1.,0.1)).x;
    v+=0.3*cnoise(4.13*p+t*float3(0.2,-1.,0.1)).x;
    v+=0.15*cnoise(8.23*p+t*float3(0.1,-1.,0.2)).x;
    v+=0.05*cnoise(16.23*p+t*float3(0.1,-1.,0.1)).x;
    v = saturate(v+0.05);
    v*=smoothstep(0.7,0.4+0.1*v,length(p));
    return v;
    }

    float4 frag (v2f i) : SV_Target
    {

    //Ryan Brucks based https://shaderbits.com/blog/creating-volumetric-ray-marcher

    float3 cameraPos = _WorldSpaceCameraPos;
    float3 CurPos = i.posWorld.xyz;
    float3 dir = normalize(CurPos-cameraPos);

    int MaxSteps = 32;
    int ShadowSteps = 16;

    float dt = 0.5/float(MaxSteps);
    float ds = 0.5/float(ShadowSteps);

    float3 LightVector = normalize(float3(cos(0.5*_Time.y),1.,sin(0.5*_Time.y)));

    float shadowthresh = 0.01;

    float3 lightenergy = float3(0.,0.,0.);

    float curdensity = 0.;
    float transmittance = 1.;

    for (int i = 0; i < MaxSteps; i++)
    {
    float cursample = map(CurPos);

    //Sample Light Absorption and Scattering
    if( cursample > 0.01)
    {
    float3 lpos = CurPos;
    float shadowdist = 0;

    for (int s = 0; s < ShadowSteps; s++)
    {
    lpos += ds*LightVector;
    float lsample = saturate(map(lpos));

    float3 shadowboxtest = floor( 0.5 + ( abs( 0.5 - lpos ) ) );
    float exitshadowbox = shadowboxtest .x + shadowboxtest .y + shadowboxtest .z;
    shadowdist += lsample;

    if(shadowdist > shadowthresh || exitshadowbox >= 1 || abs(lpos.x)>0.5 || abs(lpos.y)>0.5 || abs(lpos.z)>0.5) break;
    }

    curdensity = saturate(cursample)*2.;
    float3 shadowterm = exp(-shadowdist*float3(8.,16.,32.));
    float3 absorbedlight = shadowterm * curdensity;
    lightenergy += absorbedlight * transmittance;
    transmittance *= 1-curdensity;


    //Sky Lighting
    shadowdist = 0;

    lpos = CurPos + float3(0,0,0.05);
    float lsample = map(lpos);
    shadowdist += lsample;
    lpos = CurPos + float3(0,0,0.1);
    lsample = map(lpos);
    shadowdist += lsample;
    lpos = CurPos + float3(0,0,0.2);
    lsample = map(lpos);
    shadowdist += lsample;

    //shadowterm = exp(-shadowdist * AmbientDensity);
    //absorbedlight = exp(-shadowdist * AmbientDensity) * curdensity;
    lightenergy += exp(-shadowdist * 0.2) * curdensity * float3(0.5,0.5,0.8)*0.75 * transmittance;

    }

    CurPos += dt*dir;

    if(abs(CurPos.x)>0.5 || abs(CurPos.y)>0.5 || abs(CurPos.z)>0.5){
    break;
    }
    }

    float4 col = float4(lightenergy,1.-transmittance);
    return col;
    }
    ENDCG
    }
    }
    }