Cesium体积云实现思路

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)
);
}

根据相机的位置不同,需要分为多种情况。


Cesium体积云实现思路
http://xt3d.top/2024/06/25/volumeCloud/
作者
xt3d
发布于
2024年6月25日
许可协议