#include "UnityCG.cginc" #include "UnityDeferredLibrary.cginc" half4 _MainTex_ST; half4 _CameraDepthTexture_ST; sampler3D _NoiseTexture; sampler2D _DitherTexture; float4x4 _WorldViewProj; float4x4 _WorldViewProj_SP; float4x4 _MyLightMatrix0; float4x4 _MyWorld2Shadow; float4x4 _LeftWorldFromView; float4x4 _RightWorldFromView; float4x4 _LeftViewFromScreen; float4x4 _RightViewFromScreen; 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; float _MaxRayLength; int _SampleCount; // x: scale, y: intensity, z: intensity offset float4 _NoiseData; // x: x velocity, y: z velocity float4 _NoiseVelocity; /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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); 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); #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; } float GetLightAttenuationDir(float3 wpos) { float atten = 1; // 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); return atten; } void ApplyHeightFog(float3 wpos, inout float density) { //#ifdef HEIGHT_FOG density *= exp(-(wpos.y + 1) * 0.1); //#endif } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// float GetDensityFog(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; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// float MieScattering(float cosAngle, float4 g) { return g.w * (g.x / (pow(g.y - g.z * cosAngle, 1.5))); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// float4 RayMarch(float2 screenPos, float3 rayStart, float3 rayDir, float rayLength) { #ifdef DITHER_4_4 float2 interleavedPos = (fmod(floor(screenPos.xy), 4.0)); #if UNITY_SINGLE_PASS_STEREO float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex]; interleavedPos = (interleavedPos - scaleOffset.zw) / scaleOffset.xy; #endif 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)); #if UNITY_SINGLE_PASS_STEREO float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex]; interleavedPos = (interleavedPos - scaleOffset.zw) / scaleOffset.xy; #endif 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 density = 0; float atten = GetLightAttenuation(currentPosition); density = GetDensityFog(currentPosition); float scattering = _VolumetricLight.x * stepSize * density; extinction += _VolumetricLight.y * stepSize * density;// +scattering; float4 light = atten * scattering * exp(-extinction); #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 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; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* float4 RayMarchDir(float2 screenPos, float3 rayStart, float3 rayDir, float rayLength) { #ifdef DITHER_4_4 float2 interleavedPos = (fmod(floor(screenPos.xy), 4.0)); #if UNITY_SINGLE_PASS_STEREO float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex]; interleavedPos = (interleavedPos - scaleOffset.zw) / scaleOffset.xy; #endif 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)); #if UNITY_SINGLE_PASS_STEREO float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex]; interleavedPos = (interleavedPos - scaleOffset.zw) / scaleOffset.xy; #endif 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; float extinction = 0; cosAngle = dot(_LightDir.xyz, -rayDir); [loop] for (int i = 0; i < stepCount; ++i) { float atten = GetLightAttenuationDir(currentPosition); float density = GetDensityFog(currentPosition); float scattering = _VolumetricLight.x * stepSize * density; extinction += _VolumetricLight.y * stepSize * density;// +scattering; float4 light = atten * scattering * exp(-extinction); vlight += light; currentPosition += step; } vlight *= MieScattering(cosAngle, _MieG); // apply light's color vlight *= _LightColor; vlight = max(0, vlight); vlight.w = exp(-extinction); return vlight; } */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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; }