RoadRunner/Assets/shader/cloudy.shader
Kasper Karlgren c84494a697 new files
2025-01-21 13:54:40 +01:00

215 lines
8.9 KiB
GLSL

//Created by Paro.
Shader "Hidden/cloudy"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 viewDir : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _CameraDepthTexture;
float3 _BoundsMin, _BoundsMax;
float _CloudScale, _detailNoiseScale;
float3 _Wind, _detailNoiseWind;
Texture2D<float4> _ShapeNoise;
Texture3D<float4> _DetailNoise;
Texture2D<float4> _BlueNoise;
SamplerState sampler_ShapeNoise;
SamplerState sampler_DetailNoise;
SamplerState sampler_BlueNoise;
float _containerEdgeFadeDst;
float _detailNoiseWeight;
float _DensityThreshold;
float _DensityMultiplier;
float _lightAbsorptionThroughCloud;
float4 _phaseParams;
int _NumSteps, _numStepsLight;
float _lightAbsorptionTowardSun;
float _darknessThreshold;
float _cloudSmooth;
half4 _color;
float _alpha;
float _rayOffsetStrength;
float _RenderDistance;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
float3 viewVector = mul(unity_CameraInvProjection, float4(v.uv * 2 - 1, 0, -1));
o.viewDir = mul(unity_CameraToWorld, float4(viewVector,0));
return o;
}
float remap(float v, float minOld, float maxOld, float minNew, float maxNew) {
return minNew + (v-minOld) * (maxNew - minNew) / (maxOld-minOld);
}
float2 squareUV(float2 uv) {
float width = _ScreenParams.x;
float height =_ScreenParams.y;
//float minDim = min(width, height);
float scale = 1000;
float x = uv.x * width;
float y = uv.y * height;
return float2 (x/scale, y/scale);
}
// Returns (dstToBox, dstInsideBox). If ray misses box, dstInsideBox will be zero
float2 rayBoxDst(float3 boundsMin, float3 boundsMax, float3 rayOrigin, float3 invRaydir) {
// Adapted from: http://jcgt.org/published/0007/03/04/
float3 t0 = (boundsMin - rayOrigin) * invRaydir;
float3 t1 = (boundsMax - rayOrigin) * invRaydir;
float3 tmin = min(t0, t1);
float3 tmax = max(t0, t1);
float dstA = max(max(tmin.x, tmin.y), tmin.z);
float dstB = min(tmax.x, min(tmax.y, tmax.z));
// CASE 1: ray intersects box from outside (0 <= dstA <= dstB)
// dstA is dst to nearest intersection, dstB dst to far intersection
// CASE 2: ray intersects box from inside (dstA < 0 < dstB)
// dstA is the dst to intersection behind the ray, dstB is dst to forward intersection
// CASE 3: ray misses box (dstA > dstB)
float dstToBox = max(0, dstA);
float dstInsideBox = max(0, dstB - dstToBox);
return float2(dstToBox, dstInsideBox);
}
float sampleDensity(float3 pos)
{
float3 uvw = pos * _CloudScale * 0.001 + _Wind.xyz * 0.1 * _Time.y * _CloudScale;
float3 size = _BoundsMax - _BoundsMin;
float3 boundsCentre = (_BoundsMin+_BoundsMax) * 0.5f;
float3 duvw = pos * _detailNoiseScale * 0.001 + _detailNoiseWind.xyz * 0.1 * _Time.y * _detailNoiseScale;
float dstFromEdgeX = min(_containerEdgeFadeDst, min(pos.x - _BoundsMin.x, _BoundsMax.x - pos.x));
float dstFromEdgeY = min(_cloudSmooth, min(pos.y - _BoundsMin.y, _BoundsMax.y - pos.y));
float dstFromEdgeZ = min(_containerEdgeFadeDst, min(pos.z - _BoundsMin.z, _BoundsMax.z - pos.z));
float edgeWeight = min(dstFromEdgeZ,dstFromEdgeX)/_containerEdgeFadeDst;
float4 shape = _ShapeNoise.SampleLevel(sampler_ShapeNoise, uvw.xz, 0);
float4 detail = _DetailNoise.SampleLevel(sampler_DetailNoise, duvw, 0);
float density = max(0, lerp(shape.x, detail.x, _detailNoiseWeight) - _DensityThreshold) * _DensityMultiplier;
return density * edgeWeight * (dstFromEdgeY/_cloudSmooth);
}
// Henyey-Greenstein
float hg(float a, float g) {
float g2 = g*g;
return (1-g2) / (4*3.1415*pow(1+g2-2*g*(a), 1.5));
}
float phase(float a) {
float blend = .5;
float hgBlend = hg(a,_phaseParams.x) * (1-blend) + hg(a,-_phaseParams.y) * blend;
return _phaseParams.z + hgBlend*_phaseParams.w;
}
// Calculate proportion of light that reaches the given point from the lightsource
float lightmarch(float3 position) {
float3 dirToLight = _WorldSpaceLightPos0.xyz;
float dstInsideBox = rayBoxDst(_BoundsMin, _BoundsMax, position, 1/dirToLight).y;
float stepSize = dstInsideBox/_numStepsLight;
float totalDensity = 0;
for (int step = 0; step < _numStepsLight; step ++) {
position += dirToLight * stepSize;
totalDensity += max(0, sampleDensity(position) * stepSize);
}
float transmittance = exp(-totalDensity * _lightAbsorptionTowardSun);
return _darknessThreshold + transmittance * (1-_darknessThreshold);
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
float viewLength = length(i.viewDir);
float3 rayOrigin = _WorldSpaceCameraPos;
float3 rayDir = i.viewDir / viewLength;
//Depth
float nonlin_depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
float depth = LinearEyeDepth(nonlin_depth) * viewLength;
float2 rayToContainerInfo = rayBoxDst(_BoundsMin, _BoundsMax, rayOrigin, 1/rayDir);
float dstToBox = rayToContainerInfo.x;
float dstInsideBox = rayToContainerInfo.y;
if(dstToBox + dstInsideBox > _RenderDistance) return col;
// random starting offset (makes low-res results noisy rather than jagged/glitchy, which is nicer)
float randomOffset = _BlueNoise.SampleLevel(sampler_BlueNoise, squareUV(i.uv *3), 0);
randomOffset *= _rayOffsetStrength;
float dstTravelled = randomOffset;
float stepSize = dstInsideBox / _NumSteps;
float dstLimit = min(depth - dstToBox, dstInsideBox);
float3 entryPoint = rayOrigin + rayDir * dstToBox;
float transmittance = 1;
float3 lightEnergy = 0;
// Phase function makes clouds brighter around sun
float cosAngle = dot(rayDir, _WorldSpaceLightPos0.xyz);
float phaseVal = phase(cosAngle);
while (dstTravelled < dstLimit) {
rayOrigin = entryPoint + rayDir * dstTravelled;
float density = sampleDensity(rayOrigin);
if (density > 0) {
float lightTransmittance = lightmarch(rayOrigin);
lightEnergy += density * stepSize * transmittance * lightTransmittance * phaseVal;
transmittance *= exp(-density * stepSize * _lightAbsorptionThroughCloud);
// Exit early if T is close to zero as further samples won't affect the result much
if (transmittance < 0.1) {
break;
}
}
dstTravelled += stepSize;
}
float3 cloudCol = lightEnergy * _color;
float3 col0 = col * transmittance + cloudCol;
return float4(lerp(col, col0, smoothstep(_RenderDistance, _RenderDistance * 0.25f, dstToBox + dstInsideBox) * _alpha), 0);
}
ENDCG
}
}
}