396 lines
15 KiB
GLSL
396 lines
15 KiB
GLSL
Shader "Hidden/Volumetrics"
|
|
{
|
|
Properties
|
|
{
|
|
_MainTex ("Texture", any) = "white" {}
|
|
}
|
|
SubShader
|
|
{
|
|
// No culling or depth
|
|
Cull Off ZWrite Off ZTest Always
|
|
|
|
Pass
|
|
{
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment frag
|
|
|
|
#include "UnityCG.cginc"
|
|
//#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
|
//#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
|
|
|
|
float4x4 _LeftWorldFromView;
|
|
float4x4 _RightWorldFromView;
|
|
float4x4 _LeftViewFromScreen;
|
|
float4x4 _RightViewFromScreen;
|
|
|
|
sampler2D _DitherTexture;
|
|
|
|
UNITY_DECLARE_SHADOWMAP(_CascadeShadowMapTexture);
|
|
UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
|
|
|
|
/* #if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)
|
|
UNITY_DECLARE_TEX2DARRAY(_EnviroCloudsTex);
|
|
#else
|
|
sampler2D _EnviroCloudsTex;
|
|
#endif*/
|
|
|
|
uniform sampler3D _NoiseTexture;
|
|
|
|
uniform int _Steps;
|
|
uniform float3 _CameraPosition;
|
|
uniform float4 _VolumetricLight;
|
|
uniform float4 _HeightFog;
|
|
uniform float4 _HeightParams;
|
|
uniform float4 _NoiseData;
|
|
uniform float3 _WindDirection;
|
|
uniform float4 _MieG;
|
|
uniform float _MaxRayLength;
|
|
uniform float _MaxRayLengthLights;
|
|
|
|
uniform float4 _AmbientColor;
|
|
|
|
uniform float3 _DirLightDir;
|
|
//uniform float4 _DirLightColor;
|
|
|
|
uniform float4 _Randomness;
|
|
#if !SHADER_API_GLES3
|
|
struct PointLight
|
|
{
|
|
float3 pos;
|
|
float range;
|
|
float3 color;
|
|
float padding;
|
|
};
|
|
StructuredBuffer<PointLight> _PointLights;
|
|
float _PointLightsCount;
|
|
|
|
struct SpotLight
|
|
{
|
|
float3 pos;
|
|
float range;
|
|
float3 color;
|
|
float3 lightDirection;
|
|
float lightCosHalfAngle;
|
|
//float2 angularFalloffParameters;
|
|
//float2 distanceFalloffParameters;
|
|
float padding;
|
|
};
|
|
|
|
StructuredBuffer<SpotLight> _SpotLights;
|
|
float _SpotLightsCount;
|
|
#endif
|
|
|
|
struct v2f
|
|
{
|
|
float2 uv : TEXCOORD0;
|
|
float4 position : SV_POSITION;
|
|
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);
|
|
o.position = UnityObjectToClipPos(v.vertex);
|
|
//o.position = v.vertex * float4(2,2,1,1) + float4(-1,-1,0,0);
|
|
o.uv = v.texcoord;
|
|
return o;
|
|
}
|
|
|
|
float4 GetCascadeWeights_SplitSpheres(float3 wpos)
|
|
{
|
|
float3 fromCenter0 = wpos - unity_ShadowSplitSpheres[0].xyz;
|
|
float3 fromCenter1 = wpos - unity_ShadowSplitSpheres[1].xyz;
|
|
float3 fromCenter2 = wpos - unity_ShadowSplitSpheres[2].xyz;
|
|
float3 fromCenter3 = wpos - unity_ShadowSplitSpheres[3].xyz;
|
|
float4 distances2 = float4(dot(fromCenter0,fromCenter0), dot(fromCenter1,fromCenter1), dot(fromCenter2,fromCenter2), dot(fromCenter3,fromCenter3));
|
|
float4 weights = float4(distances2 >= unity_ShadowSplitSqRadii);
|
|
return weights;
|
|
}
|
|
|
|
float4 GetCascadeShadowCoord(float4 pos, float4 cascadeWeights)
|
|
{
|
|
return mul(unity_WorldToShadow[(int)dot(cascadeWeights, float4(1,1,1,1))], pos);
|
|
}
|
|
|
|
|
|
/*
|
|
float GetCascadeWeights_SplitSpheres(float3 positionWS)
|
|
{
|
|
float3 fromCenter0 = positionWS - unity_ShadowSplitSpheres[0].xyz;
|
|
float3 fromCenter1 = positionWS - unity_ShadowSplitSpheres[1].xyz;
|
|
float3 fromCenter2 = positionWS - unity_ShadowSplitSpheres[2].xyz;
|
|
float3 fromCenter3 = positionWS - unity_ShadowSplitSpheres[3].xyz;
|
|
float4 distances2 = float4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3));
|
|
|
|
float4 weights = float4(distances2 >= unity_ShadowSplitSqRadii);
|
|
weights.yzw = saturate(weights.yzw - weights.xyz);
|
|
|
|
return float(4.0) - dot(weights, float4(4, 3, 2, 1));
|
|
}
|
|
|
|
float4 GetCascadeShadowCoord(float3 positionWS, half cascadeIndex)
|
|
{
|
|
float4 shadowCoord = mul(unity_WorldToShadow[cascadeIndex], float4(positionWS, 1.0));
|
|
|
|
return float4(shadowCoord.xyz, 0);
|
|
}
|
|
*/
|
|
|
|
float anisotropy(float costheta)
|
|
{
|
|
float g = _MieG.x;
|
|
float gsq = g*g;
|
|
float denom = 1 + gsq - 2.0 * g * costheta;
|
|
denom = denom * denom * denom;
|
|
denom = sqrt(max(0, denom));
|
|
return (1 - gsq) / denom;
|
|
}
|
|
|
|
float anisotropyPointSpot(float costheta)
|
|
{
|
|
float g = 0.8;
|
|
float gsq = g*g;
|
|
float denom = 1 + gsq - 2.0 * g * costheta;
|
|
denom = denom * denom * denom;
|
|
denom = sqrt(max(0, denom));
|
|
return (1 - gsq) / denom;
|
|
}
|
|
|
|
float Attenuation(float distNorm)
|
|
{
|
|
return 1.0 / (1.0 + 25.0 * distNorm);
|
|
}
|
|
|
|
float DirectionalLight(float3 wpos)
|
|
{
|
|
float atten = 1.0f;
|
|
|
|
float4 cascadeWeights = GetCascadeWeights_SplitSpheres(wpos);
|
|
bool inside = dot(cascadeWeights, float4(1, 1, 1, 1)) < 4;
|
|
|
|
float4 samplePos = GetCascadeShadowCoord(float4(wpos, 1), cascadeWeights);
|
|
|
|
float shadows = UNITY_SAMPLE_SHADOW(_CascadeShadowMapTexture, samplePos.xyz).r;
|
|
|
|
atten = inside ? shadows : 1.0f;
|
|
|
|
if(shadows > 0.0f)
|
|
atten = 1.0f;
|
|
|
|
return atten;
|
|
}
|
|
#if !SHADER_API_GLES3
|
|
float3 PointLights(float3 pos)
|
|
{
|
|
float3 color = 0;
|
|
|
|
for (int i = 0; i < _PointLightsCount; i++)
|
|
{
|
|
float3 posToLight = _PointLights[i].pos - pos;
|
|
float distNorm = dot(posToLight, posToLight) * _PointLights[i].range;
|
|
float att = Attenuation(distNorm);
|
|
|
|
//#if ANISOTROPY
|
|
float3 cameraToPos = normalize(pos - _WorldSpaceCameraPos.xyz);
|
|
float costheta = dot(cameraToPos, normalize(posToLight));
|
|
att *= anisotropyPointSpot(costheta);
|
|
//#endif
|
|
|
|
color += _PointLights[i].color * att;
|
|
}
|
|
return color;
|
|
}
|
|
|
|
float3 SpotLights(float3 pos)
|
|
{
|
|
float3 color = 0;
|
|
for (int i = 0; i < _SpotLightsCount; i++)
|
|
{
|
|
float3 posToLight = _SpotLights[i].pos - pos;
|
|
float distNorm = dot(posToLight, posToLight) * _SpotLights[i].range;
|
|
float att = Attenuation(distNorm);
|
|
|
|
half3 lightVector = normalize(pos - _SpotLights[i].pos);
|
|
half cosAngle = dot(_SpotLights[i].lightDirection.xyz, lightVector);
|
|
|
|
half angleAttenuation = 1;
|
|
angleAttenuation = smoothstep(_SpotLights[i].lightCosHalfAngle, lerp(1, _SpotLights[i].lightCosHalfAngle, 0.8f), cosAngle);
|
|
angleAttenuation = pow(angleAttenuation, 2.0f);
|
|
att *= angleAttenuation;
|
|
|
|
#if ANISOTROPY
|
|
float3 cameraToPos = normalize(pos - _CameraPos.xyz);
|
|
float costheta = dot(cameraToPos, normalize(posToLight));
|
|
att *= anisotropyPointSpot(costheta);
|
|
#endif
|
|
color += _SpotLights[i].color * att;
|
|
|
|
}
|
|
return color;
|
|
}
|
|
#endif
|
|
//-----------------------------------------------------------------------------------------
|
|
// GetDensity
|
|
//-----------------------------------------------------------------------------------------
|
|
float GetDensity(float3 wpos, inout float density, float depth, float3 rayDir)
|
|
{
|
|
density = 1.0f;
|
|
|
|
// #ifdef NOISE
|
|
// float4 noise = tex3D(_NoiseTexture, frac(wpos * _NoiseData.x + float3(_Time.y * _WindDirection.x, 0, _Time.y * _WindDirection.y)));
|
|
// float noiseFbm = (noise.g * 0.625) + (noise.b * 0.25) + (noise.a * 0.125);
|
|
// noiseFbm = saturate(noiseFbm - _NoiseData.y);
|
|
// density *= saturate(noiseFbm);
|
|
// #endif
|
|
return density;
|
|
}
|
|
|
|
|
|
|
|
float2 squareUV(float2 uv)
|
|
{
|
|
float width = _ScreenParams.x;
|
|
float height = _ScreenParams.y;
|
|
float scale = 1000;
|
|
float x = uv.x * width;
|
|
float y = uv.y * height;
|
|
return float2 (x/scale, y/scale);
|
|
}
|
|
float2 WorldToScreenUV(float3 worldPos)
|
|
{
|
|
// Project world position into clip space
|
|
float4 clipPos = mul(UNITY_MATRIX_VP, float4(worldPos, 1.0));
|
|
|
|
// Perspective divide
|
|
clipPos.xyz /= clipPos.w;
|
|
|
|
// Convert from clip space (-1..1) to UV space (0..1)
|
|
float2 uv = clipPos.xy * 0.5 + 0.5;
|
|
|
|
return uv;
|
|
}
|
|
|
|
float4 RayMarch(float2 uv,float2 screenPos, float3 rayStart, float3 rayDir, float rayLength, float rayLengthLights, float linearDepth)
|
|
{
|
|
if (rayLength <= 0.01 || !all(isfinite(rayDir)))
|
|
return float4(0, 0, 0, 0);
|
|
|
|
float2 interleavedPos = fmod(floor(saturate(screenPos.xy)), 8.0);
|
|
interleavedPos = clamp(interleavedPos, 0.0, 7.999);
|
|
|
|
#if UNITY_SINGLE_PASS_STEREO
|
|
float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
|
|
interleavedPos = (interleavedPos - scaleOffset.zw) / scaleOffset.xy;
|
|
#endif
|
|
|
|
float4 ditherUV = saturate(float4(interleavedPos / 8.0 + float2(0.5 / 8.0, 0.5 / 8.0),0,0));
|
|
float offset = tex2Dlod(_DitherTexture, ditherUV).w; //+ _Randomness.xy
|
|
|
|
int stepCount = _Steps;
|
|
|
|
float stepSize = rayLength / stepCount;
|
|
float3 step = rayDir * stepSize;
|
|
|
|
float stepSizeLights = rayLengthLights / stepCount;
|
|
float3 stepLights = rayDir * stepSizeLights;
|
|
|
|
float3 currentPositionDithered = rayStart + step * offset;
|
|
float3 currentPositionLightsDithered = rayStart + stepLights * offset;
|
|
float3 currentPosition = rayStart + step;
|
|
|
|
float4 color = float4(0.0,0.0,0.0,0);
|
|
float cosAngle;
|
|
|
|
float extinction = 0;
|
|
float transmitance = 0;
|
|
float ambient = 0;
|
|
cosAngle = dot(_DirLightDir.xyz, -rayDir);
|
|
|
|
float ani = anisotropy(cosAngle);
|
|
float4 lightsColor = float4(0,0,0,0);
|
|
|
|
[loop]
|
|
for (int i = 0; i < stepCount; i++)
|
|
{
|
|
float density = GetDensity(currentPosition, density, linearDepth, rayDir);
|
|
float2 shadowUV = WorldToScreenUV(currentPositionDithered);
|
|
//float cloudsShadows = pow(UNITY_SAMPLE_SCREENSPACE_TEXTURE(_EnviroCloudsTex, shadowUV).b,0.25);
|
|
|
|
float atten = DirectionalLight(currentPositionDithered) * 0.1 ;//* (1-cloudsShadows);
|
|
|
|
float scattering = _VolumetricLight.x * density;
|
|
extinction += _VolumetricLight.y * density;
|
|
|
|
transmitance += atten * scattering * exp(-extinction);
|
|
#if !SHADER_API_GLES3
|
|
lightsColor.rgb += PointLights(currentPositionLightsDithered) * stepSizeLights * density;
|
|
lightsColor.rgb += SpotLights(currentPositionLightsDithered) * stepSizeLights * density;
|
|
#endif
|
|
currentPosition += step;
|
|
currentPositionDithered += step;
|
|
currentPositionLightsDithered += stepLights;
|
|
}
|
|
|
|
//color.rgb = _DirLightColor.rgb * transmitance * ani;
|
|
color.a = transmitance * ani;
|
|
color.rgb += lightsColor.rgb * 0.1;
|
|
|
|
color = max(0, color);
|
|
|
|
return color;
|
|
}
|
|
|
|
float4 frag (v2f i) : SV_Target
|
|
{
|
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
|
|
|
|
float2 uv = i.uv.xy;
|
|
|
|
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
|
|
float linearDepth = Linear01Depth(depth);
|
|
|
|
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 = depth; // 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
|
|
float3 wpos = mul(eyeToWorld, viewPos).xyz;
|
|
|
|
float3 rayStart = _WorldSpaceCameraPos;
|
|
float3 rayDir = wpos - _WorldSpaceCameraPos;
|
|
//rayDir *= linearDepth;
|
|
|
|
float rayLength = length(rayDir);
|
|
rayDir /= rayLength;
|
|
|
|
float rayLengthLights = min(rayLength, _MaxRayLengthLights);
|
|
rayLength = min(rayLength, _MaxRayLength);
|
|
|
|
float4 color = RayMarch(uv, i.position.xy, rayStart, rayDir, rayLength, rayLengthLights, linearDepth);
|
|
|
|
return color;
|
|
}
|
|
ENDCG
|
|
}
|
|
}
|
|
}
|