unity, WaterProDaytime注意事项。
一,多个WaterProDaytime不要公用material。
原因是:水面material的shader(FXWaterPro.shader)引用了reflectionTexture,而水面1的reflectionTexture是由水面1的reflectionCamera渲染出来的。水面2的reflectionTexture是由水面2的reflectionCamera渲染出来的。由于水面1的reflectionCamera与水面2的reflectionCamera的观察方向不同,所以必须用两个不同的camera,所以得到的reflectionTexture也是不同的,那么如果水面1和水面2的material用同一个,它们的shader引用的reflectionTexture就只能是同一个,这与水面1与水面2的reflectionTexture不同相矛盾,所以水面1和水面2必须用不同的matrial。(用refractionTexture来分析也是一样)。(如果好奇多个水面使用同一个matrial会发生什么,可以做一下试验,结果就是当摄像机旋转到某个角度时水面的reflection和refraction效果会发生奇怪的跳变)。
如果我们要在场景中同时添加两个Water,正确的做法是:
(1)由WaterProDaytime.prefab创建两个实例Water1和Water2。
(2)将WaterProDaytime.mat复制出两个副本,WaterProDaytime1.mat和WaterProDaytime2.mat。
(3)用WaterProDaytime1.mat替换Water1原来的material,用WaterProDaytime2.mat替换Water2原来的material。
同理,即使场景中只放一个Water,也应该将WaterProDaytime.mat复制一个副本替换原来的material,以避免原prefab的material被污染。
二,WaterProDaytime在Orthographic相机上的bug。
对于Orthographic相机,如果water在视截体内且water平面与相机视线平行,unity将报出“Screen position out of view frustum”这个error。报错行是Water.cs脚本中的reflectionCamera.Render(),但经过分析可以确定是reflectionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane)这一句导致的,更具体地说是unity的Camera.CalculateObliqueMatrix这个API的实现有bug/对Orthographic相机支持不完善/根本就不支持Orthographic?亦或是Camera.CalculateObliqueMatrix正确完成了自己的计算,但Camera.Render()中对于Camera.CalculateObliqueMatrix的特殊情况下的计算结果没有作出正确的理解。
Camera.CalculateObliqueMatrix(clipPlane)在文档中的解释是:
Calculates and returns oblique near-plane projection matrix.
Given a clip plane vector, this function returns camera's projection matrix which has this clip plane set as its near plane.
(关于obliqueMatrix还可以参考:
http://forum.unity3d.com/threads/problem-camera-calculateobliquematrix.252916/
http://www.terathon.com/code/oblique.html
http://aras-p.info/texts/obliqueortho.html
http://www.terathon.com/lengyel/Lengyel-Oblique.pdf)
也就是说,oblique投影矩阵与普通投影矩阵(透视投影矩阵和正交投影矩阵)的差别是:普通投影矩阵所描述的视截体近平面与锥轴垂直,而oblique投影矩阵所描述的视截体近平面是斜的(与锥轴不垂直)。
如下图所示,左图中红色的是透视投影视截体,右图中红色的是oblique投影视截体。由于水面是反射面,所以渲染反射图象时必须以视截体被水面所截的截面作为视口,即“斜视口”,所以必须将反射相机转化成oblique投影模式。reflectionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane)就是干这个事儿。
不过对于一些特殊情况,得不到有限的 斜视口 ,如下两图:
上面左图是透视投影下得不到有限斜视口的例子,右图是正交投影下得不到斜视口的例子。
不过对WaterProDaytime进行测试我发现在其在透视投影相机下总是不会报error的(至少我没测到反例),这说明Camera.CalculateObliqueMatrix在透视投影下还是工作良好而且Camera.Render能正确理解Camera.CalculateObliqueMatrix的计算结果。但对于正交投影相机,经过测试,如上面右图所示,当water与锥轴平行且在h1之下或在h2之上时安全,但在h1和h2之间时会报error。
由于这是unity自已API的问题,完美的解决只能等官方修正。
但work around是有的,就是:
(1)把Water.cs中的
reflectionCamera.Render();
改成:
if(cam是正交投影相机&&视线与本水面平行){
reflectionCamera.Render();
}
(2)把Water.cs中的
refractionCamera.Render();
改成:
if(cam是正交投影相机&&视线与本水面平行){
refractionCamera.Render();
}
三,WaterProDaytime不能在cubemap中反射。
如下图,其中动态cubemap球体用下面日志中的方法做的:http://www.cnblogs.com/wantnon/p/4480727.html
当球体的Camera_cubeMapRealTime的Culling Mask选为“除Water外所有物体”,效果如下:
但是当球体的Camera_cubeMapRealTime的Culling Mask选为Everything,效果变成:
显然是不正常的。原因我还不清楚。