当前位置: 首页 > news >正文

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

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

数据加载完成后下一步我们应该做的是排序操作,但是现在我们先省略排序的步骤直接先进行数据的渲染,因为排序只会影响绘制图形的前后(遮挡)关系,并不会影响图形的变换及着色结果,并且排序的结果是否正确只有渲染出来才能验证,所以我们先将数据渲染出来,再看排序对渲染结果的影响。

实例化渲染准备

在开始渲染Splat数据之前我们需要先准备好图形实例化渲染的基本环境,相关内容前面百万级图形渲染一章已经介绍得非常详细了,这里就不再重复阐述,实例化渲染一个矩形的基本代码如下

// z// | /y// |/// o------xconstpositions=newFloat32Array([-2,-2,2,-2,2,2,-2,2]);constvertexBuffer=Cesium.Buffer.createVertexBuffer({context:viewer.scene.context,typedArray:positions,usage:Cesium.BufferUsage.STATIC_DRAW});constattributes=[{index:0,vertexBuffer:vertexBuffer,componentsPerAttribute:2,componentDatatype:Cesium.ComponentDatatype.FLOAT,}];constvertexArray=newCesium.VertexArray({context:viewer.scene.context,attributes:attributes});constvs=`...`constfs=`...`constshaderProgram=Cesium.ShaderProgram.fromCache({context:viewer.scene.context,vertexShaderSource:vs,fragmentShaderSource:fs,attributeLocations:{position:0,}})letposition=Cesium.Cartesian3.fromDegrees(105.41883,26.68244,0);letmodelMatrix=Cesium.Transforms.eastNorthUpToFixedFrame(position);letcommand=newCesium.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;}}letp=newMyPrimitive();viewer.scene.primitives.add(p);

代码中我们省略了着色器代码,缺省代码可以在前面章节中获取,因为这里我们只先准备一个基本骨架。因为我们最终需要将结果渲染在地球上的某个位置,所以需要指定一个从世界坐标原点到该位置的变换矩阵modelMatrix,这里可以自行定义该位置的坐标值。

instanceCount属性初始值为0,当vertexCount变化后记得更新instanceCount

数据纹理与索引

接下来我们按照 Splat Viewer 中的代码先将高斯数据打包到一张纹理图里面去,因为打包过程是在WebWorker中进行的,所以我们需要将数据传入Worker并注册消息事件接收结果。

letdataTexture=newCesium.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})});}letcommand=newCesium.DrawCommand({...uniformMap:{u_texture:()=>{returndataTexture;},}})...

上面的代码中我们先创建了一张默认的纹理图,当我们接收到WebWorker的打包结果后再覆写其内容。现在我们需要手动触发纹理打包操作,因为Splat Viewer中默认是排序时才会打包纹理,而我们现在并没有开启排序。

要触发纹理打包操作我们需要修改两处代码,一是在接收到网络请求的高斯数据后将其推送到WebWorker,二是在WebWorker中接收到数据后调用generateTexture函数进行纹理打包。

functioncreateWorker(self){...letsortRunning;self.onmessage=(e)=>{if(e.data.buffer){buffer=e.data.buffer;vertexCount=e.data.vertexCount;}elseif(e.data.vertexCount){vertexCount=e.data.vertexCount;}elseif(e.data.view){viewProj=e.data.view;throttledSort();}};}...while(true){...if(vertexCount>lastVertexCount){worker.postMessage({buffer:splatData.buffer,vertexCount:Math.floor(bytesRead/rowLength),});lastVertexCount=vertexCount;}}

现在纹理数据已经有了,但是我们还得为其准备一个默认的索引(排序),这里我们直接将0,1,2,3,4...设置为其默认的索引即可,索引数据需要通过Buffer来传递,所以我们需要修改一下前面默认的VAO

constinitialIndices=newUint32Array(x);for(leti=0;i<x;i++)initialIndices[i]=i;letindexBuffer=Cesium.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中的两个着色器代码片段直接拷贝进来替换vsfsfs代码基本不需要进行任何修改,而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 index=int(splatIndex); mat4 view=czm_modelView; mat4 projection=czm_projection; vec2 viewport=czm_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,当然也可以自定义混合模式。

letcommand=newCesium.DrawCommand({modelMatrix:modelMatrix,vertexArray:vertexArray,shaderProgram:shaderProgram,...})

示例效果可到 xt3d 官网 运行查看

现在颜色渲染基本上正确了,但是看上去是倾斜的,这是因为默认的Splat数据是Y轴朝上的,我们只需要将其绕X轴旋转一下即可。

letposition=Cesium.Cartesian3.fromDegrees(105.41883,26.68244,0);letmodelMatrix=Cesium.Transforms.eastNorthUpToFixedFrame(position);letrotation=Cesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(-90)));Cesium.Matrix4.multiply(modelMatrix,rotation,modelMatrix);

示例效果可到 xt3d 官网 运行查看

更多内容见 Cesium高级教程-教程简介

http://www.jsqmd.com/news/1067787/

相关文章:

  • 「铝镁双驱·智铸未来」马路科技携全域解决方案重磅登陆上海压铸展(7/15-17)
  • AI写论文的宝藏工具!这4款AI论文生成神器,高效完成论文
  • 手把手教你在 AMD 新本上部署本地 AI,从零开始不踩坑
  • [1189] 气球的最大数量
  • Unity 动画系统进阶:Root Motion根运动的开启与控制
  • Cesium 地形教程
  • 网易云音乐无损下载终极指南:3步永久保存你的歌单
  • 推荐信AI写作指南:如何快速为不同教授量身定制RL?
  • 日常中的小家电设备如何能够精准向适配器索要电源呢
  • AI Agent 出问题时,不要只看最终回答:一次请求级调试的思路
  • 数据看板设计:如何从“能看”变成“好用”
  • CNC编程效率低?麟思数控10秒出程序解困
  • 博尔塔拉黄金白银回收铂金旧金回收无套路门店 TOP 榜单 实地测评资料整理
  • Windows任务栏透明化:为什么传统方案失效而TranslucentTB能成功?
  • 苏州晟雅泰电子:关于车规级DS90UB941ASRTDRQ1的核心功能与参数
  • TRAC-seq:tRNA m7G修饰测序你与最前沿的m7G研究,只差一个TRAC-seq
  • 为什么选择biliTickerBuy:5个让你轻松搞定B站购票的核心功能
  • 算力付费的必然性:从通用幻觉到专业精准的范式升级
  • 我用 Python + AI 做了一套 SEO 优化工具:从关键词挖掘到排名监控,流量翻倍的秘密
  • 在 AMD 显卡上部署 SGLang 推理服务,配置细节全记录
  • 维护开源项目时,如何把一条 Issue 回复写清楚
  • 一文搞懂:常用设计模式实战——AI生成代码时代,设计模式为什么是开发者的“终极护城河”?
  • 萨科微slkor6月18日每日芯闻,国际芯闻:
  • 基于800V直流架构的数据中心微电网技术经济评估
  • 如何快速搭建跨平台游戏串流服务器:Sunshine终极配置指南
  • 基于“端-边-云”架构的工业互联网组建与运维实战(附避坑指南)
  • AI Shell对话OBS,存储管理“说”着搞定
  • Vulkan 还是 ROCm,AMD 显卡跑大模型的后端之争终结篇
  • 终极指南:三步免费解锁WeMod专业版功能 - Wand-Enhancer完整教程
  • 3分钟掌握ViGEmBus:让你的电脑秒变游戏手柄工厂 [特殊字符]