又踩了一坑,好在谷歌到了之前的一个人遇到相同的问题,顺利解决。
先说说问题背景,我目前的毕设是体数据渲染,实现的办法是raycast。最基本的一点就是在fragment program里,获取raycast的方向。
问题出现在加入阴影之后。在这之前,获取方向是没问题的。
在透视相机中,可以简单地用float3 viewDir = normalize(worldPos - _WorldSpaceCameraPos);
来获取方向。
在正交相机中,我们需要换个方法,来获取相机的朝向(对正交相机来说,朝向就是raycast的方向)。Unity提供了一个参数来表示当前相机的类型。
因此我们可以用这样的式子替换,来适应两种相机
- float3 viewDir =
- unity_OrthoParams.w * -UNITY_MATRIX_V[2].xyz //这个-UNITY_MATRIX_V[2].xyz可以用来表示相机的朝向,即C#代码里的transform.forward
- +
- (1- unity_OrthoParams.w) * (worldPos - _WorldSpaceCameraPos);
这个时候我用常规的办法加入了阴影(引入ShadowCaster的pass)。并且用同样的办法计算深度然后输出。然后……就出问题了,主要表现是在Shadow Map里渲染错位。
一番排查之后发现了问题。目前我只考虑单个Directional Light,下面也都按照Directional Light来说。按照Shadow Map的流程,我们会需要Directional Light为相机渲染一次Shadow Map。此时该Light相当于一个正交相机。尽管之前我们对两种相机都做了适应,但是……在渲染ShadowMap的时候,这个unity_OrthoParams,是不管用的,因此viewDir的计算是按照透视相机来的,于是就错位了。
所以我们得自己判断正交还是透视了。好在有大神给出了解答:使用UNITY_MATRIX_P[3][3]
进行判断。为0表示透视,1表示正交。具体原理我也不清楚,不过测试之后是完全可行的。
修改后的viewDir如下:
- float3 viewDir = normalize(
- UNITY_MATRIX_P[3][3] * -UNITY_MATRIX_V[2].xyz
- +
- (1 - UNITY_MATRIX_P[3][3]) * (worldPos - _WorldSpaceCameraPos));
另外推荐一个系列教程:http://catlikecoding.com/unity/tutorials/ 其中包括了shader编程,由浅入深地把unity5的standard surface shader复现了一遍,包括编辑器界面。同时包括了各种技术的原理、实现。看一遍绝对是受益颇多。除开shader的教程,其他的几个教程也是非常有意思。总之强烈推荐。