Unity Shader 学习笔记(14) 阴影
2017-11-19 21:13
344 查看
Unity Shader 学习笔记(14) 阴影
参考书籍:《Unity Shader 入门精要》【常见问题】Unity阴影和深度纹理的补充说明(重要)
*版本:2017.1.1f1
阴影
实现原理
使用Shadow Map技术。把摄像机与光源位置重合,光源的阴影部分就是摄像机看不到的地方。前向渲染路径中,最重要的平行光如果开启了阴影,Unity就会为光源计算阴影映射纹理(shadowmap),本质就是深度图,记录光源出发到最近表面位置。两种方法:
1. 摄像机放在光源位置,然后按正常渲染流程(调用Base Pass 和 Additional Pass)来更新深度信息,得到阴影映射纹理。
2. 摄像机放在光源位置,调用额外的Pass:LightMode = ShadowCaster,把顶点变换到光源空间,渲染目标不是帧缓存,而是阴影映射纹理。
阴影采样
传统方法:正常渲染Pass,计算顶点的光源空间,用xy分量对纹理采样,如果顶点值大于该深度值,就说明在阴影区域。
Unity5及以后:
屏幕空间的阴影映射技术(Screenapce Shadow Map)。是在延迟渲染中产生阴影的方法。不过需要显卡支持MRT。根据阴影映射纹理和深度纹理得到屏幕空间的阴影图。阴影图包含了屏幕空间所有阴影区域。
接收其他物体阴影
在Shader中对阴影映射纹理(包括屏幕空间的阴影图)进行采样,结果和最后的光照相乘即可。向其他物体投射阴影
把物体加入到光源的阴影映射纹理计算中(让其他物体可以得到该物体信息),即执行LightMode 为 ShadowCaster的Pass。具体实现
使用AutoLight.cginc文件内的三个宏:- SHADOW_COORDS:声明了一个名为
_ShadowCoord的阴影纹理坐标变量。
- TRANSFER_SHADOW:根据不同平台实现。如果使用了屏幕空间的阴影映射技术,会使用内置
ComputeScreenPos函数计算
_ShadowCoord;否则就直接把顶点转换到光源空间存到
_ShadowCoord。
- SHADOW_ATTENUATION:对
_ShadowCoord采样,得到阴影信息。
需要注意:内置宏用了一些变量名需要在自己定义的时候要匹配:a2f的顶点坐标为vertex,输出的v2f结构体名为v,v2f中顶点位置名为pos。
计算阴影深度,绘制深度纹理:
![](http://oz530cn73.bkt.clouddn.com/68.png)
绘制阴影:
![](http://oz530cn73.bkt.clouddn.com/69.png)
最后图形:
![](http://oz530cn73.bkt.clouddn.com/70.png)
BasePass:
... #include "AutoLight.cginc" // 计算阴影的宏 ... struct v2f { ... // 对阴影纹理采样的坐标,参数为下一个可用插值寄存器的索引值(前面TEXCOORD0和1,所以这里是2) SHADOW_COORDS(2) }; v2f vert(a2v v) { ... // 计算阴影纹理坐标 TRANSFER_SHADOW(o); return o; } fixed4 frag(v2f i) : SV_Target { ... // 计算阴影值 fixed shadow = SHADOW_ATTENUATION(i); return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0); }
衰减和阴影统一管理
使用AutoLight.cginc的内置宏UNITY_LIGHT_ATTENUATION,不再需要自己判断光源类型等,也不用在BasePass中单独处理阴影。如果Additional Pass需要添加阴影,用#pragma multi_compile_fwdadd_fullshadows命令。
第一个参数变量名,宏会创建这个名字的变量;第二个参数为v2f结构体,用来计算阴影;第三个参数是世界空间的坐标,计算光照衰减。
fixed4 frag(v2f i) : SV_Target { ... // 不需要定义atten,下面宏会自己定义 // 使用内置宏同时计算光照衰减和阴影。自动声明atten变量。 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); return fixed4(ambient + (diffuse + specular) * atten, 1.0); }
部分源码如下。可以看到点光源、聚光灯、平行光的计算。后面还有部分代码是根据是否启用cookie的情况做不同版本的宏。
![](http://oz530cn73.bkt.clouddn.com/71.png)
透明物体的阴影
透明测试与阴影
如果直接使用FallBack默认回调(VertexLit、Diffuse、Specular),往往无法得到正确的阴影,因为在透明测试中某些片元丢弃了,而默认回调没有进行这样的处理。可以使用回调Transparent/Cutout/VertexLit。可见源文件Alpha-VertexLit.shader。
第一个:FallBack “Transparent/VertexLit”;第二个:FallBack “Transparent/Cutout/VertexLit”;第三个:基于第二个同时在MeshRenderer的Cast Shadows选择Two Sided。
![](http://oz530cn73.bkt.clouddn.com/72.png)
透明混合与阴影
由于需要关闭深度写入,阴影处理变得复杂,所以内置半透明的Shader都是没有阴影效果的。可以修改Fallback为VertexLit、Diffuse等不透明物体用的UnityShader。默认不投射不接收阴影(FallBack “Transparent/VertexLit”):
![](http://oz530cn73.bkt.clouddn.com/73.png)
强制接收阴影(FallBack “VertexLit”),但不能透过立方体看到后面墙壁的阴影:
![](http://oz530cn73.bkt.clouddn.com/74.png)
半透明阴影
原文见这里。实现原理:对物体剔除一些片元(挖洞),通过洞的密度和大小,伪造出阴影的透明效果,实际阴影颜色没有改变。
_DitherMaskLOD就是用来计算哪些地方挖洞的纹理。
关键代码:
tex3D(_DitherMaskLOD, float3(vpos.xy_0.25,alpha_0.9375)).a。
![](http://oz530cn73.bkt.clouddn.com/116.png)
相关文章推荐
- UnityShader入门精要学习笔记(十三):光照衰减与Unity阴影
- Unity Shader学习笔记:凹凸映射
- Unity学习笔记14——导入视频格式问题(QuickTime player)
- Unity Shader 学习笔记(23) 运动模糊
- Unity Shader 学习笔记(26) 边缘检测(深度和法线纹理)
- Unity Shader 学习笔记(4)Unity Shader内置变量、函数,Shader Model
- 【UnityShader】学习笔记 灯光
- Unity Shader学习笔记:内置变量
- Unity Shader 学习笔记(九) UV动画Shader实例
- Unity Shader 学习笔记(8) 纹理映射、凹凸映射
- 【 4000 UnityShader】学习笔记 可编程渲染管线结构及语义
- UnityShader入门精要学习笔记(二十):运动模糊
- unity之shader学习笔记(四)--高光反射
- Unity Shader 学习笔记 (二) 简单颜色Shader
- Unity Shader学习笔记:Bloom效果
- Unity Shader学习笔记:遮罩纹理
- Unity Shader 学习笔记(17) 程序纹理(Procedural Texture)、程序材质(Procedural Materials)
- unity之shader学习笔记(二)
- Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础
- Unity Shader学习笔记:渲染纹理