Unity中实现Post Process Volumetric Lighting

一直觉得体积光之类的效果很酷炫,在shadertoy也看到了很多惊艳的ray marching实现。看了一些文章和Paper相关的内容之后决定在Unity中实现一遍,记录下来。文中都是个人的理解,肯定会有一些错误,希望能有所帮助。

Volumetric LightING

  • 自定义Shader中采样Shadow Map
  • 通过CameraDepthTexture计算世界坐标
  • Ray Marching 计算scatter

自定义shader中采样shadow Map

关于shadowmap

在unity中,每个开启投射阴影的光源都会计算一张shadowMap。shadowMap可以用于实现screen space shadow。简单的说对于screen space的一个像素我们可以通过这个像素的深度值来获取该像素的世界坐标,要判断这个像素是不是在阴影之中,就是该点朝光源方向(平行光就是光源方向,点光源就是光源位置)是否被其他物体阻挡,然而在shader中发射一条射线进行碰撞检测是不现实的。而shadowmap的实现方式是在光源位置渲染整个场景,记录下深度信息储存在shadowmap中。
如果屏幕空间的一个像素P1,其对应世界坐标映射在shadowmap的点P2。如果P1的深度值大于P2的深度值,则P1处于阴影之中。

渲染shadowmap要尽可能把主摄像机视椎体内的对象全部包括在内部,这样所有的像素才能在shadowmap中找到对应的点。如果一个点在屏幕空间中但是其对应的世界坐标不在shadowmap中一般设定其不在阴影中。在光源处渲染场景的视锥要包括所有对象的同时又不能太大。太大在相同大小的shadowmap情况下会导致阴影的精度降低。

具体的shadowmap渲染有许多不同的实现方式,Cascaded Shadow Maps可以解决一些ShadowMap的精度问题。具体如何选取光源位置的视椎体可以阅读基于Shadow Map的阴影实现

在shader中获取shadowmap

在shader中可以通过 _CameraDepthTexture直接获取到当前的深度图进行采样,然而untiy没有直接提供这样的对象,通过其他方式获取到shadowmap。
Unity SL-BuiltinMacros中提供了方法来对shadowmap进行采样,但并没有具体的使用方法。

Macro: Use:
UNITY_DECLARE_SHADOWMAP(tex) Declares a shadowmap Texture variable with name “tex”.
UNITY_SAMPLE_SHADOW(tex,uv) Samples shadowmap Texture “tex” at given “uv” coordinate (XY components are Texture location, Z component is depth to compare with). Returns single float value with the shadow term in 0..1 range.

经过了一番(漫长)搜索之后终于找到了使用的方式,要通过unity提供的command buffer把光源的shadowmap Texture 传递给shader。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private Light m_light;
private CommandBuffer m_cbShadowMap;
...
void OnRenderObject()
{
if (m_light == null) m_light = GetComponent<Light>();
if (m_cbShadowMap == null)
{
m_cbShadowMap = new CommandBuffer();
m_cbShadowMap.name = "ShadowMap";
m_cbShadowMap.SetGlobalTexture("_DirectionalLightShadowMap", new RenderTargetIdentifier(BuiltinRenderTextureType.CurrentActive));
}
if (m_light.GetCommandBuffers(LightEvent.AfterShadowMap).Length == 0)
{
m_light.AddCommandBuffer(LightEvent.AfterShadowMap, m_cbShadowMap);
}
}

在LightEvent.AfterShadowMap时把该光源的rendertexure 设置为全局变量 _DirectionalLightShadowMap,这样就能在shader中获取shadowmap了sampler2D _DirectionalLightShadowMap;

采样shadowMap

待续

通过CameraDepthTexture计算世界坐标

待续

Ray Marching 计算scatter

待续