今天用GLSL实现一下Shadow Mapping,Shadow Mapping原理也比较简单,先在灯光的位置生成一个相机,平行光就使用正交模式,本篇使用的就是平行光,并且相机的朝向与灯光朝向一致,渲染出一张深度图,也就是生成了Shadow Mapping图,接收阴影时通过uv去采样深度图,比较深度即可,大于深度则说明被遮挡了,反之则没有,更详情的可以参考文章顶部的链接。
以下是效果:
生成深度图
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 (SHADER_TARGET_GLSL)
depth = depth* 0.5 + 0.5; //(-1, 1)-->(0, 1)
#elif defined (UNITY_REVERSED_Z) //如果是使用的reverzed-z,需要处理一下
depth = 1.0 - depth; //(1, 0)-->(0, 1)
#endif
gl_FragColor = EncodeFloatRGBA(depth);
}
#endif
ENDGLSL
}
}
}
接收阴影
Shader "GLSL/ShadowMapping/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"
uniform mat4 _gWorldToShadow;
uniform sampler2D _gShadowMapTexture;
/*{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 _gShadowMapTexture_TexelSize;
uniform float _gShadowStrength;
//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;
}
#ifdef VERTEX
out vec4 shadowCoord;
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vec4 worldPos = unity_ObjectToWorld * gl_Vertex;
shadowCoord = _gWorldToShadow * worldPos;
}
#endif
#ifdef FRAGMENT
in vec4 shadowCoord;
void main()
{
// shadow
vec2 uv = shadowCoord.xy / shadowCoord.w;
uv = uv * 0.5 + 0.5; //(-1, 1)-->(0, 1)
float depth = shadowCoord.z / shadowCoord.w;
#if defined (SHADER_TARGET_GLSL)
depth = depth * 0.5 + 0.5; //(-1, 1)-->(0, 1)
#elif defined (UNITY_REVERSED_Z)
depth = 1 - depth; //(1, 0)-->(0, 1)
#endif
// PCFSample
// float shadow = PCFSample(depth, uv);
// gl_FragColor = vec4(shadow, shadow, shadow, shadow);
// sample depth texture
vec4 col = texture(_gShadowMapTexture, uv);//310以后texture2D过期了,使用texture函数
float sampleDepth = DecodeFloatRGBA(col);
float shadow = sampleDepth < depth ? _gShadowStrength : 1.0;//接受物体片元的深度与深度图的值比较,大于则表示被挡住灯光,显示为阴影,否则显示自己的颜色(这里显示白色)
gl_FragColor = vec4(shadow, shadow, shadow, shadow);
}
#endif
ENDGLSL
}
}
}
C#设置全局变量
using UnityEngine;
public class ShadowMapping : MonoBehaviour
{
public Light dirLight;
Camera dirLightCamera;
public int shadowResolution = 1;
public Shader shadowCaster = null;
private RenderTexture rt_2d;
void OnDestroy()
{
dirLightCamera = null;
DestroyImmediate(rt_2d);
}
//创建rt
private RenderTexture CreateRenderTexture()
{
RenderTextureFormat rtFormat = RenderTextureFormat.Default;
if (!SystemInfo.SupportsRenderTextureFormat(rtFormat))
rtFormat = RenderTextureFormat.Default;
rt_2d = new RenderTexture(512 * shadowResolution, 512 * shadowResolution, 24, rtFormat);
rt_2d.hideFlags = HideFlags.DontSave;
Shader.SetGlobalTexture("_gShadowMapTexture", rt_2d);
return rt_2d;
}
//创建正交相机
public Camera CreateDirLightCamera()
{
GameObject goLightCamera = new GameObject("Directional Light Camera");
Camera LightCamera = goLightCamera.AddComponent<Camera>();
LightCamera.cullingMask = 1 << LayerMask.NameToLayer("Caster");
LightCamera.backgroundColor = Color.white;
LightCamera.clearFlags = CameraClearFlags.SolidColor;
LightCamera.orthographic = true;
LightCamera.orthographicSize = 10f;
LightCamera.nearClipPlane = 0.3f;
LightCamera.farClipPlane = 100;
LightCamera.transform.rotation = dirLight.transform.rotation;
LightCamera.transform.position = dirLight.transform.position;
//LightCamera.enabled = false;
return LightCamera;
}
private void Update()
{
if (dirLight)
{
if (!dirLightCamera)
{
dirLightCamera = CreateDirLightCamera();
dirLightCamera.targetTexture = CreateRenderTexture();
}
//dirLightCamera.RenderWithShader(shadowCaster, "");//没用
dirLightCamera.SetReplacementShader(shadowCaster, "RenderType");
Shader.SetGlobalFloat("_gShadowBias", 0.005f);
Shader.SetGlobalFloat("_gShadowStrength", 0.5f);
Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(dirLightCamera.projectionMatrix, false);
Shader.SetGlobalMatrix("_gWorldToShadow", projectionMatrix * dirLightCamera.worldToCameraMatrix);
}
}
}
好了,下次可以实现一下CSM(Cascaded Shadow Mapping),一起期待吧。