You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
425 lines
12 KiB
GLSL
425 lines
12 KiB
GLSL
Shader "Enviro/Standard/RaymarchClouds"
|
|
{
|
|
Properties
|
|
{
|
|
|
|
}
|
|
SubShader
|
|
{
|
|
Cull Off ZWrite Off ZTest Always
|
|
|
|
Tags{ "RenderType" = "Opaque" }
|
|
|
|
Pass
|
|
{
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment frag
|
|
#pragma target 3.0
|
|
#pragma exclude_renderers gles d3d9
|
|
#pragma multi_compile __ UNITY_COLORSPACE_GAMMA
|
|
#pragma multi_compile __ ENVIRO_DEPTHBLENDING
|
|
#pragma multi_compile __ ENVIRO_CURLNOISE
|
|
#pragma multi_compile __ ENVIRO_HALTONOFFSET
|
|
#pragma multi_compile __ ENVIROURP
|
|
#include "UnityCG.cginc"
|
|
#include "../../../Core/Resources/Shaders/Core/EnviroFogCore.cginc"
|
|
#include "Core/EnviroVolumeCloudsCore.cginc"
|
|
|
|
uniform half4 _MainTex_ST;
|
|
float4x4 _LeftWorldFromView;
|
|
float4x4 _RightWorldFromView;
|
|
float4x4 _LeftViewFromScreen;
|
|
float4x4 _RightViewFromScreen;
|
|
|
|
#ifdef ENVIRO_HALTONOFFSET
|
|
float _RaymarchOffset;
|
|
float4 _TexelSize;
|
|
#else
|
|
#define BAYER_FACTOR (1.0/16.0)
|
|
const float bayerFilter[16] =
|
|
{
|
|
0.0*(1.0 / 16.0) ,
|
|
8.0*(1.0 / 16.0),
|
|
2.0*(1.0 / 16.0),
|
|
10.0*(1.0 / 16.0),
|
|
12.0*(1.0 / 16.0),
|
|
4.0*(1.0 / 16.0),
|
|
14.0*(1.0 / 16.0),
|
|
6.0*(1.0 / 16.0),
|
|
3.0*(1.0 / 16.0),
|
|
11.0*(1.0 / 16.0),
|
|
1.0*(1.0 / 16.0),
|
|
9.0*(1.0 / 16.0),
|
|
15.0*(1.0 / 16.0),
|
|
7.0*(1.0 / 16.0),
|
|
13.0*(1.0 / 16.0),
|
|
5.0*(1.0 / 16.0)
|
|
};
|
|
#endif
|
|
float3 ScreenSpaceDither(float2 vScreenPos, float3 clr)
|
|
{
|
|
float d = dot(float2(131.0, 312.0), vScreenPos.xy + _Time.y);
|
|
float3 vDither = float3(d, d, d);
|
|
vDither.rgb = frac(vDither.rgb / float3(103.0, 71.0, 97.0)) - float3(0.5, 0.5, 0.5);
|
|
return (vDither.rgb / 15.0) * 1.0 * Luminance(clr);
|
|
}
|
|
|
|
struct appdata
|
|
{
|
|
float4 vertex : POSITION;
|
|
float2 uv : TEXCOORD0;
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
};
|
|
|
|
struct v2f
|
|
{
|
|
float4 position : SV_POSITION;
|
|
float2 uv : TEXCOORD0;
|
|
float3 sky : TEXCOORD1;
|
|
float4 screenPos : TEXCOORD2;
|
|
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
|
|
v2f vert(appdata_img v)
|
|
{
|
|
v2f o;
|
|
UNITY_SETUP_INSTANCE_ID(v);
|
|
UNITY_INITIALIZE_OUTPUT(v2f, o);
|
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
|
#if defined(ENVIROURP)
|
|
o.position = float4(v.vertex.xyz,1.0);
|
|
#if UNITY_UV_STARTS_AT_TOP
|
|
o.position.y *= -1;
|
|
#endif
|
|
#else
|
|
o.position = UnityObjectToClipPos(v.vertex);
|
|
#endif
|
|
o.uv = v.texcoord;
|
|
o.sky.x = saturate(_SunDir.y + 0.25);
|
|
o.sky.y = saturate(clamp(1.0 - _SunDir.y, 0.0, 0.5));
|
|
o.screenPos = ComputeScreenPos(o.position);
|
|
return o;
|
|
}
|
|
|
|
|
|
float4 frag(v2f i) : SV_Target
|
|
{
|
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
|
|
|
|
float4 cameraRay = float4(i.uv * 2.0 - 1.0, 1.0, 1.0);
|
|
//World Space
|
|
float3 EyePosition = _CameraPosition;
|
|
float3 EyePositionDepth = _WorldSpaceCameraPos;
|
|
//Workaround for large scale games where player position will be resetted.
|
|
//float3 EyePosition = float3(0.0,_CameraPosition.y, 0.0);
|
|
|
|
float2 sPos = i.position.xy;
|
|
|
|
float3 ray = 0;
|
|
|
|
|
|
//#if UNITY_SINGLE_PASS_STEREO
|
|
if (unity_StereoEyeIndex == 0)
|
|
{
|
|
cameraRay = mul(_InverseProjection, cameraRay);
|
|
cameraRay = cameraRay / cameraRay.w;
|
|
ray = normalize(mul((float3x3)_InverseRotation, cameraRay.xyz));
|
|
}
|
|
else
|
|
{
|
|
cameraRay = mul(_InverseProjection_SP, cameraRay);
|
|
cameraRay = cameraRay / cameraRay.w;
|
|
ray = normalize(mul((float3x3)_InverseRotation_SP, cameraRay.xyz));
|
|
}
|
|
//#else
|
|
// cameraRay = mul(_InverseProjection, cameraRay);
|
|
// cameraRay = cameraRay / cameraRay.w;
|
|
// ray = normalize(mul((float3x3)_InverseRotation, cameraRay.xyz));
|
|
//#endif
|
|
|
|
|
|
float rawDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, UnityStereoTransformScreenSpaceTex(i.uv));
|
|
bool depthPresent = rawDepth > 0.0;
|
|
float dpth = Linear01Depth(rawDepth);
|
|
|
|
#ifdef ENVIRO_DEPTHBLENDING
|
|
float4x4 proj, eyeToWorld;
|
|
|
|
if (unity_StereoEyeIndex == 0)
|
|
{
|
|
proj = _LeftViewFromScreen;
|
|
eyeToWorld = _LeftWorldFromView;
|
|
}
|
|
else
|
|
{
|
|
proj = _RightViewFromScreen;
|
|
eyeToWorld = _RightWorldFromView;
|
|
}
|
|
|
|
//bit of matrix math to take the screen space coord (u,v,depth) and transform to world space
|
|
float2 uvClip = i.uv * 2.0 - 1.0;
|
|
float clipDepth = rawDepth; // Fix for OpenGl Core thanks to Lars Bertram
|
|
clipDepth = (UNITY_NEAR_CLIP_VALUE < 0) ? clipDepth * 2 - 1 : clipDepth;
|
|
float4 clipPos = float4(uvClip, clipDepth, 1.0);
|
|
float4 viewPos = mul(proj, clipPos); // inverse projection by clip position
|
|
viewPos /= viewPos.w; // perspective division
|
|
float4 wsPos = float4(mul(eyeToWorld, viewPos).xyz, 1);
|
|
float4 wsDir = wsPos - float4(EyePosition, 0);
|
|
float3 viewDir = normalize(wsDir);
|
|
#endif
|
|
|
|
float4 sky = ComputeScatteringClouds(ray, i.sky.xy, _gameTime);
|
|
float4 color = float4(0,0,0,1);
|
|
|
|
float3 LightDirection = _LightDir;
|
|
float3 LightColor = _LightColor.rgb;
|
|
|
|
//Switch to Moon Light Color
|
|
if (_CloudDensityScale.w < _CloudDensityScale.z)
|
|
LightColor = _MoonLightColor.rgb;
|
|
|
|
float pRad = _CloudsParameter.w;
|
|
float3 pCent = float3(EyePosition.x, -pRad, EyePosition.z);
|
|
|
|
float3 startPos;
|
|
float3 endPos;
|
|
|
|
// find nearest inner shell point
|
|
float2 ih = 0.0f;
|
|
uint innerShellHits = intersectRaySphere(
|
|
EyePosition,
|
|
ray,
|
|
pCent,
|
|
pRad + _CloudsParameter.x,
|
|
ih);
|
|
|
|
// find nearest outer shell point
|
|
float2 oh = 0.0f;
|
|
uint outerShellHits = intersectRaySphere(
|
|
EyePosition,
|
|
ray,
|
|
pCent,
|
|
pRad + _CloudsParameter.y,
|
|
oh);
|
|
|
|
// world space ray intersections
|
|
float3 innerShellHit = EyePositionDepth + (ray * ih.x);
|
|
float3 outerShellHit = EyePositionDepth + (ray * oh.x);
|
|
|
|
float2 hitDistance;
|
|
// eye radius from planet center
|
|
float ch = length(EyePosition - pCent) - _CloudsParameter.w;
|
|
|
|
if (ch < _CloudsParameter.x)
|
|
{
|
|
|
|
#ifdef ENVIRO_DEPTHBLENDING
|
|
// exit if there's something in front of the start of the cloud volume
|
|
if ((depthPresent && (distance(wsPos, EyePositionDepth) < distance(innerShellHit, EyePositionDepth))) || ray.y < -0.05) // shell hits are guaranteed, but the ground may be occluding cloud layer
|
|
{
|
|
return float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
}
|
|
#else
|
|
|
|
if (ray.y < -0.02)
|
|
return float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
#endif
|
|
|
|
endPos = outerShellHit;
|
|
|
|
hitDistance = float2(ih.x, oh.x);
|
|
}
|
|
else if (ch > _CloudsParameter.y)
|
|
{
|
|
float3 firstShellHit = outerShellHit;
|
|
float3 secondShellHit = outerShellHits == 2u && innerShellHits == 0u ? EyePosition + (ray * oh.y) : innerShellHit;
|
|
|
|
#ifdef ENVIRO_DEPTHBLENDING
|
|
if (outerShellHits == 0u || depthPresent && (distance(wsPos, EyePositionDepth) <= distance(firstShellHit, EyePositionDepth)))
|
|
{
|
|
return float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
}
|
|
#endif
|
|
|
|
endPos = secondShellHit;
|
|
|
|
float hit2 = outerShellHits == 2u && innerShellHits == 0u ? oh.y : ih.x;
|
|
hitDistance = float2(oh.x, hit2);
|
|
|
|
}
|
|
else // between shells
|
|
{
|
|
float3 shellHit = innerShellHits > 0u ? innerShellHit : outerShellHit;
|
|
float hit = innerShellHits > 0u ? ih.x : oh.x;
|
|
float height = Remap(EyePosition.y, _CloudsParameter.x, _CloudsParameter.y, 0, 1);
|
|
hitDistance = ResolveInside(EyePosition.xyz, ray, lerp(25000, 100000, height));
|
|
|
|
#ifdef ENVIRO_DEPTHBLENDING
|
|
if (depthPresent && (distance(wsPos, EyePositionDepth) < distance(shellHit, EyePositionDepth)))
|
|
{
|
|
shellHit = wsPos;
|
|
hitDistance.y = (wsPos - EyePositionDepth) / viewDir;
|
|
}
|
|
#endif
|
|
endPos = shellHit;
|
|
|
|
//float reducedDistance = 500 * (1.0 + 0.0) / (1 * lerp(1.0, 0.015, smoothstep(-0.2, -0.6, 0.1)));
|
|
//hit = min(hit, 0.0 + reducedDistance);
|
|
//hitDistance = float2(0.0, hit);
|
|
}
|
|
|
|
hitDistance.x = max(0.0, hitDistance.x);
|
|
///
|
|
|
|
int steps = (int)lerp(_Steps.x, _Steps.x, ray.y);
|
|
float rayStepLength = (1 * (hitDistance.y - hitDistance.x) / steps);
|
|
float3 rayStep = ray * rayStepLength;
|
|
|
|
#ifdef ENVIRO_HALTONOFFSET
|
|
const float bayerOffsets[3][3] = {
|
|
{ 0, 7, 3 },
|
|
{ 6, 5, 2 },
|
|
{ 4, 1, 8 }
|
|
};
|
|
|
|
float2 screenPos = i.screenPos.xy / i.screenPos.w;
|
|
int2 texelID = int2(fmod(screenPos, 3.0)); //Calculate a texel id to index bayer matrix.
|
|
|
|
float bayerOffset = (bayerOffsets[texelID.x][texelID.y]) / 9.0f; //bayeroffset between[0,1)
|
|
float offset = -fmod(_RaymarchOffset + bayerOffset, 1.0f); //final offset combined. The value will be multiplied by sample step in GetDensity.
|
|
|
|
float3 pos = (EyePosition + (hitDistance.x + offset * rayStepLength) * ray);
|
|
rayStepLength = rayStepLength * offset;
|
|
|
|
#else
|
|
float3 pos = (EyePosition + (hitDistance.x + rayStepLength) * ray);
|
|
uint a = uint(i.uv.x) % 4;
|
|
uint b = uint(i.uv.y) % 4;
|
|
pos += bayerFilter[a * 4 + b] * rayStep;
|
|
#endif
|
|
|
|
float cloud_test = 0.0;
|
|
int zero_density_sample_count = 0;
|
|
float sampled_density_previous = -1.0;
|
|
float ds = 0.0;
|
|
float trans = 1.0;
|
|
float intensity = 0.0;
|
|
float alpha = 1.0;
|
|
float eyeToEnd = distance(EyePosition, endPos);
|
|
float lod = saturate((0.5 - Remap(eyeToEnd, 0, _CloudsParameter.w * 0.1, 0, 1.25) ) * 1.25);
|
|
float inScatteringAngle = dot(normalize(ray), normalize(LightDirection));
|
|
|
|
#ifndef ENVIRO_DEPTHBLENDING
|
|
// Reduce steps when rendering behind objects.
|
|
if (dpth < 1)
|
|
steps *= _stepsInDepth;
|
|
#else
|
|
#endif
|
|
//Raymarching
|
|
[loop]
|
|
for (int iCount = 0; iCount < steps; iCount++)
|
|
{
|
|
|
|
#ifdef ENVIRO_HALTONOFFSET
|
|
pos += rayStep;
|
|
#endif
|
|
//Calculate projection height
|
|
float height = GetSamplingHeight(pos, pCent);
|
|
|
|
//Get out of expensive raymarching
|
|
if (alpha <= 0.01 || height > 1.0 || height < 0.0 || _CloudsCoverageSettings.x <= -0.9)
|
|
break;
|
|
|
|
// Get Weather Data
|
|
float3 weather = GetWeather(pos);
|
|
|
|
if (cloud_test > 0.0)
|
|
{
|
|
float sampled_density = CalculateCloudDensity(pos, pCent, weather, 0, lod, true);
|
|
|
|
if (sampled_density == 0.0 && sampled_density_previous == 0.0)
|
|
{
|
|
zero_density_sample_count++;
|
|
}
|
|
|
|
if (zero_density_sample_count < 11 && sampled_density != 0.0)
|
|
{
|
|
float dl = GetDensityAlongRay(pos, pCent, LightDirection, weather, lod);
|
|
ds += saturate(sampled_density);
|
|
|
|
float extinction = _CloudDensityScale.x * sampled_density;
|
|
float transmittance = exp(-extinction );
|
|
|
|
float hg = max(HenryGreenstein(inScatteringAngle, _CloudsLighting.y) * 0.5, _CloudsLighting.z * 2 * HenryGreenstein(inScatteringAngle, 0.99 - _CloudsLighting.w));
|
|
float luminance = GetLightEnergy(pos, height, dl, ds, hg, inScatteringAngle, rayStepLength, _CloudsLighting.x, weather);
|
|
|
|
float integScatt = (luminance - luminance * transmittance);
|
|
|
|
intensity += trans * integScatt;
|
|
trans *= transmittance;
|
|
alpha *= max(trans, 0.0);
|
|
|
|
float3 sunLight = pow(LightColor, 2) * _LightIntensity;
|
|
sunLight.rgb = sunLight.rgb * intensity * saturate(alpha);
|
|
color.rgb += sunLight.rgb;
|
|
|
|
if (alpha <= _CloudsCoverageSettings.z)
|
|
alpha = 0.0;
|
|
}
|
|
// if not, then set cloud_test to zero so that we go back to the cheap sample case
|
|
else
|
|
{
|
|
cloud_test = 0.0;
|
|
zero_density_sample_count = 0;
|
|
}
|
|
|
|
sampled_density_previous = sampled_density;
|
|
}
|
|
else
|
|
{
|
|
// sample density the cheap way, only using the low frequency noise
|
|
cloud_test = CalculateCloudDensity(pos, pCent, weather, 0, lod, false);
|
|
|
|
if (cloud_test == 0.0)
|
|
{
|
|
pos += rayStep;
|
|
}
|
|
else //take a step back and capture area we skipped.
|
|
{
|
|
pos -= rayStep;
|
|
}
|
|
}
|
|
#ifndef ENVIRO_HALTONOFFSET
|
|
pos += rayStep;
|
|
#endif
|
|
}
|
|
|
|
color.a = saturate(1 - alpha);
|
|
|
|
// Ambient Lighting
|
|
float3 ambientColor = (sky.rgb * _AmbientLightColor.rgb);
|
|
color = color + float4(ambientColor * _AmbientSkyColorIntensity * _CloudsLightingExtended.y, 0) * saturate(1 - (color));
|
|
|
|
//Tonemapping
|
|
if (_CloudsLightingExtended.z == 0)
|
|
{
|
|
color.rgb = tonemapACES(color.rgb, _CloudsLightingExtended.w);
|
|
}
|
|
|
|
//Dithering
|
|
color.rgb += ScreenSpaceDither(sPos, color.rgb);
|
|
|
|
|
|
#if defined(UNITY_COLORSPACE_GAMMA)
|
|
color.rgb = LinearToGammaSpace(color.rgb);
|
|
#endif
|
|
return color;
|
|
}
|
|
ENDCG
|
|
}
|
|
}
|
|
}
|