Unity实时阴影实现——Shadow Mapping
Unity的实时阴影-ShadowMap实现原理
Unity实时阴影实现——Cascaded Shadow Mapping
用GLSL实现CMS(Cascaded Shadow Mapping)说了好久了,今天抽空搞一下,代码是参考网上大神的,不过翻译成GLSL还是遇到了一些坑,先看看效果吧:
锯齿还是有点严重,提高shadowmap的分辨率好像没什么效果,软阴影可能好点吧,不过后面有时间再搞吧。
1.生成深度图
Shader "GLSL/ShadowMapping/Caster"
{
SubShader {
Tags {
"RenderType" = "Opaque"
}
Pass {
Fog { Mode Off }
Cull front//设置Cull front可解决面向光源的acne问题
GLSLPROGRAM
//gl_Vertex 顶点
//gl_Position 裁剪空间坐标输出到片元着色器
//gl_FragColor 输出颜色
#include "UnityCG.glslinc"
#include "lib/Custom.glslinc"
uniform float _gShadowBias;
struct v2f {
vec4 pos;//其实没用到,为了展示如何使用glsl结构体
vec2 depth;
};
#ifdef VERTEX
out v2f v;
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_Position.z += _gShadowBias;
v.depth = gl_Position.zw;
}
#endif
#ifdef FRAGMENT
in v2f v;
void main()
{
float depth = v.depth.x / v.depth.y;
#if defined (UNITY_REVERSED_Z)
depth = 1 - depth; //(1, 0)-->(0, 1)
#else
depth = depth*0.5 + 0.5; //(-1, 1)-->(0, 1)
#endif
gl_FragColor = EncodeFloatRGBA(depth);
}
#endif
ENDGLSL
}
}
}
2.生成级联相关数据
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CascadedShadowMapping : MonoBehaviour
{
public Light dirLight;
Camera dirLightCamera;//灯光空间的相机
public int shadowResolution = 1;
public Shader shadowCaster = null;
private Matrix4x4 biasMatrix = Matrix4x4.identity;
List world2ShadowMats = new List(4);
GameObject[] dirLightCameraSplits = new GameObject[4];
RenderTexture[] depthTextures = new RenderTexture[4];//四级阴影纹理
void OnDestroy()
{
dirLightCamera = null;
for (int i = 0; i < 4; i++)
{
if (depthTextures[i])
{
DestroyImmediate(depthTextures[i]);
}
}
}
void Awake()
{
biasMatrix.SetRow(0, new Vector4(0.5f, 0, 0, 0.5f));
biasMatrix.SetRow(1, new Vector4(0, 0.5f, 0, 0.5f));
biasMatrix.SetRow(2, new Vector4(0, 0, 0.5f, 0.5f));
biasMatrix.SetRow(3, new Vector4(0, 0, 0, 1f));
InitFrustumCorners();
}
//初始化rt,4级阴影纹理对应4张rt
private void CreateRenderTexture()
{
RenderTextureFormat rtFormat = RenderTextureFormat.Default;
if (!SystemInfo.SupportsRenderTextureFormat(rtFormat))
rtFormat = RenderTextureFormat.Default;
for (int i = 0; i < 4; i++)
{
depthTextures[i] = new RenderTexture(1024, 1024, 24, rtFormat);
Shader.SetGlobalTexture("_gShadowMapTexture" + i, depthTextures[i]);
}
}
//创建灯光摄像机
public Camera CreateDirLightCamera()
{
GameObject goLightCamera = new GameObject("Directional Light Camera");
Camera LightCamera = goLightCamera.AddComponent();
LightCamera.cullingMask = 1 << LayerMask.NameToLayer("Caster");
LightCamera.backgroundColor = Color.white;
LightCamera.clearFlags = CameraClearFlags.SolidColor;
LightCamera.orthographic = true;
LightCamera.enabled = false;
for (int i = 0; i < 4; i++)
{
dirLightCameraSplits[i] = new GameObject("dirLightCameraSplits" + i);
}
return LightCamera;
}
private void Update()
{
CalcMainCameraSplitsFrustumCorners();
CalcLightCameraSplitsFrustum();
if (dirLight)
{
if (!dirLightCamera)
{
dirLightCamera = CreateDirLightCamera();
CreateRenderTexture();
}
Shader.SetGlobalFloat("_gShadowBias", 0.005f);
Shader.SetGlobalFloat("_gShadowStrength", 0.5f);
world2ShadowMats.Clear();
//构建4级阴影纹理
for (int i = 0; i < 4; i++)
{
ConstructLightCameraSplits(i);
dirLightCamera.targetTexture = depthTextures[i];
dirLightCamera.RenderWithShader(shadowCaster, "");
Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(dirLightCamera.projectionMatrix, false);
world2ShadowMats.Add(projectionMatrix * dirLightCamera.worldToCameraMatrix);
}
Shader.SetGlobalMatrixArray("_gWorld2Shadow", world2ShadowMats);
}
}
float[] _LightSplitsNear;
float[] _LightSplitsFar;
struct FrustumCorners
{
public Vector3[] nearCorners;
public Vector3[] farCorners;
}
FrustumCorners[] mainCamera_Splits_fcs;
FrustumCorners[] lightCamera_Splits_fcs;
//初始化4级远近裁剪面坐标
void InitFrustumCorners()
{
mainCamera_Splits_fcs = new FrustumCorners[4];
lightCamera_Splits_fcs = new FrustumCorners[4];
for (int i = 0; i < 4; i++)
{
mainCamera_Splits_fcs[i].nearCorners = new Vector3[4];
mainCamera_Splits_fcs[i].farCorners = new Vector3[4];
lightCamera_Splits_fcs[i].nearCorners = new Vector3[4];
lightCamera_Splits_fcs[i].farCorners = new Vector3[4];
}
}
void CalcMainCameraSplitsFrustumCorners()
{
float near = Camera.main.nearClipPlane;
float far = Camera.main.farClipPlane;
//分4级:x + x*2 + x*2*2 + x*2*2*2 = 100% ==>15*x = 100% ==> x = 0.066666666≈0.067 = 6.7%
//得到6.7%、13.3%、26.7%、53.3%等分的4级远近裁剪坐标
float[] nears = { near, far * 0.067f + near, far * 0.133f + far * 0.067f + near, far * 0.267f + far * 0.133f + far * 0.067f + near };
float[] fars = { far * 0.067f + near, far * 0.133f + far * 0.067f + near, far * 0.267f + far * 0.133f + far * 0.067f + near, far };
_LightSplitsNear = nears;
_LightSplitsFar = fars;
Shader.SetGlobalVector("_gLightSplitsNear", new Vector4(_LightSplitsNear[0], _LightSplitsNear[1], _LightSplitsNear[2], _LightSplitsNear[3]));
Shader.SetGlobalVector("_gLightSplitsFar", new Vector4(_LightSplitsFar[0], _LightSplitsFar[1], _LightSplitsFar[2], _LightSplitsFar[3]));
//计算主摄像机的4级视锥体
for (int k = 0; k < 4; k++)
{
Camera.main.CalculateFrustumCorners(new Rect(0, 0, 1, 1), _LightSplitsNear[k], Camera.MonoOrStereoscopicEye.Mono, mainCamera_Splits_fcs[k].nearCorners);
for (int i = 0; i < 4; i++)
{
mainCamera_Splits_fcs[k].nearCorners[i] = Camera.main.transform.TransformPoint(mainCamera_Splits_fcs[k].nearCorners[i]);
}
Camera.main.CalculateFrustumCorners(new Rect(0, 0, 1, 1), _LightSplitsFar[k], Camera.MonoOrStereoscopicEye.Mono, mainCamera_Splits_fcs[k].farCorners);
for (int i = 0; i < 4; i++)
{
mainCamera_Splits_fcs[k].farCorners[i] = Camera.main.transform.TransformPoint(mainCamera_Splits_fcs[k].farCorners[i]);
}
}
}
//计算灯光相机包围盒
void CalcLightCameraSplitsFrustum()
{
if (dirLightCamera == null)
return;
for (int k = 0; k < 4; k++)
{
for (int i = 0; i < 4; i++)
{
lightCamera_Splits_fcs[k].nearCorners[i] = dirLightCameraSplits[k].transform.InverseTransformPoint(mainCamera_Splits_fcs[k].nearCorners[i]);
lightCamera_Splits_fcs[k].farCorners[i] = dirLightCameraSplits[k].transform.InverseTransformPoint(mainCamera_Splits_fcs[k].farCorners[i]);
}
float[] xs = { lightCamera_Splits_fcs[k].nearCorners[0].x, lightCamera_Splits_fcs[k].nearCorners[1].x, lightCamera_Splits_fcs[k].nearCorners[2].x, lightCamera_Splits_fcs[k].nearCorners[3].x,
lightCamera_Splits_fcs[k].farCorners[0].x, lightCamera_Splits_fcs[k].farCorners[1].x, lightCamera_Splits_fcs[k].farCorners[2].x, lightCamera_Splits_fcs[k].farCorners[3].x };
float[] ys = { lightCamera_Splits_fcs[k].nearCorners[0].y, lightCamera_Splits_fcs[k].nearCorners[1].y, lightCamera_Splits_fcs[k].nearCorners[2].y, lightCamera_Splits_fcs[k].nearCorners[3].y,
lightCamera_Splits_fcs[k].farCorners[0].y, lightCamera_Splits_fcs[k].farCorners[1].y, lightCamera_Splits_fcs[k].farCorners[2].y, lightCamera_Splits_fcs[k].farCorners[3].y };
float[] zs = { lightCamera_Splits_fcs[k].nearCorners[0].z, lightCamera_Splits_fcs[k].nearCorners[1].z, lightCamera_Splits_fcs[k].nearCorners[2].z, lightCamera_Splits_fcs[k].nearCorners[3].z,
lightCamera_Splits_fcs[k].farCorners[0].z, lightCamera_Splits_fcs[k].farCorners[1].z, lightCamera_Splits_fcs[k].farCorners[2].z, lightCamera_Splits_fcs[k].farCorners[3].z };
float minX = Mathf.Min(xs);
float maxX = Mathf.Max(xs);
float minY = Mathf.Min(ys);
float maxY = Mathf.Max(ys);
float minZ = Mathf.Min(zs);
float maxZ = Mathf.Max(zs);
lightCamera_Splits_fcs[k].nearCorners[0] = new Vector3(minX, minY, minZ);
lightCamera_Splits_fcs[k].nearCorners[1] = new Vector3(maxX, minY, minZ);
lightCamera_Splits_fcs[k].nearCorners[2] = new Vector3(maxX, maxY, minZ);
lightCamera_Splits_fcs[k].nearCorners[3] = new Vector3(minX, maxY, minZ);
lightCamera_Splits_fcs[k].farCorners[0] = new Vector3(minX, minY, maxZ);
lightCamera_Splits_fcs[k].farCorners[1] = new Vector3(maxX, minY, maxZ);
lightCamera_Splits_fcs[k].farCorners[2] = new Vector3(maxX, maxY, maxZ);
lightCamera_Splits_fcs[k].farCorners[3] = new Vector3(minX, maxY, maxZ);
Vector3 pos = lightCamera_Splits_fcs[k].nearCorners[0] + (lightCamera_Splits_fcs[k].nearCorners[2] - lightCamera_Splits_fcs[k].nearCorners[0]) * 0.5f;
dirLightCameraSplits[k].transform.position = dirLightCameraSplits[k].transform.TransformPoint(pos);
dirLightCameraSplits[k].transform.rotation = dirLight.transform.rotation;
}
}
void ConstructLightCameraSplits(int k)
{
dirLightCamera.transform.position = dirLightCameraSplits[k].transform.position;
dirLightCamera.transform.rotation = dirLightCameraSplits[k].transform.rotation;
dirLightCamera.nearClipPlane = lightCamera_Splits_fcs[k].nearCorners[0].z;
dirLightCamera.farClipPlane = lightCamera_Splits_fcs[k].farCorners[0].z;
dirLightCamera.aspect = Vector3.Magnitude(lightCamera_Splits_fcs[k].nearCorners[0] - lightCamera_Splits_fcs[k].nearCorners[1]) / Vector3.Magnitude(lightCamera_Splits_fcs[k].nearCorners[1] - lightCamera_Splits_fcs[k].nearCorners[2]);
dirLightCamera.orthographicSize = Vector3.Magnitude(lightCamera_Splits_fcs[k].nearCorners[1] - lightCamera_Splits_fcs[k].nearCorners[2]) * 0.5f;
}
}
3.接受阴影
Shader "GLSL/CSMShadowMapping/Receiver" {
SubShader {
Tags { "RenderType"="Opaque" }
LOD 300
Pass {
Name "FORWARD"
Tags{ "LightMode" = "ForwardBase" }
GLSLPROGRAM
//gl_Vertex 顶点
//gl_Position 裁剪空间坐标输出到片元着色器
//gl_FragColor 输出颜色
#include "UnityCG.glslinc"
#include "lib/Custom.glslinc"
#pragma fragmentoption ARB_precision_hint_fastest
uniform mat4 _gWorldToShadow;
uniform sampler2D _gShadowMapTexture;
uniform vec4 _gShadowMapTexture_TexelSize;
/*{TextureName}_TexelSize - a float4 property contains texture size information :
x contains 1.0 / width
y contains 1.0 / height
z contains width
w contains height*/
uniform vec4 _gLightSplitsNear;
uniform vec4 _gLightSplitsFar;
uniform mat4 _gWorld2Shadow[4];
uniform sampler2D _gShadowMapTexture0;
uniform sampler2D _gShadowMapTexture1;
uniform sampler2D _gShadowMapTexture2;
uniform sampler2D _gShadowMapTexture3;
uniform float _gShadowStrength;
struct v2f
{
vec2 uv;
vec4 shadowCoord;
float eyeZ;
vec4 worldPos;
};
//3x3的PCF Soft Shadow
float PCFSample(float depth, vec2 uv)
{
float shadow = 0.0;
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
vec4 col = texture(_gShadowMapTexture, uv + vec2(x, y) * _gShadowMapTexture_TexelSize.xy);
float sampleDepth = DecodeFloatRGBA(col);
shadow += sampleDepth < depth ? _gShadowStrength : 1.0;//接受物体片元的深度与深度图的值比较,大于则表示被挡住灯光,显示为阴影,否则显示自己的颜色(这里显示白色)
}
}
return shadow /= 9.0;
}
vec4 getCascadeWeights(float z)
{
vec4 zNear = vec4(z >= _gLightSplitsNear.x?1.0:0.0,z >= _gLightSplitsNear.y?1.0:0.0,z >= _gLightSplitsNear.z?1.0:0.0,z >= _gLightSplitsNear.w?1.0:0.0);
vec4 zFar = vec4(z < _gLightSplitsFar.x?1.0:0.0,z < _gLightSplitsFar.y?1.0:0.0,z < _gLightSplitsFar.z?1.0:0.0,z < _gLightSplitsFar.w?1.0:0.0);
vec4 weights = zNear * zFar;
return weights;
}
vec4 getShadowCoord(vec4 wpos, vec4 cascadeWeights)
{
vec3 sc0 = (_gWorld2Shadow[0] * wpos).xyz;
vec3 sc1 = (_gWorld2Shadow[1] * wpos).xyz;
vec3 sc2 = (_gWorld2Shadow[2] * wpos).xyz;
vec3 sc3 = (_gWorld2Shadow[3] * wpos).xyz;
return vec4(sc0 * cascadeWeights[0] + sc1 * cascadeWeights[1] + sc2 * cascadeWeights[2] + sc3 * cascadeWeights[3], 1);
}
vec4 SampleShadowTexture(vec4 wPos, vec4 cascadeWeights)
{
vec4 shadowCoord0 = (_gWorld2Shadow[0] * wPos);
vec4 shadowCoord1 = (_gWorld2Shadow[1] * wPos);
vec4 shadowCoord2 = (_gWorld2Shadow[2] * wPos);
vec4 shadowCoord3 = (_gWorld2Shadow[3] * wPos);
shadowCoord0.xy /= shadowCoord0.w;
shadowCoord1.xy /= shadowCoord1.w;
shadowCoord2.xy /= shadowCoord2.w;
shadowCoord3.xy /= shadowCoord3.w;
shadowCoord0.xy = shadowCoord0.xy*0.5 + 0.5;
shadowCoord1.xy = shadowCoord1.xy*0.5 + 0.5;
shadowCoord2.xy = shadowCoord2.xy*0.5 + 0.5;
shadowCoord3.xy = shadowCoord3.xy*0.5 + 0.5;
vec4 sampleDepth0 = texture(_gShadowMapTexture0, shadowCoord0.xy);
vec4 sampleDepth1 = texture(_gShadowMapTexture1, shadowCoord1.xy);
vec4 sampleDepth2 = texture(_gShadowMapTexture2, shadowCoord2.xy);
vec4 sampleDepth3 = texture(_gShadowMapTexture3, shadowCoord3.xy);
float depth0 = shadowCoord0.z / shadowCoord0.w;
float depth1 = shadowCoord1.z / shadowCoord1.w;
float depth2 = shadowCoord2.z / shadowCoord2.w;
float depth3 = shadowCoord3.z / shadowCoord3.w;
#if defined (UNITY_REVERSED_Z)
depth0 = 1 - depth0; //(1, 0)-->(0, 1)
depth1 = 1 - depth1;
depth2 = 1 - depth2;
depth3 = 1 - depth3;
#else
depth0 = depth0*0.5 + 0.5; //(-1, 1)-->(0, 1)
depth1 = depth1*0.5 + 0.5;
depth2 = depth2*0.5 + 0.5;
depth3 = depth3*0.5 + 0.5;
#endif
float shadow0 = sampleDepth0.r < depth0 ? _gShadowStrength : 1.0;
float shadow1 = sampleDepth1.r < depth1 ? _gShadowStrength : 1.0;
float shadow2 = sampleDepth2.r < depth2 ? _gShadowStrength : 1.0;
float shadow3 = sampleDepth3.r < depth3 ? _gShadowStrength : 1.0;
//return col0;
float shadow = shadow0 * cascadeWeights[0] + shadow1 * cascadeWeights[1] + shadow2 * cascadeWeights[2] + shadow3 * cascadeWeights[3];
//return shadow * cascadeWeights;
return vec4(shadow, shadow, shadow, shadow);
}
#ifdef VERTEX
out v2f o;
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
o.uv = gl_MultiTexCoord0.xy;
o.worldPos = unity_ObjectToWorld * gl_Vertex;
o.shadowCoord = _gWorldToShadow * o.worldPos;
o.eyeZ = gl_Position.w;
}
#endif
#ifdef FRAGMENT
in v2f o;
void main()
{
vec4 weights = getCascadeWeights(o.eyeZ);
// sample depth texture
vec4 col = SampleShadowTexture(o.worldPos, weights);//310以后texture2D过期了,使用texture函数
gl_FragColor = col;
}
#endif
ENDGLSL
}
}
}
github:https://github.com/eangulee/GLSLInUnity.git