Cesium离屏渲染深度图实验

一直 觉得人家都有自己的博客好酷啊

自己也想写一个

那就从CESIUM的离屏渲染开始吧 

其实这个功能一直想做了 对于测试深度图的正确性来说应该是很有用的

而且也很酷嘛 查了很久网上好像也没有什么公开的代码 我来试试吧

首先在html界面设置一个固定在右下角的canvas用来显示渲染结果

我前端基础不是很好 目前就是主打一个凑合着用

<style>
     #outputCanvas {
            position: absolute;
            width: 20%;
            height: 20%;
            bottom: 0;
            right: 0;
            z-index: 999;
            background: black;
            border: solid blue 1px;
        }
</style>
 <canvas id="outputCanvas"></canvas>

初始化Cesium 加载3Dtiles模型这些步骤就略过吧

直接进入到渲染阶段

首先是深度图的获取问题 后处理阶段会自己有一个深度图 但是不知道到底是怎么获取的

一直很好奇 网上很多关于CESIUM获取当前场景深度图的问题都是没有答案

Cesium好像也没有什么公开的接口让人拿到他的深度图

只能自己想办法拿 感觉从后处理渲染结果拿是一个方法 但总感觉很蠢

明明已经有深度图了 想办法拿到就行

获取深度图的代码某些原因不能分享 说一下大概思路吧

直接拿cesium底层的DrawCommand命令 自己新建一个FBO缓存区 

执行这个渲染命令 把结果存到FBO中

就拿到了当前场景的深度图

不过这个深度图 和当前视椎体挂钩 很多时候并不是那么的直观 还需要研究

拿到的深度图是存在FBO中的 要把这个FBO输出到Canvas中

function 	saveTextureToImage(framebuffer,texture) {
    const context = viewer.scene.context;
    // Create a framebuffer to read texture

    // Create a canvas
    const canvas = document.getElementById('outputCanvas');
    canvas.width = texture.width;
    canvas.height = texture.height;
    const width=texture.width;
    const height=texture.height;
    const context2d = canvas.getContext('2d');
    const imageData = context2d.createImageData(texture.width, texture.height);

    // Read pixels from framebuffer
    var pixels = viewer.scene.context.readPixels({
        x: 0,
        y: 0,
        width: width,
        height: height,
        framebuffer: framebuffer,
      });

      let imgData = new ImageData(new Uint8ClampedArray(pixels), width, height); 
      let flippedImgData = new ImageData(width, height);

      // 遍历原始图像数据,按照上下翻转的方式将像素值填充到新的 ImageData 对象中
      for (let y = 0; y < height; y++) {
          for (let x = 0; x < width; x++) {
              // 计算原始图像数据和翻转后图像数据的索引
              let originalIndex = (y * width + x) * 4;
              let flippedIndex = ((height - 1 - y) * width + x) * 4;
      
              // 将原始图像数据的像素值填充到翻转后图像数据中
              flippedImgData.data[flippedIndex] = imgData.data[originalIndex];
              flippedImgData.data[flippedIndex + 1] = imgData.data[originalIndex + 1];
              flippedImgData.data[flippedIndex + 2] = imgData.data[originalIndex + 2];
              flippedImgData.data[flippedIndex + 3] = imgData.data[originalIndex + 3];
          }
      }
      for (let i = 0; i < flippedImgData.data.length; i += 4) {
        let gray = 0.299 * flippedImgData.data[i] + 0.587 * flippedImgData.data[i + 1] + 0.114 * flippedImgData.data[i + 2];
        flippedImgData.data[i] = gray;
        flippedImgData.data[i + 1] = gray;
        flippedImgData.data[i + 2] = gray;
    }
      
      // 绘制翻转后的图像数据到画布上
      context2d.putImageData(flippedImgData, 0, 0);
    // Draw the image data to the canvas
   

    // Export the canvas to an image

    // Open a new window/tab and display the image
    
    // Cleanup
    framebuffer.destroy();
}

这样深度图就画在画布上了

下一步要考虑的是什么时候画的问题 首先想到的是放到渲染循环里 每渲染一帧 canvas对应更新

