🗒️Real-Time Grass Rendering for General 3D Scenes
type
status
date
slug
summary
tags
category
icon
password
Grass Blade Model
草叶的描述集为:组成(贝塞尔曲线控制点、静置高度(长度)、宽度、方向角、上向量),其中上向量的朝向由地面的法线确定。
是固定的,通过Physical Model解算得出,通过计算得出。
Physical Model
Displacement
的位移通过如下公式计算:
其中分别是恢复力,重力和风力,是因为碰撞所产生的位移,叠加起来就是的最终位移。
最终的位移会保存到Force Map中,AOE范围内的每片树叶占用一个像素,其RGB通道保存的三维向量数值。特别的,其A通道保存草叶因碰撞所受影响的强度系数,简称碰撞强度系数。
随着时间的推进,会越来越小(但不会为负数),这导致在后续没有连续碰撞的时候草叶会“站立起来”。可以用如下的模型描述的变化:
其中是初始碰撞强度,是用户给定的衰减速率,是时间。
Natural Forces
因为只有是可移动的,因此描述的作用力都是作用在上,下图描述的是草叶的受力情况:
Recovery
草叶需要在被碰撞之后恢复到原来的位置,这点和弹簧很相似,因此恢复力可以用胡克定律描述。假设是的初始点,是草叶的刚性系数(决定刚体的柔韧性),那么恢复力为:
Gravity
在这里会受到两个重力,一个是自然重力,另外一个是前向重力。
是全局重力和重心重力的插值和。全局重力是指场景固定的重力,它始终竖直向下。重心重力的方向是指向重心。给定插值参数,那么:
的大小是倍的,方向是垂直于宽度平面的单位向量所指向的方向:
总共受到的重力为:
Wind
风的强度在场景里的描述是一个向量值函数,他根据位置返回这个位置的风强。我们常常采用正弦和余弦函数的加权和来建模这个函数。
接下来考虑风对草叶的影响。一般来说,风对草叶的顶部(接近处)的影响比较大,而对底部(接近处)的影响比较小。这个影响因子的计算如下:
另外,风的方向如果在宽度方向的影响比较小,而在与叶片正交(垂直于)的影响比较大。这个影响因子的计算如下:
这两个影响因子的乘积影响最终的风力:
即:
State Validation
在计算三个力之后,就可以确定,进而确定新的。不过这个位置不一定是合法的,比如可能计算出来的位置是在地面的下面。具体有三个点:
- 不能在地面的下面
- 必须通过计算得出
- 草叶的长度必须等于静置时的高度
第一点,可以用如下公式计算:
第二点,可以用投影计算:
的水平距离:
于是可以用如下关系确定,保证介于之间:
第三点可以用贝塞尔曲线近似长度计算:
在这里我们是二阶贝塞尔,所以,于是:
最后用纠正:
Collision
交互的时候执行Collision的逻辑。为了能高效执行,我们将碰撞的主动触发者的包围盒假定为球体。这就意味着对于复杂的网格,需要用多个球体近似.
我们无须判断曲线上的任意一点与球体相交,这样效率比较低。我们直接找到一个点,只要碰到了,那就发生了碰撞,这样省去了找曲线上任意一点的时间:
位于三角形的重心.
接着就使用经典球体求交点的方法,假设碰撞点为,球心,球半径为,求碰撞点指向球表面的向量:
按理来说应该是碰撞点指向圆心的向量,那为什么是指向球表面? 因为是恒成立的,这就意味着方向始终是和相反的.
这个就是碰撞产生的位移,其大小为,影响碰撞强度系数:
Rendering
Culling
Orientation test
这个剔除部份基于相机和Blade的方向。主要是因为,我们的Blade说到底都是伪3D,这就意味着Blade是没有厚度的(我们从头到尾都没考虑到厚度)。因此如果Blade的宽度方向与相机的前向量平行,会产生一些伪影,所以需要剔除。
假设相机的前向量为,Blade的宽度方向向量为,那么这个Blade需要剔除的条件为:
View-frustum test
这是比较经典的视锥体剔除。我们测试范围内的每个Blade的,并利用VP变换获得透视空间(不考虑正交空间)的坐标。给一个微小的值(也可以为0)用来偏移透视除法因子(同时表示视锥体边界),得到的为偏移的视锥体边界:
不满足的Blade都应该剔除。
Distance test
相对于相机较远的Blades也应该剔除,一是为了性能,二是防止在透视投影的情况下离相机越远越容易出现z-fighting的现象,三是Blades在远处很小容易产生 Aliasing Artifacts(锯齿)。
因为在接近地平线观察草时所看到的草的密度会比在草的上方看到草的密度要大,所以在进行剔除之前,先计算相机到Blade在由定义的投影平面内的距离 :
其中,表示相机与的角度。明显在上方的时候,在地平线的时候。
假设用户定义的最大距离为,将Blade划分为个距离级别,属于的均匀分布。最低级别不会剔除不会剔除任何Blade,级别1只会剔除一个Blade……级别会剔除所有的Blade。
为了确认哪些同样距离级别Blades被剔除,每个Blade都有个索引()。以下不等式成立的时候,就会剔除Blade:
Occlusion test
方法比较简单,比较深度即可。
Blade Geometry
接下来是如何确定草的形状,叶片形状的生成在曲面细分着色器中执行。对于叶片的形状,最初由确定,其中表示沿叶片宽度的插值,表示沿高度的插值。
我们采用De Casteljau’s算法实现形状的绘制,计算切向量作为中间结果。副切线由预先计算的沿叶片宽度的方向矢量直接给出。利用两个切向量,可以通过叉积计算法线。这些计算结果如下式所示,其中 是使用插值参数得到的曲线点,和是跨越叶片宽度 的两个结果曲线点。另外,和分别是辅助向量:
下面将基于这个算法,实现更复杂的形状。
Basic shapes
位置通过点通过线性插值,其中插值参数由决定:
比如Blade的形状是一个正方形,那么;如果是三角形,那么;1/4椭圆的;三角形+矩形的组合的,其中(其实就是把分段函数写到一个函数里了)。
Dandelion
上面的方法可以实现很复杂的形状,比如:
3D displacement
这是一个可选选项,添加3D位移以增强其真实感的方法,位移沿着叶片中轴的法向量发生,使叶片的横截面呈“V”形。
其中是Blade宽度,是法向量。
Width correction
远处的Blade因为投影在屏幕上的像素很小,容易出现锯齿。
通过使用基于像素宽度的校正值修改相应形状的插值参数可以减少这种影响,这样无论选择的形状如何,远距离的草叶都被渲染为四边形。假设NDC的基向量为,那么执行以下步骤可以修正这个现象:
- 曲线的点通过MVP变换到NDC坐标系,其点的范围
- 计算这些屏幕坐标之间的差值,得到向量
- 计算,表示叶片在NDC下的宽度,可以通过和来计算修正值(表示叶片的最小宽度,表示区间长度,假如,那么像素区间在的叶片都需要纠正)。如果,那么,强制让这个叶片渲染成四边形,无影响。
下面是计算的公式: