422 lines
14 KiB
Plaintext
422 lines
14 KiB
Plaintext
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
|
|
|
|
// Collects cascaded shadows into screen space buffer
|
|
Shader "Hidden/Internal-ScreenSpaceShadows" {
|
|
Properties {
|
|
_ShadowMapTexture ("", any) = "" {}
|
|
_ODSWorldTexture("", 2D) = "" {}
|
|
}
|
|
|
|
CGINCLUDE
|
|
|
|
UNITY_DECLARE_SHADOWMAP(_ShadowMapTexture);
|
|
float4 _ShadowMapTexture_TexelSize;
|
|
#define SHADOWMAPSAMPLER_AND_TEXELSIZE_DEFINED
|
|
sampler2D _ODSWorldTexture;
|
|
|
|
#include "UnityCG.cginc"
|
|
#include "UnityShadowLibrary.cginc"
|
|
|
|
// Configuration
|
|
|
|
|
|
// Should receiver plane bias be used? This estimates receiver slope using derivatives,
|
|
// and tries to tilt the PCF kernel along it. However, since we're doing it in screenspace
|
|
// from the depth texture, the derivatives are wrong on edges or intersections of objects,
|
|
// leading to possible shadow artifacts. So it's disabled by default.
|
|
// See also UnityGetReceiverPlaneDepthBias in UnityShadowLibrary.cginc.
|
|
//#define UNITY_USE_RECEIVER_PLANE_BIAS
|
|
|
|
|
|
// Blend between shadow cascades to hide the transition seams?
|
|
#define UNITY_USE_CASCADE_BLENDING 0
|
|
#define UNITY_CASCADE_BLEND_DISTANCE 0.1
|
|
|
|
|
|
struct appdata {
|
|
float4 vertex : POSITION;
|
|
float2 texcoord : TEXCOORD0;
|
|
#if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)
|
|
float3 ray0 : TEXCOORD1;
|
|
float3 ray1 : TEXCOORD2;
|
|
#else
|
|
float3 ray : TEXCOORD1;
|
|
#endif
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
};
|
|
|
|
struct v2f {
|
|
|
|
float4 pos : SV_POSITION;
|
|
|
|
// xy uv / zw screenpos
|
|
float4 uv : TEXCOORD0;
|
|
// View space ray, for perspective case
|
|
float3 ray : TEXCOORD1;
|
|
// Orthographic view space positions (need xy as well for oblique matrices)
|
|
float3 orthoPosNear : TEXCOORD2;
|
|
float3 orthoPosFar : TEXCOORD3;
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
v2f vert (appdata v)
|
|
{
|
|
v2f o;
|
|
UNITY_SETUP_INSTANCE_ID(v);
|
|
UNITY_TRANSFER_INSTANCE_ID(v, o);
|
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
|
float4 clipPos;
|
|
#if defined(STEREO_CUBEMAP_RENDER_ON)
|
|
clipPos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, v.vertex));
|
|
#else
|
|
clipPos = UnityObjectToClipPos(v.vertex);
|
|
#endif
|
|
o.pos = clipPos;
|
|
o.uv.xy = v.texcoord;
|
|
|
|
// unity_CameraInvProjection at the PS level.
|
|
o.uv.zw = ComputeNonStereoScreenPos(clipPos);
|
|
|
|
// Perspective case
|
|
#if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)
|
|
o.ray = unity_StereoEyeIndex == 0 ? v.ray0 : v.ray1;
|
|
#else
|
|
o.ray = v.ray;
|
|
#endif
|
|
|
|
// To compute view space position from Z buffer for orthographic case,
|
|
// we need different code than for perspective case. We want to avoid
|
|
// doing matrix multiply in the pixel shader: less operations, and less
|
|
// constant registers used. Particularly with constant registers, having
|
|
// unity_CameraInvProjection in the pixel shader would push the PS over SM2.0
|
|
// limits.
|
|
clipPos.y *= _ProjectionParams.x;
|
|
float3 orthoPosNear = mul(unity_CameraInvProjection, float4(clipPos.x,clipPos.y,-1,1)).xyz;
|
|
float3 orthoPosFar = mul(unity_CameraInvProjection, float4(clipPos.x,clipPos.y, 1,1)).xyz;
|
|
orthoPosNear.z *= -1;
|
|
orthoPosFar.z *= -1;
|
|
o.orthoPosNear = orthoPosNear;
|
|
o.orthoPosFar = orthoPosFar;
|
|
|
|
return o;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Helpers
|
|
// ------------------------------------------------------------------
|
|
UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
|
|
// sizes of cascade projections, relative to first one
|
|
float4 unity_ShadowCascadeScales;
|
|
|
|
//
|
|
// Keywords based defines
|
|
//
|
|
#if defined (SHADOWS_SPLIT_SPHERES)
|
|
#define GET_CASCADE_WEIGHTS(wpos, z) getCascadeWeights_splitSpheres(wpos)
|
|
#else
|
|
#define GET_CASCADE_WEIGHTS(wpos, z) getCascadeWeights( wpos, z )
|
|
#endif
|
|
|
|
#if defined (SHADOWS_SINGLE_CASCADE)
|
|
#define GET_SHADOW_COORDINATES(wpos,cascadeWeights) getShadowCoord_SingleCascade(wpos)
|
|
#else
|
|
#define GET_SHADOW_COORDINATES(wpos,cascadeWeights) getShadowCoord(wpos,cascadeWeights)
|
|
#endif
|
|
|
|
/**
|
|
* Gets the cascade weights based on the world position of the fragment.
|
|
* Returns a float4 with only one component set that corresponds to the appropriate cascade.
|
|
*/
|
|
inline fixed4 getCascadeWeights(float3 wpos, float z)
|
|
{
|
|
fixed4 zNear = float4( z >= _LightSplitsNear );
|
|
fixed4 zFar = float4( z < _LightSplitsFar );
|
|
fixed4 weights = zNear * zFar;
|
|
return weights;
|
|
}
|
|
|
|
/**
|
|
* Gets the cascade weights based on the world position of the fragment and the poisitions of the split spheres for each cascade.
|
|
* Returns a float4 with only one component set that corresponds to the appropriate cascade.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Returns the shadowmap coordinates for the given fragment based on the world position and z-depth.
|
|
* These coordinates belong to the shadowmap atlas that contains the maps for all cascades.
|
|
*/
|
|
inline float4 getShadowCoord( 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;
|
|
}
|
|
|
|
/**
|
|
* Same as the getShadowCoord; but optimized for single cascade
|
|
*/
|
|
inline float4 getShadowCoord_SingleCascade( float4 wpos )
|
|
{
|
|
return float4( mul (unity_WorldToShadow[0], wpos).xyz, 0);
|
|
}
|
|
|
|
/**
|
|
* Get camera space coord from depth and inv projection matrices
|
|
*/
|
|
inline float3 computeCameraSpacePosFromDepthAndInvProjMat(v2f i)
|
|
{
|
|
float zdepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv.xy);
|
|
|
|
#if defined(UNITY_REVERSED_Z)
|
|
zdepth = 1 - zdepth;
|
|
#endif
|
|
|
|
// View position calculation for oblique clipped projection case.
|
|
// this will not be as precise nor as fast as the other method
|
|
// (which computes it from interpolated ray & depth) but will work
|
|
// with funky projections.
|
|
float4 clipPos = float4(i.uv.zw, zdepth, 1.0);
|
|
clipPos.xyz = 2.0f * clipPos.xyz - 1.0f;
|
|
float4 camPos = mul(unity_CameraInvProjection, clipPos);
|
|
camPos.xyz /= camPos.w;
|
|
camPos.z *= -1;
|
|
return camPos.xyz;
|
|
}
|
|
|
|
/**
|
|
* Get camera space coord from depth and info from VS
|
|
*/
|
|
inline float3 computeCameraSpacePosFromDepthAndVSInfo(v2f i)
|
|
{
|
|
float zdepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv.xy);
|
|
|
|
// 0..1 linear depth, 0 at camera, 1 at far plane.
|
|
float depth = lerp(Linear01Depth(zdepth), zdepth, unity_OrthoParams.w);
|
|
#if defined(UNITY_REVERSED_Z)
|
|
zdepth = 1 - zdepth;
|
|
#endif
|
|
|
|
// view position calculation for perspective & ortho cases
|
|
float3 vposPersp = i.ray * depth;
|
|
float3 vposOrtho = lerp(i.orthoPosNear, i.orthoPosFar, zdepth);
|
|
// pick the perspective or ortho position as needed
|
|
float3 camPos = lerp(vposPersp, vposOrtho, unity_OrthoParams.w);
|
|
return camPos.xyz;
|
|
}
|
|
|
|
inline float3 computeCameraSpacePosFromDepth(v2f i);
|
|
|
|
/**
|
|
* Hard shadow
|
|
*/
|
|
fixed4 frag_hard (v2f i) : SV_Target
|
|
{
|
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); // required for sampling the correct slice of the shadow map render texture array
|
|
float4 wpos;
|
|
float3 vpos;
|
|
|
|
#if defined(STEREO_CUBEMAP_RENDER_ON)
|
|
wpos.xyz = tex2D(_ODSWorldTexture, i.uv.xy).xyz;
|
|
wpos.w = 1.0f;
|
|
vpos = mul(unity_WorldToCamera, wpos).xyz;
|
|
#else
|
|
vpos = computeCameraSpacePosFromDepth(i);
|
|
wpos = mul (unity_CameraToWorld, float4(vpos,1));
|
|
#endif
|
|
fixed4 cascadeWeights = GET_CASCADE_WEIGHTS (wpos, vpos.z);
|
|
float4 shadowCoord = GET_SHADOW_COORDINATES(wpos, cascadeWeights);
|
|
|
|
//1 tap hard shadow
|
|
fixed shadow = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, shadowCoord);
|
|
shadow = lerp(_LightShadowData.r, 1.0, shadow);
|
|
|
|
fixed4 res = shadow;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Soft Shadow (SM 3.0)
|
|
*/
|
|
fixed4 frag_pcfSoft(v2f i) : SV_Target
|
|
{
|
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); // required for sampling the correct slice of the shadow map render texture array
|
|
float4 wpos;
|
|
float3 vpos;
|
|
|
|
#if defined(STEREO_CUBEMAP_RENDER_ON)
|
|
wpos.xyz = tex2D(_ODSWorldTexture, i.uv.xy).xyz;
|
|
wpos.w = 1.0f;
|
|
vpos = mul(unity_WorldToCamera, wpos).xyz;
|
|
#else
|
|
vpos = computeCameraSpacePosFromDepth(i);
|
|
|
|
// sample the cascade the pixel belongs to
|
|
wpos = mul(unity_CameraToWorld, float4(vpos,1));
|
|
#endif
|
|
fixed4 cascadeWeights = GET_CASCADE_WEIGHTS(wpos, vpos.z);
|
|
float4 coord = GET_SHADOW_COORDINATES(wpos, cascadeWeights);
|
|
|
|
float3 receiverPlaneDepthBias = 0.0;
|
|
#ifdef UNITY_USE_RECEIVER_PLANE_BIAS
|
|
// Reveiver plane depth bias: need to calculate it based on shadow coordinate
|
|
// as it would be in first cascade; otherwise derivatives
|
|
// at cascade boundaries will be all wrong. So compute
|
|
// it from cascade 0 UV, and scale based on which cascade we're in.
|
|
float3 coordCascade0 = getShadowCoord_SingleCascade(wpos);
|
|
float biasMultiply = dot(cascadeWeights,unity_ShadowCascadeScales);
|
|
receiverPlaneDepthBias = UnityGetReceiverPlaneDepthBias(coordCascade0.xyz, biasMultiply);
|
|
#endif
|
|
|
|
#if defined(SHADER_API_MOBILE)
|
|
half shadow = UnitySampleShadowmap_PCF5x5(coord, receiverPlaneDepthBias);
|
|
#else
|
|
half shadow = UnitySampleShadowmap_PCF7x7(coord, receiverPlaneDepthBias);
|
|
#endif
|
|
shadow = lerp(_LightShadowData.r, 1.0f, shadow);
|
|
|
|
// Blend between shadow cascades if enabled
|
|
//
|
|
// Not working yet with split spheres, and no need when 1 cascade
|
|
#if UNITY_USE_CASCADE_BLENDING && !defined(SHADOWS_SPLIT_SPHERES) && !defined(SHADOWS_SINGLE_CASCADE)
|
|
half4 z4 = (float4(vpos.z,vpos.z,vpos.z,vpos.z) - _LightSplitsNear) / (_LightSplitsFar - _LightSplitsNear);
|
|
half alpha = dot(z4 * cascadeWeights, half4(1,1,1,1));
|
|
|
|
UNITY_BRANCH
|
|
if (alpha > 1 - UNITY_CASCADE_BLEND_DISTANCE)
|
|
{
|
|
// get alpha to 0..1 range over the blend distance
|
|
alpha = (alpha - (1 - UNITY_CASCADE_BLEND_DISTANCE)) / UNITY_CASCADE_BLEND_DISTANCE;
|
|
|
|
// sample next cascade
|
|
cascadeWeights = fixed4(0, cascadeWeights.xyz);
|
|
coord = GET_SHADOW_COORDINATES(wpos, cascadeWeights);
|
|
|
|
#ifdef UNITY_USE_RECEIVER_PLANE_BIAS
|
|
biasMultiply = dot(cascadeWeights,unity_ShadowCascadeScales);
|
|
receiverPlaneDepthBias = UnityGetReceiverPlaneDepthBias(coordCascade0.xyz, biasMultiply);
|
|
#endif
|
|
|
|
half shadowNextCascade = UnitySampleShadowmap_PCF3x3(coord, receiverPlaneDepthBias);
|
|
shadowNextCascade = lerp(_LightShadowData.r, 1.0f, shadowNextCascade);
|
|
shadow = lerp(shadow, shadowNextCascade, alpha);
|
|
}
|
|
#endif
|
|
|
|
return shadow;
|
|
}
|
|
ENDCG
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// Subshader for hard shadows:
|
|
// Just collect shadows into the buffer. Used on pre-SM3 GPUs and when hard shadows are picked.
|
|
|
|
SubShader {
|
|
Tags{ "ShadowmapFilter" = "HardShadow" }
|
|
Pass {
|
|
ZWrite Off ZTest Always Cull Off
|
|
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment frag_hard
|
|
#pragma multi_compile_shadowcollector
|
|
|
|
inline float3 computeCameraSpacePosFromDepth(v2f i)
|
|
{
|
|
return computeCameraSpacePosFromDepthAndVSInfo(i);
|
|
}
|
|
ENDCG
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// Subshader for hard shadows:
|
|
// Just collect shadows into the buffer. Used on pre-SM3 GPUs and when hard shadows are picked.
|
|
// This version does inv projection at the PS level, slower and less precise however more general.
|
|
|
|
SubShader {
|
|
Tags{ "ShadowmapFilter" = "HardShadow_FORCE_INV_PROJECTION_IN_PS" }
|
|
Pass{
|
|
ZWrite Off ZTest Always Cull Off
|
|
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment frag_hard
|
|
#pragma multi_compile_shadowcollector
|
|
|
|
inline float3 computeCameraSpacePosFromDepth(v2f i)
|
|
{
|
|
return computeCameraSpacePosFromDepthAndInvProjMat(i);
|
|
}
|
|
ENDCG
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// Subshader that does soft PCF filtering while collecting shadows.
|
|
// Requires SM3 GPU.
|
|
|
|
Subshader {
|
|
Tags {"ShadowmapFilter" = "PCF_SOFT"}
|
|
Pass {
|
|
ZWrite Off ZTest Always Cull Off
|
|
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment frag_pcfSoft
|
|
#pragma multi_compile_shadowcollector
|
|
#pragma target 3.0
|
|
|
|
inline float3 computeCameraSpacePosFromDepth(v2f i)
|
|
{
|
|
return computeCameraSpacePosFromDepthAndVSInfo(i);
|
|
}
|
|
ENDCG
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// Subshader that does soft PCF filtering while collecting shadows.
|
|
// Requires SM3 GPU.
|
|
// This version does inv projection at the PS level, slower and less precise however more general.
|
|
|
|
Subshader{
|
|
Tags{ "ShadowmapFilter" = "PCF_SOFT_FORCE_INV_PROJECTION_IN_PS" }
|
|
Pass{
|
|
ZWrite Off ZTest Always Cull Off
|
|
|
|
CGPROGRAM
|
|
#pragma vertex vert
|
|
#pragma fragment frag_pcfSoft
|
|
#pragma multi_compile_shadowcollector
|
|
#pragma target 3.0
|
|
|
|
inline float3 computeCameraSpacePosFromDepth(v2f i)
|
|
{
|
|
return computeCameraSpacePosFromDepthAndInvProjMat(i);
|
|
}
|
|
ENDCG
|
|
}
|
|
}
|
|
|
|
Fallback Off
|
|
}
|