650 lines
19 KiB
GLSL
650 lines
19 KiB
GLSL
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
|
|
|
|
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
|
|
// Upgrade NOTE: replaced 'unity_World2Shadow' with 'unity_WorldToShadow'
|
|
|
|
// Copyright(c) 2016, Michal Skalsky
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification,
|
|
// are permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software without
|
|
// specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT
|
|
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
Shader "Sandbox/VolumetricLight"
|
|
{
|
|
Properties
|
|
{
|
|
[HideInInspector]_MainTex ("Texture", 2D) = "white" {}
|
|
[HideInInspector]_ZTest ("ZTest", Float) = 0
|
|
[HideInInspector]_LightColor("_LightColor", Color) = (1,1,1,1)
|
|
}
|
|
SubShader
|
|
{
|
|
Tags { "RenderType"="Opaque" }
|
|
LOD 100
|
|
|
|
CGINCLUDE
|
|
|
|
#if defined(SHADOWS_DEPTH) || defined(SHADOWS_CUBE)
|
|
#define SHADOWS_NATIVE
|
|
#endif
|
|
|
|
#include "UnityCG.cginc"
|
|
#include "UnityDeferredLibrary.cginc"
|
|
|
|
sampler3D _NoiseTexture;
|
|
sampler2D _DitherTexture;
|
|
|
|
float4 _FrustumCorners[4];
|
|
|
|
struct appdata
|
|
{
|
|
float4 vertex : POSITION;
|
|
};
|
|
|
|
float4x4 _WorldViewProj;
|
|
float4x4 _MyLightMatrix0;
|
|
float4x4 _MyWorld2Shadow;
|
|
|
|
float3 _CameraForward;
|
|
|
|
// x: scattering coef, y: extinction coef, z: range w: skybox extinction coef
|
|
float4 _VolumetricLight;
|
|
// x: 1 - g^2, y: 1 + g^2, z: 2*g, w: 1/4pi
|
|
float4 _MieG;
|
|
|
|
// x: scale, y: intensity, z: intensity offset
|
|
float4 _NoiseData;
|
|
// x: x velocity, y: z velocity
|
|
float4 _NoiseVelocity;
|
|
// x: ground level, y: height scale, z: unused, w: unused
|
|
float4 _HeightFog;
|
|
//float4 _LightDir;
|
|
|
|
float _MaxRayLength;
|
|
|
|
int _SampleCount;
|
|
|
|
struct v2f
|
|
{
|
|
float4 pos : SV_POSITION;
|
|
float4 uv : TEXCOORD0;
|
|
float3 wpos : TEXCOORD1;
|
|
};
|
|
|
|
v2f vert(appdata v)
|
|
{
|
|
v2f o;
|
|
o.pos = mul(_WorldViewProj, v.vertex);
|
|
o.uv = ComputeScreenPos(o.pos);
|
|
o.wpos = mul(unity_ObjectToWorld, v.vertex);
|
|
return o;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// GetCascadeWeights_SplitSpheres
|
|
//-----------------------------------------------------------------------------------------
|
|
inline fixed4 GetCascadeWeights_SplitSpheres(float3 wpos)
|
|
{
|
|
float3 fromCenter0 = wpos.xyz - unity_ShadowSplitSpheres[0].xyz;
|
|
float3 fromCenter1 = wpos.xyz - unity_ShadowSplitSpheres[1].xyz;
|
|
float3 fromCenter2 = wpos.xyz - unity_ShadowSplitSpheres[2].xyz;
|
|
float3 fromCenter3 = wpos.xyz - unity_ShadowSplitSpheres[3].xyz;
|
|
float4 distances2 = float4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3));
|
|
|
|
fixed4 weights = float4(distances2 < unity_ShadowSplitSqRadii);
|
|
weights.yzw = saturate(weights.yzw - weights.xyz);
|
|
return weights;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// GetCascadeShadowCoord
|
|
//-----------------------------------------------------------------------------------------
|
|
inline float4 GetCascadeShadowCoord(float4 wpos, fixed4 cascadeWeights)
|
|
{
|
|
float3 sc0 = mul(unity_WorldToShadow[0], wpos).xyz;
|
|
float3 sc1 = mul(unity_WorldToShadow[1], wpos).xyz;
|
|
float3 sc2 = mul(unity_WorldToShadow[2], wpos).xyz;
|
|
float3 sc3 = mul(unity_WorldToShadow[3], wpos).xyz;
|
|
|
|
float4 shadowMapCoordinate = float4(sc0 * cascadeWeights[0] + sc1 * cascadeWeights[1] + sc2 * cascadeWeights[2] + sc3 * cascadeWeights[3], 1);
|
|
#if defined(UNITY_REVERSED_Z)
|
|
float noCascadeWeights = 1 - dot(cascadeWeights, float4(1, 1, 1, 1));
|
|
shadowMapCoordinate.z += noCascadeWeights;
|
|
#endif
|
|
return shadowMapCoordinate;
|
|
}
|
|
|
|
UNITY_DECLARE_SHADOWMAP(_CascadeShadowMapTexture);
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// GetLightAttenuation
|
|
//-----------------------------------------------------------------------------------------
|
|
float GetLightAttenuation(float3 wpos)
|
|
{
|
|
float atten = 0;
|
|
#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)
|
|
atten = 1;
|
|
#if defined (SHADOWS_DEPTH)
|
|
// sample cascade shadow map
|
|
float4 cascadeWeights = GetCascadeWeights_SplitSpheres(wpos);
|
|
bool inside = dot(cascadeWeights, float4(1, 1, 1, 1)) < 4;
|
|
float4 samplePos = GetCascadeShadowCoord(float4(wpos, 1), cascadeWeights);
|
|
|
|
atten = inside ? UNITY_SAMPLE_SHADOW(_CascadeShadowMapTexture, samplePos.xyz) : 1.0f;
|
|
atten = _LightShadowData.r + atten * (1 - _LightShadowData.r);
|
|
//atten = inside ? tex2Dproj(_ShadowMapTexture, float4((samplePos).xyz, 1)).r : 1.0f;
|
|
#endif
|
|
#if defined (DIRECTIONAL_COOKIE)
|
|
// NOT IMPLEMENTED
|
|
#endif
|
|
#elif defined (SPOT)
|
|
float3 tolight = _LightPos.xyz - wpos;
|
|
half3 lightDir = normalize(tolight);
|
|
|
|
float4 uvCookie = mul(_MyLightMatrix0, float4(wpos, 1));
|
|
// negative bias because http://aras-p.info/blog/2010/01/07/screenspace-vs-mip-mapping/
|
|
atten = tex2Dbias(_LightTexture0, float4(uvCookie.xy / uvCookie.w, 0, -8)).w;
|
|
atten *= uvCookie.w < 0;
|
|
float att = dot(tolight, tolight) * _LightPos.w;
|
|
atten *= tex2D(_LightTextureB0, att.rr).UNITY_ATTEN_CHANNEL;
|
|
|
|
#if defined(SHADOWS_DEPTH)
|
|
float4 shadowCoord = mul(_MyWorld2Shadow, float4(wpos, 1));
|
|
atten *= saturate(UnitySampleShadowmap(shadowCoord));
|
|
#endif
|
|
#elif defined (POINT) || defined (POINT_COOKIE)
|
|
float3 tolight = wpos - _LightPos.xyz;
|
|
half3 lightDir = -normalize(tolight);
|
|
|
|
float att = dot(tolight, tolight) * _LightPos.w;
|
|
atten = tex2D(_LightTextureB0, att.rr).UNITY_ATTEN_CHANNEL;
|
|
|
|
atten *= UnityDeferredComputeShadow(tolight, 0, float2(0, 0));
|
|
|
|
#if defined (POINT_COOKIE)
|
|
atten *= texCUBEbias(_LightTexture0, float4(mul(_MyLightMatrix0, half4(wpos, 1)).xyz, -8)).w;
|
|
#endif //POINT_COOKIE
|
|
#endif
|
|
return atten;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// ApplyHeightFog
|
|
//-----------------------------------------------------------------------------------------
|
|
void ApplyHeightFog(float3 wpos, inout float density)
|
|
{
|
|
#ifdef HEIGHT_FOG
|
|
density *= exp(-(wpos.y + _HeightFog.x) * _HeightFog.y);
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// GetDensity
|
|
//-----------------------------------------------------------------------------------------
|
|
float GetDensity(float3 wpos)
|
|
{
|
|
float density = 1;
|
|
#ifdef NOISE
|
|
float noise = tex3D(_NoiseTexture, frac(wpos * _NoiseData.x + float3(_Time.y * _NoiseVelocity.x, 0, _Time.y * _NoiseVelocity.y)));
|
|
noise = saturate(noise - _NoiseData.z) * _NoiseData.y;
|
|
density = saturate(noise);
|
|
#endif
|
|
ApplyHeightFog(wpos, density);
|
|
|
|
return density;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// MieScattering
|
|
//-----------------------------------------------------------------------------------------
|
|
float MieScattering(float cosAngle, float4 g)
|
|
{
|
|
return g.w * (g.x / (pow(g.y - g.z * cosAngle, 1.5)));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// RayMarch
|
|
//-----------------------------------------------------------------------------------------
|
|
float4 RayMarch(float2 screenPos, float3 rayStart, float3 rayDir, float rayLength)
|
|
{
|
|
#ifdef DITHER_4_4
|
|
float2 interleavedPos = (fmod(floor(screenPos.xy), 4.0));
|
|
float offset = tex2D(_DitherTexture, interleavedPos / 4.0 + float2(0.5 / 4.0, 0.5 / 4.0)).w;
|
|
#else
|
|
float2 interleavedPos = (fmod(floor(screenPos.xy), 8.0));
|
|
float offset = tex2D(_DitherTexture, interleavedPos / 8.0 + float2(0.5 / 8.0, 0.5 / 8.0)).w;
|
|
#endif
|
|
|
|
int stepCount = _SampleCount;
|
|
|
|
float stepSize = rayLength / stepCount;
|
|
float3 step = rayDir * stepSize;
|
|
|
|
float3 currentPosition = rayStart + step * offset;
|
|
|
|
float4 vlight = 0;
|
|
|
|
float cosAngle;
|
|
#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)
|
|
float extinction = 0;
|
|
cosAngle = dot(_LightDir.xyz, -rayDir);
|
|
#else
|
|
// we don't know about density between camera and light's volume, assume 0.5
|
|
float extinction = length(_WorldSpaceCameraPos - currentPosition) * _VolumetricLight.y * 0.5;
|
|
#endif
|
|
[loop]
|
|
for (int i = 0; i < stepCount; ++i)
|
|
{
|
|
float atten = GetLightAttenuation(currentPosition);
|
|
float density = GetDensity(currentPosition);
|
|
|
|
float scattering = _VolumetricLight.x * stepSize * density;
|
|
extinction += _VolumetricLight.y * stepSize * density;// +scattering;
|
|
|
|
float4 light = atten * scattering * exp(-extinction);
|
|
|
|
//#if PHASE_FUNCTOIN
|
|
#if !defined (DIRECTIONAL) && !defined (DIRECTIONAL_COOKIE)
|
|
// phase functino for spot and point lights
|
|
float3 tolight = normalize(currentPosition - _LightPos.xyz);
|
|
cosAngle = dot(tolight, -rayDir);
|
|
light *= MieScattering(cosAngle, _MieG);
|
|
#endif
|
|
//#endif
|
|
vlight += light;
|
|
|
|
currentPosition += step;
|
|
}
|
|
|
|
#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)
|
|
// apply phase function for dir light
|
|
vlight *= MieScattering(cosAngle, _MieG);
|
|
#endif
|
|
|
|
// apply light's color
|
|
vlight *= _LightColor;
|
|
|
|
vlight = max(0, vlight);
|
|
#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE) // use "proper" out-scattering/absorption for dir light
|
|
vlight.w = exp(-extinction);
|
|
#else
|
|
vlight.w = 0;
|
|
#endif
|
|
return vlight;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// RayConeIntersect
|
|
//-----------------------------------------------------------------------------------------
|
|
float2 RayConeIntersect(in float3 f3ConeApex, in float3 f3ConeAxis, in float fCosAngle, in float3 f3RayStart, in float3 f3RayDir)
|
|
{
|
|
float inf = 10000;
|
|
f3RayStart -= f3ConeApex;
|
|
float a = dot(f3RayDir, f3ConeAxis);
|
|
float b = dot(f3RayDir, f3RayDir);
|
|
float c = dot(f3RayStart, f3ConeAxis);
|
|
float d = dot(f3RayStart, f3RayDir);
|
|
float e = dot(f3RayStart, f3RayStart);
|
|
fCosAngle *= fCosAngle;
|
|
float A = a*a - b*fCosAngle;
|
|
float B = 2 * (c*a - d*fCosAngle);
|
|
float C = c*c - e*fCosAngle;
|
|
float D = B*B - 4 * A*C;
|
|
|
|
if (D > 0)
|
|
{
|
|
D = sqrt(D);
|
|
float2 t = (-B + sign(A)*float2(-D, +D)) / (2 * A);
|
|
bool2 b2IsCorrect = c + a * t > 0 && t > 0;
|
|
t = t * b2IsCorrect + !b2IsCorrect * (inf);
|
|
return t;
|
|
}
|
|
else // no intersection
|
|
return inf;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// RayPlaneIntersect
|
|
//-----------------------------------------------------------------------------------------
|
|
float RayPlaneIntersect(in float3 planeNormal, in float planeD, in float3 rayOrigin, in float3 rayDir)
|
|
{
|
|
float NdotD = dot(planeNormal, rayDir);
|
|
float NdotO = dot(planeNormal, rayOrigin);
|
|
|
|
float t = -(NdotO + planeD) / NdotD;
|
|
if (t < 0)
|
|
t = 100000;
|
|
return t;
|
|
}
|
|
|
|
ENDCG
|
|
|
|
// pass 0 - point light, camera inside
|
|
Pass
|
|
{
|
|
ZTest Off
|
|
Cull Front
|
|
ZWrite Off
|
|
Blend One One
|
|
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment fragPointInside
|
|
#pragma target 4.0
|
|
|
|
#define UNITY_HDR_ON
|
|
|
|
#pragma shader_feature HEIGHT_FOG
|
|
#pragma shader_feature NOISE
|
|
#pragma shader_feature SHADOWS_CUBE
|
|
#pragma shader_feature POINT_COOKIE
|
|
#pragma shader_feature POINT
|
|
|
|
#ifdef SHADOWS_DEPTH
|
|
#define SHADOWS_NATIVE
|
|
#endif
|
|
|
|
|
|
fixed4 fragPointInside(v2f i) : SV_Target
|
|
{
|
|
float2 uv = i.uv.xy / i.uv.w;
|
|
|
|
// read depth and reconstruct world position
|
|
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
|
|
|
|
float3 rayStart = _WorldSpaceCameraPos;
|
|
float3 rayEnd = i.wpos;
|
|
|
|
float3 rayDir = (rayEnd - rayStart);
|
|
float rayLength = length(rayDir);
|
|
|
|
rayDir /= rayLength;
|
|
|
|
float linearDepth = LinearEyeDepth(depth);
|
|
float projectedDepth = linearDepth / dot(_CameraForward, rayDir);
|
|
rayLength = min(rayLength, projectedDepth);
|
|
|
|
return RayMarch(i.pos.xy, rayStart, rayDir, rayLength);
|
|
}
|
|
ENDCG
|
|
}
|
|
|
|
// pass 1 - spot light, camera inside
|
|
Pass
|
|
{
|
|
ZTest Off
|
|
Cull Front
|
|
ZWrite Off
|
|
Blend One One
|
|
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment fragPointInside
|
|
#pragma target 4.0
|
|
|
|
#define UNITY_HDR_ON
|
|
|
|
#pragma shader_feature HEIGHT_FOG
|
|
#pragma shader_feature NOISE
|
|
#pragma shader_feature SHADOWS_DEPTH
|
|
#pragma shader_feature SPOT
|
|
|
|
#ifdef SHADOWS_DEPTH
|
|
#define SHADOWS_NATIVE
|
|
#endif
|
|
|
|
fixed4 fragPointInside(v2f i) : SV_Target
|
|
{
|
|
float2 uv = i.uv.xy / i.uv.w;
|
|
|
|
// read depth and reconstruct world position
|
|
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
|
|
|
|
float3 rayStart = _WorldSpaceCameraPos;
|
|
float3 rayEnd = i.wpos;
|
|
|
|
float3 rayDir = (rayEnd - rayStart);
|
|
float rayLength = length(rayDir);
|
|
|
|
rayDir /= rayLength;
|
|
|
|
float linearDepth = LinearEyeDepth(depth);
|
|
float projectedDepth = linearDepth / dot(_CameraForward, rayDir);
|
|
rayLength = min(rayLength, projectedDepth);
|
|
|
|
return RayMarch(i.pos.xy, rayStart, rayDir, rayLength);
|
|
}
|
|
ENDCG
|
|
}
|
|
|
|
// pass 2 - point light, camera outside
|
|
Pass
|
|
{
|
|
//ZTest Off
|
|
ZTest [_ZTest]
|
|
Cull Back
|
|
ZWrite Off
|
|
Blend One One
|
|
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment fragPointOutside
|
|
#pragma target 4.0
|
|
|
|
#define UNITY_HDR_ON
|
|
|
|
#pragma shader_feature HEIGHT_FOG
|
|
#pragma shader_feature SHADOWS_CUBE
|
|
#pragma shader_feature NOISE
|
|
//#pragma multi_compile POINT POINT_COOKIE
|
|
#pragma shader_feature POINT_COOKIE
|
|
#pragma shader_feature POINT
|
|
|
|
fixed4 fragPointOutside(v2f i) : SV_Target
|
|
{
|
|
float2 uv = i.uv.xy / i.uv.w;
|
|
|
|
// read depth and reconstruct world position
|
|
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
|
|
|
|
float3 rayStart = _WorldSpaceCameraPos;
|
|
float3 rayEnd = i.wpos;
|
|
|
|
float3 rayDir = (rayEnd - rayStart);
|
|
float rayLength = length(rayDir);
|
|
|
|
rayDir /= rayLength;
|
|
|
|
float3 lightToCamera = _WorldSpaceCameraPos - _LightPos;
|
|
|
|
float b = dot(rayDir, lightToCamera);
|
|
float c = dot(lightToCamera, lightToCamera) - (_VolumetricLight.z * _VolumetricLight.z);
|
|
|
|
float d = sqrt((b*b) - c);
|
|
float start = -b - d;
|
|
float end = -b + d;
|
|
|
|
float linearDepth = LinearEyeDepth(depth);
|
|
float projectedDepth = linearDepth / dot(_CameraForward, rayDir);
|
|
end = min(end, projectedDepth);
|
|
|
|
rayStart = rayStart + rayDir * start;
|
|
rayLength = end - start;
|
|
|
|
return RayMarch(i.pos.xy, rayStart, rayDir, rayLength);
|
|
}
|
|
ENDCG
|
|
}
|
|
|
|
// pass 3 - spot light, camera outside
|
|
Pass
|
|
{
|
|
//ZTest Off
|
|
ZTest[_ZTest]
|
|
Cull Back
|
|
ZWrite Off
|
|
Blend One One
|
|
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment fragSpotOutside
|
|
#pragma target 4.0
|
|
|
|
#define UNITY_HDR_ON
|
|
|
|
#pragma shader_feature HEIGHT_FOG
|
|
#pragma shader_feature SHADOWS_DEPTH
|
|
#pragma shader_feature NOISE
|
|
#pragma shader_feature SPOT
|
|
|
|
#ifdef SHADOWS_DEPTH
|
|
#define SHADOWS_NATIVE
|
|
#endif
|
|
|
|
float _CosAngle;
|
|
float4 _ConeAxis;
|
|
float4 _ConeApex;
|
|
float _PlaneD;
|
|
|
|
fixed4 fragSpotOutside(v2f i) : SV_Target
|
|
{
|
|
float2 uv = i.uv.xy / i.uv.w;
|
|
|
|
// read depth and reconstruct world position
|
|
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
|
|
|
|
float3 rayStart = _WorldSpaceCameraPos;
|
|
float3 rayEnd = i.wpos;
|
|
|
|
float3 rayDir = (rayEnd - rayStart);
|
|
float rayLength = length(rayDir);
|
|
|
|
rayDir /= rayLength;
|
|
|
|
|
|
// inside cone
|
|
float3 r1 = rayEnd + rayDir * 0.001;
|
|
|
|
// plane intersection
|
|
float planeCoord = RayPlaneIntersect(_ConeAxis, _PlaneD, r1, rayDir);
|
|
// ray cone intersection
|
|
float2 lineCoords = RayConeIntersect(_ConeApex, _ConeAxis, _CosAngle, r1, rayDir);
|
|
|
|
float linearDepth = LinearEyeDepth(depth);
|
|
float projectedDepth = linearDepth / dot(_CameraForward, rayDir);
|
|
|
|
float z = (projectedDepth - rayLength);
|
|
rayLength = min(planeCoord, min(lineCoords.x, lineCoords.y));
|
|
rayLength = min(rayLength, z);
|
|
|
|
return RayMarch(i.pos.xy, rayEnd, rayDir, rayLength);
|
|
}
|
|
ENDCG
|
|
}
|
|
|
|
// pass 4 - directional light
|
|
Pass
|
|
{
|
|
ZTest Off
|
|
Cull Off
|
|
ZWrite Off
|
|
Blend One One, One Zero
|
|
|
|
CGPROGRAM
|
|
|
|
#pragma vertex vertDir
|
|
#pragma fragment fragDir
|
|
#pragma target 4.0
|
|
|
|
#define UNITY_HDR_ON
|
|
|
|
#pragma shader_feature HEIGHT_FOG
|
|
#pragma shader_feature NOISE
|
|
#pragma shader_feature SHADOWS_DEPTH
|
|
#pragma shader_feature DIRECTIONAL_COOKIE
|
|
#pragma shader_feature DIRECTIONAL
|
|
|
|
#ifdef SHADOWS_DEPTH
|
|
#define SHADOWS_NATIVE
|
|
#endif
|
|
|
|
struct VSInput
|
|
{
|
|
float4 vertex : POSITION;
|
|
float2 uv : TEXCOORD0;
|
|
uint vertexId : SV_VertexID;
|
|
};
|
|
|
|
struct PSInput
|
|
{
|
|
float4 pos : SV_POSITION;
|
|
float2 uv : TEXCOORD0;
|
|
float3 wpos : TEXCOORD1;
|
|
};
|
|
|
|
PSInput vertDir(VSInput i)
|
|
{
|
|
PSInput o;
|
|
|
|
o.pos = UnityObjectToClipPos(i.vertex);
|
|
o.uv = i.uv;
|
|
|
|
// SV_VertexId doesn't work on OpenGL for some reason -> reconstruct id from uv
|
|
//o.wpos = _FrustumCorners[i.vertexId];
|
|
o.wpos = _FrustumCorners[i.uv.x + i.uv.y*2];
|
|
|
|
return o;
|
|
}
|
|
|
|
fixed4 fragDir(PSInput i) : SV_Target
|
|
{
|
|
float2 uv = i.uv.xy;
|
|
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
|
|
float linearDepth = Linear01Depth(depth);
|
|
|
|
float3 wpos = i.wpos;
|
|
float3 rayStart = _WorldSpaceCameraPos;
|
|
float3 rayDir = wpos - _WorldSpaceCameraPos;
|
|
rayDir *= linearDepth;
|
|
|
|
float rayLength = length(rayDir);
|
|
rayDir /= rayLength;
|
|
|
|
rayLength = min(rayLength, _MaxRayLength);
|
|
|
|
float4 color = RayMarch(i.pos.xy, rayStart, rayDir, rayLength);
|
|
|
|
if (linearDepth > 0.999999)
|
|
{
|
|
color.w = lerp(color.w, 1, _VolumetricLight.w);
|
|
}
|
|
return color;
|
|
}
|
|
ENDCG
|
|
}
|
|
}
|
|
}
|