# Unity阴影渲染实现(一)Unity宏阴影

这段时间使用Unity把阴影自己尝试着实现了一下。原理讲起来不难,但是在实现过程中还是踩了不少的坑。下面就让我把这段时间遇到的坑以及整个实现的过程都记录一下。本来是打算用最近在写的一个Vulkan渲染器去实现的,结果由于进度太慢连RHI都还没有封装完,想了想还是先用Unity搞一下好了。果然学习还是要一步一步慢慢来。那废话也不多说了,就开始吧。

既然是Unity的阴影,首先不得不提的就是Unity自带的用来渲染阴影的三个宏,分别是SHADOW_COORDS, TRANSFER_SHADOW以及SHADOW_ATTENUATION

SHADOW_COORDS用于声明一个用于对阴影纹理采样的坐标,使用方法也非常简单,直接写在v2f的结构体里面就可以了,例如

struct v2f {
    //其他属性的声明
    ...

    //声明阴影纹理坐标
    SHADOW_COORDS(2)
};
1
2
3
4
5
6
7

TRANSFER_SHADOW的实现会根据平台的不同而不同。如果当前平台支持屏幕空间的阴影映射,则会通过调用内置的ComputeScreenPos函数来计算_ShadowCoord,如果不支持屏幕空间的阴影映射,就会使用传统的阴影映射技术,通过把定点坐标从模型空间变换到光源空间,然后存储到_ShadowCoord中。如果我们在v2f结构体中使用了SHADOW_COORDS,只需要再在顶点着色器中直接使用TRANSFER_SHADOW即可

v2f vert(a2v v) {
    v2f o;
    //其他属性值的计算
    ...
    
    //转换纹理坐标
    TRANSFER_SHADOW(o);
    
    return o;
}
1
2
3
4
5
6
7
8
9
10

SHADOW_ATTENUATION负责使用_ShadowCoord对阴影相关的纹理进行采样得到阴影信息。这句宏也是直接在片元着色器中直接使用即可

fixed4 frag(v2f i) : SV_Target {
    //物体表面光照计算
    ...
    
    //使用内置宏对阴影进行采样
    fixed shadow = SHADOW_ATTENUATION(i);

    return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0);
}
1
2
3
4
5
6
7
8
9

当然,既然是宏,在使用的时候对命名也会有一些要求。为了使这些宏正确工作,需要保证a2v结构体中的顶点坐标命名必须为vertex, 顶点着色器的输入结构体a2v必须命名为vv2f中的顶点位置变量必须命名为pos

最后我们只需要在Unity编辑器当中,对想要投射阴影对物体开启Cast Shadow选项,对接收阴影的平面开启Receive Shadows选项就能渲染出阴影了。效果大概长这样子

unity shadow

但仅仅是这样子的话,我们自己好像啥也没有做,那么下一篇,就让我们来实现一个自定义阴影。