Cesium中实现体积云可以通过云图噪声+光线步进的方式来实现,云图噪声用于生成云的形状,而光线步进则用于从云图噪声中采样云的密度
具体实现可以通过Primitive或者后处理,因为体渲染是没有顶点信息的,需要在片元着色器中进行相关计算,但是我们的光线步进计算需要坐标信息,如果是通过Primitive的方式,我们可以在顶点着色器中将坐标传递到片元着色器,如果是采样后处理的方式,我们需要在片元着色器中还原世界坐标。
在进行光线步进时,为了提高计算性能,需要确定光线步进的起点和终点,比如实现局部体积云时,我们一般是限制云在一个Box盒子内进行渲染,也就是限定光线步进的范围为一个Box。最后转换为求光线(射线)与Box求交,GLSL算法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| vec4 rayBoxDst(vec3 boundsMin, vec3 boundsMax, vec3 rayOrigin, vec3 invRaydir) { vec3 t0 = (boundsMin - rayOrigin) * invRaydir; vec3 t1 = (boundsMax - rayOrigin) * invRaydir; vec3 tmin = min(t0, t1); vec3 tmax = max(t0, t1);
float dstA = max(max(tmin.x, tmin.y), tmin.z); float dstB = min(tmax.x, min(tmax.y, tmax.z));
float dstToBox = max(0., dstA); float dstInsideBox = max(0., dstB - dstToBox); return vec4(dstToBox, dstInsideBox,dstA,dstB); }
|
如果是实现全球体积云,求取光线步进的起点和终点则稍微复杂,因为云层范围是一个球壳。

计算光线的步进起点和终点需要计算光线与球的交点,GLSL算法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| vec2 raySphereIntersect(vec3 r0, vec3 rd, float sr) { float a = dot(rd, rd); float b = 2.0 * dot(rd, r0); float c = dot(r0, r0) - (sr * sr); float d = (b * b) - 4.0 * a * c;
if (d < 0.0) return vec2(-1.0, -1.0); float squaredD = sqrt(d);
return vec2( (-b - squaredD) / (2.0 * a), (-b + squaredD) / (2.0 * a) ); }
|
根据相机的位置不同,需要分为多种情况。
