Cesium高级教程-3D高斯泼溅-Splat-高斯数据渲染

发布时间:2026/6/23 14:29:49
Cesium高级教程-3D高斯泼溅-Splat-高斯数据渲染 Cesium高级教程-3D高斯泼溅-Splat-高斯数据渲染数据加载完成后下一步我们应该做的是排序操作但是现在我们先省略排序的步骤直接先进行数据的渲染因为排序只会影响绘制图形的前后遮挡关系并不会影响图形的变换及着色结果并且排序的结果是否正确只有渲染出来才能验证所以我们先将数据渲染出来再看排序对渲染结果的影响。实例化渲染准备在开始渲染Splat数据之前我们需要先准备好图形实例化渲染的基本环境相关内容前面百万级图形渲染一章已经介绍得非常详细了这里就不再重复阐述实例化渲染一个矩形的基本代码如下// z// | /y// |/// o------xconstpositionsnewFloat32Array([-2,-2,2,-2,2,2,-2,2]);constvertexBufferCesium.Buffer.createVertexBuffer({context:viewer.scene.context,typedArray:positions,usage:Cesium.BufferUsage.STATIC_DRAW});constattributes[{index:0,vertexBuffer:vertexBuffer,componentsPerAttribute:2,componentDatatype:Cesium.ComponentDatatype.FLOAT,}];constvertexArraynewCesium.VertexArray({context:viewer.scene.context,attributes:attributes});constvs...constfs...constshaderProgramCesium.ShaderProgram.fromCache({context:viewer.scene.context,vertexShaderSource:vs,fragmentShaderSource:fs,attributeLocations:{position:0,}})letpositionCesium.Cartesian3.fromDegrees(105.41883,26.68244,0);letmodelMatrixCesium.Transforms.eastNorthUpToFixedFrame(position);letcommandnewCesium.DrawCommand({modelMatrix:modelMatrix,vertexArray:vertexArray,shaderProgram:shaderProgram,renderState:Cesium.RenderState.fromCache({depthTest:{enabled:true,}}),pass:Cesium.Pass.OPAQUE,instanceCount:vertexCount,uniformMap:{}})classMyPrimitive{constructor(){}update(frameState){frameState.commandList.push(command);}isDestroyed(){returnfalse;}}letpnewMyPrimitive();viewer.scene.primitives.add(p);代码中我们省略了着色器代码缺省代码可以在前面章节中获取因为这里我们只先准备一个基本骨架。因为我们最终需要将结果渲染在地球上的某个位置所以需要指定一个从世界坐标原点到该位置的变换矩阵modelMatrix这里可以自行定义该位置的坐标值。instanceCount属性初始值为0当vertexCount变化后记得更新instanceCount数据纹理与索引接下来我们按照 Splat Viewer 中的代码先将高斯数据打包到一张纹理图里面去因为打包过程是在WebWorker中进行的所以我们需要将数据传入Worker并注册消息事件接收结果。letdataTexturenewCesium.Texture({context:viewer.scene.context,width:1,height:1,pixelFormat:Cesium.PixelFormat.RGBA_INTEGER,pixelDatatype:Cesium.PixelDatatype.UNSIGNED_INT,sampler:newCesium.Sampler({minificationFilter:Cesium.TextureMinificationFilter.NEAREST,magnificationFilter:Cesium.TextureMagnificationFilter.NEAREST})});functioncreateTexture(texdata,texwidth,texheight){returnnewCesium.Texture({context:viewer.scene.context,width:texwidth,height:texheight,pixelFormat:Cesium.PixelFormat.RGBA_INTEGER,pixelDatatype:Cesium.PixelDatatype.UNSIGNED_INT,source:{arrayBufferView:texdata,width:texwidth,height:texheight,},sampler:newCesium.Sampler({minificationFilter:Cesium.TextureMinificationFilter.NEAREST,magnificationFilter:Cesium.TextureMagnificationFilter.NEAREST})});}letcommandnewCesium.DrawCommand({...uniformMap:{u_texture:(){returndataTexture;},}})...上面的代码中我们先创建了一张默认的纹理图当我们接收到WebWorker的打包结果后再覆写其内容。现在我们需要手动触发纹理打包操作因为Splat Viewer中默认是排序时才会打包纹理而我们现在并没有开启排序。要触发纹理打包操作我们需要修改两处代码一是在接收到网络请求的高斯数据后将其推送到WebWorker二是在WebWorker中接收到数据后调用generateTexture函数进行纹理打包。functioncreateWorker(self){...letsortRunning;self.onmessage(e){if(e.data.buffer){buffere.data.buffer;vertexCounte.data.vertexCount;}elseif(e.data.vertexCount){vertexCounte.data.vertexCount;}elseif(e.data.view){viewProje.data.view;throttledSort();}};}...while(true){...if(vertexCountlastVertexCount){worker.postMessage({buffer:splatData.buffer,vertexCount:Math.floor(bytesRead/rowLength),});lastVertexCountvertexCount;}}现在纹理数据已经有了但是我们还得为其准备一个默认的索引排序这里我们直接将0,1,2,3,4...设置为其默认的索引即可索引数据需要通过Buffer来传递所以我们需要修改一下前面默认的VAO。constinitialIndicesnewUint32Array(x);for(leti0;ix;i)initialIndices[i]i;letindexBufferCesium.Buffer.createVertexBuffer({context:viewer.scene.context,typedArray:initialIndices,usage:Cesium.BufferUsage.STATIC_DRAW});constattributes[{index:0,vertexBuffer:vertexBuffer,componentsPerAttribute:2,componentDatatype:Cesium.ComponentDatatype.FLOAT,},{index:1,vertexBuffer:indexBuffer,componentsPerAttribute:1,instanceDivisor:1,componentDatatype:Cesium.ComponentDatatype.UNSIGNED_INT}];着色器代码修改现在我们把Splat Viewer中的两个着色器代码片段直接拷贝进来替换vs与fsfs代码基本不需要进行任何修改而vs代码中我们需要一些修改才能正常运行。首先是视图矩阵(view)、投影矩阵projection以及视口大小viewport需要改为Cesium内置的变量其次是focal参数焦距Cesium中并没有内置的我们可以通过vec2(viewport.y / 2.0) * abs(projection[1][1]);或vec2(czm_viewport.z * czm_projection[0][0])来获取其余代码保持不变。constvs... uniform highp usampler2D u_texture; // uniform mat4 projection, view; // uniform vec2 focal; // uniform vec2 viewport; in vec2 position; in float splatIndex;//Cesium中不支持int类我们可以先用float后面再进行类型转换 out vec4 vColor; out vec2 vPosition; void main () { int indexint(splatIndex); mat4 viewczm_modelView; mat4 projectionczm_projection; vec2 viewportczm_viewport.zw; vec2 focal vec2(viewport.y / 2.0) * abs(projection[1][1]); uvec4 cen texelFetch(u_texture, ivec2((uint(index) 0x3ffu) 1, uint(index) 10), 0); vec4 cam view * vec4(uintBitsToFloat(cen.xyz), 1); vec4 pos2d projection * cam; ... }.trim();示例效果可到 xt3d 官网 运行查看至此就可以运行页面查看渲染结果不出意外的话现在看到的就是一堆带有颜色的椭圆这些椭圆都是由高斯椭球投影而来因为现在没有开启颜色混合所以看起来和原始效果差别有点大。颜色混合与轴向开启颜色混合选项需要修改绘制指令的渲染状态属性混合模式有很多种Cesium内置的这里我们选择与Splat Viewer中比较近似的一种PRE_MULTIPLIED_ALPHA_BLEND当然也可以自定义混合模式。letcommandnewCesium.DrawCommand({modelMatrix:modelMatrix,vertexArray:vertexArray,shaderProgram:shaderProgram,...})示例效果可到 xt3d 官网 运行查看现在颜色渲染基本上正确了但是看上去是倾斜的这是因为默认的Splat数据是Y轴朝上的我们只需要将其绕X轴旋转一下即可。letpositionCesium.Cartesian3.fromDegrees(105.41883,26.68244,0);letmodelMatrixCesium.Transforms.eastNorthUpToFixedFrame(position);letrotationCesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(-90)));Cesium.Matrix4.multiply(modelMatrix,rotation,modelMatrix);示例效果可到 xt3d 官网 运行查看更多内容见 Cesium高级教程-教程简介