上一篇写了关于如何实现雪景的技术方案:
本篇在此基础上,说下雨景的实现思路,效果见下:

方案
之前是打算将模型抽象成一套黑盒,然后对于雨和雪,只需要改变对粒子的贴图即可。但实际操作后发现自己忽视了粒子系统的问题。
粒子系统认为每个点仅仅是一个点,不应该有三维的形状。粒子系统只会将粒子的顶点坐标投射到屏幕像素上,并以该点为中心渲染一个小正方形。
对于雪花来讲,如果直接用一个白色的小球来代表的话,不论从什么角度观察,都是一个白色的圆,所以是各向同性的。所以只需要在小正方形里渲染一个小白圆就可以。
但是对于雨滴来讲,它是一个细长的结构,所以从天空向下看雨滴和平视雨滴看到的是不一样的。所以需要额外设计来保证雨滴具有三维形状。
根据调研,为了使粒子有三维结构:
- 第一种是对粒子进行贴图,但是贴图不是直接贴在小正方形上,而是让贴图乘上 MVP 矩阵,然后将透视的结果绘制在小正方形里。这种算法需要将 MVP 的 matrix 和 texture 都传入到 fragment shader 中,相当于每一个小像素都需要去变换,有大量的重复计算。
- 第二种是拿数学公式模拟雨滴,可以直接在 vertex shader 中计算出来雨滴应该绘制的形状,然后将参数传入到 fragment shader 中,将对应的图形绘制在小正方形里。
显然第二种方法更好些,效率高,还能复用雪景模型盒的大部分代码。
椭圆方程
以粒子的空间坐标为椭球的球心,在 vertex shader 中进行计算时,根据该球心坐标,在 Z 轴值上加减 L/2 (半焦距,设定好的固定常数)得到两个焦点的空间坐标,将这两个焦点投影到屏幕上。
由于绘制是以球心在屏幕上的投影坐标为中心的正方形,则需要根据这两个投影后的焦点在屏幕中的位置来确定所绘制的正方形大小。

将上下焦点的投影坐标传入到 fragment shader 中,再以这两点为焦点用椭圆方程绘制一个扁平的椭圆来表示雨滴。
这里要注意一点,这样绘制的椭圆不一定就是该椭球在屏幕上的精确投影,只是当该形状及其狭长时,这样的近似会极其接近,足以满足视觉效果。

考虑风向
在 Z 值上加减 L/2 是默认雨滴是竖直方向的,如果有风时,雨滴会有一定的偏向。
将风抽象成一个模长受限的水平向量(模长最大值可以被设定),然后让风与向量 (0,0,1) 的夹角等于雨滴与 Z 轴的夹角即可。

总结
雨景部分相比于雪景主要就是解决了粒子各向异性问题。既然贴图的路不好走,又不想抛弃粒子系统去渲染大量三维物体,于是就用简易的数学公式来模拟一个三维雨滴形状,从而保证了渲染效率基本不受影响。
而且在雪景的模型盒系统的支持下,雨景还可以有俯视视角、平移、旋转、缩放等等类似的景象,具有良好的视觉效果。
待续。