在3dtiles加载完成后的事件里增加渲染循环事件

      success:function(data){
            let mainJson= eval(data);//解析json对象
            for(let i=0;i<mainJson.RECORDS.length;i++){
            // for(let i=0;i<1;i++){
  
               let m=addTileset(mainJson.RECORDS[i].ModelUrl);
               viewer.zoomTo(m);
               
               viewer.scene.preRender .addEventListener(() => {
                //console.log('postRender')
               let result=getDepth();
               
               saveTextureToImage(result.framebuffer,result.Texture)
              })

 这样结果确实是出来了 但是帧数很低 展示一部分截图 帧数太低了 而且深度图不稳定

  FPS=FPS+1
               if(FPS%10==0)
               {
               let result=getDepth();
               saveTextureToImage(result.framebuffer,result.Texture)
               }

统计帧数并每十帧刷新一次 帧数提高很多 但感觉不是治本之策

既然他每一帧都会获取 我只是额外获取一下 为什么会这么影响帧数 难道没有办法直接拿到那个深度图吗 而且这个深度只有平视的时候能看见 俯视的时候就没了 很奇怪的

更改渲染命令中片元着色器的部分 不用cesium的打包 直接输出为rgb值

显示效果更好

`#version 300 es
    #ifdef GL_FRAGMENT_PRECISION_HIGH
        precision highp float;
        precision highp int;
    #else
        precision mediump float;
        precision mediump int;
        #define highp mediump
    #endif
    
    #define OES_texture_float_linear
    
    #define OES_texture_float
    
    
    #line 0
    layout(location = 0) out vec4 out_FragColor;
    
    
    
    
    
    
    
    
    
    
    vec4 czm_packDepth(float depth)
    {
        
        
        vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * depth;
        enc = fract(enc);
        enc -= enc.yzww * vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 0.0);
        return enc;
    }
    
    
    
    #line 0
    uniform highp sampler2D u_depthTexture;
    
    in vec2 v_textureCoordinates;
    
    void main()
    {
        float z=texture(u_depthTexture, v_textureCoordinates).r;
        out_FragColor =vec4(z,z,z,1.0);
       
    }`

另外  开启对数深度的情况下深度图的获取更加稳定 原因不知道

最终效果如下:

但是这里用的是同一个摄像机视角 我以为并不能叫真正的离屏渲染 

后续要研究不同视角的离屏 渲染  已经cesium 的深度图 一些技术

先到这吧 

第一篇博客 不清楚的地方请见谅

主要是我也查了很多 国内外论坛上关于这个的讨论很少

要不就是语焉不详的  不喜欢这样 写一篇 也算记录一下

看到很多大佬写的博客 很羡慕他们那么知识渊博 好像啥都懂

希望大家都早日能成为大牛

                                                                                                                                        祝好

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
离屏渲染是一种将图形渲染到与屏幕不直接关联的缓冲区的技术。在Cesium中,离屏渲染可以通过使用WebGL的帧缓冲对象(Framebuffer Object,FBO)来实现。 离屏渲染Cesium中的应用场景包括: 1. 生成纹理:可以将场景渲染到一个纹理中,然后将该纹理用于其他的图形操作,例如后期处理、投影等。 2. 阴影计算:可以将场景渲染到一个深度纹理中,然后使用该深度纹理来计算阴影效果。 3. 屏幕空间反射(Screen Space Reflection,SSR):可以将场景渲染到一个颜色纹理和一个法线纹理中,然后使用这些纹理来计算屏幕空间反射效果。 具体实现离屏渲染的步骤如下: 1. 创建一个帧缓冲对象(Framebuffer Object,FBO)。 2. 创建一个纹理附件(Texture Attachment),用于存储渲染结果。 3. 将帧缓冲对象绑定到渲染管线中。 4. 渲染场景到帧缓冲对象中的纹理附件。 5. 解绑帧缓冲对象,将渲染结果用于其他的图形操作。 以下是一个使用Cesium进行离屏渲染的示例代码: ```javascript // 创建帧缓冲对象 var framebuffer = new Cesium.Framebuffer({ context: viewer.scene.context, colorTextures: [new Cesium.Texture({ context: viewer.scene.context })], depthTexture: new Cesium.Texture({ context: viewer.scene.context, format: Cesium.PixelFormat.DEPTH_COMPONENT }) }); // 将帧缓冲对象绑定到渲染管线中 viewer.scene.frameState.framebuffer = framebuffer; // 渲染场景到帧缓冲对象中的纹理附件 viewer.scene.render(); // 解绑帧缓冲对象 viewer.scene.frameState.framebuffer = undefined; // 获取渲染结果的纹理 var resultTexture = framebuffer.getColorTexture(0); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值