Cesium高阶学习八、体渲染

一、Cesium如何实现体渲染

如果您还不知道什么是体渲染,可以参考体绘制 ,体渲染常用来绘制比如云、雾这种空间连续的事物。体渲染的实现方法有很多,Cesium目前也有自带的体渲染 Cesium官网体渲染示例
在这里插入图片描述

本章我们介绍一种比较通用的体渲染方法(3D纹理+RayMarching+代理几何),这种方式在ThreeJS中已有示例可以参考 柏林噪声云朵
1、3D纹理:也可称为体纹理,将体数据存储到一张3D纹理图中,在着色器中对纹理进行采样,获取对应的体数据。
2、RayMarching:即光线步进,一种对3D纹理进行采样的方法,其原理是在每个像素上计算一条光线,当光线进入体纹理范围内后,将光线细分成更小的光线片段进行步进迭代,每次步进时计算相应的强度信息。最终迭代结束后,将所有步进结果进行叠加计算,得到像素的颜色。
在这里插入图片描述

3、代理几何:通过光线步进采样体纹理,最后叠加的结果需要显示到一个载体上,我们一般定义一个几何,用于承载显示结果,称为代理几何。

4、在Cesium中实现:我们知道,Cesium底层是基于WebGL的,而WebGL已经支持3D纹理,但是Cesium并没有开放相关接口,所以需要修改Cesium源码以支持3D纹理。代理几何需要同时操作顶点着色器和片元着色器,所以有两种方式,一是Primitive+Appearance,二是DrawCommand自定义Primitive,两种方式在前面的相关章节中我们都有介绍。

二、准备工作

1、定义Texture3D类

Cesium中目前没有3D纹理类,我们可以参照Cesium的2D纹理类Texture定义一个3D纹理类Texture3D,我们先看一下Texture类的源码。(源码位于 packages\engine\Source\Renderer\Texture.js)

function Texture(options) {
   
  options = defaultValue(options, defaultValue.EMPTY_OBJECT);

  ...
  if (defined(source)) {
   
    if (defined(source.arrayBufferView)) {
   
      gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);

      // Source: typed array
      let arrayBufferView = source.arrayBufferView;

      if (isCompressed) {
    
        ...
      } else {
    
        gl.texImage2D(
          textureTarget,
          0,
          internalFormat,
          width,
          height,
          0,
          pixelFormat,
          PixelDatatype.toWebGLConstant(pixelDatatype, context),
          arrayBufferView
        );  
      }
    } 
  } else {
   
    gl.texImage2D(
      textureTarget,
      0,
      internalFormat,
      width,
      height,
      0,
      pixelFormat,
      PixelDatatype.toWebGLConstant(pixelDatatype, context),
      null
    );
    initialized = false;
  }
  gl.bindTexture(textureTarget, null);

  ...
  this._id = createGuid();
  this._context = context;
  this._textureFilterAnisotropic = context._textureFilterAnisotropic;
  this._textureTarget = textureTarget;
  this._texture = texture;
  this._internalFormat = internalFormat;
  this._pixelFormat = pixelFormat;
  this._pixelDatatype = pixelDatatype;
  this._width = width;
  this._height = height;
  this._dimensions = new Cartesian2(width, height);
  this._hasMipmap = false;
  this._sizeInBytes = sizeInBytes;
  this._preMultiplyAlpha = preMultiplyAlpha;
  this._flipY = flipY;
  this._initialized = initialized;
  this._sampler = undefined;

  this.sampler = defined(options.sampler) ? options.sampler : new Sampler();
}

最核心的参考代码为:

gl.texImage2D(
          textureTarget,
          0,
          internalFormat,
          width,
          height,
          0,
          pixelFormat,
          PixelDatatype.toWebGLConstant(pixelDatatype, context),
          arrayBufferView
        );  
      }

3D纹理类只需要将gl.texImage2D方法换为gl.texImage3D,然后传入对应参数即可:

function Texture3D(options) {
   
    options = defaultValue(options, defaultValue.EMPTY_OBJECT);

    Check.defined("options.context", options.context);

    const context = options.context;
    let width = options.width;
    let height = options.height;
    let depth = options.depth;
    let source = options.source;

    const pixelFormat = defaultValue(options.pixelFormat, PixelFormat.RGBA);
    const pixelDatatype = defaultValue(options.pixelDataType, PixelDatatype.UNSIGNED_BYTE);
    const internalFormat = PixelFormat.toInternalFormat(pixelFormat, pixelDatatype, context);

    if (!defined(width) || !defined(height) || !defined(depth)) {
   
        throw new DeveloperError(
            "options requires a source field to create an 3d texture. width or height or dimension fileds"
        )
    }

    Check.typeOf.number.greaterThan("width", width, 0);

    if (width > ContextLimits.maximumTextureSize) {
   
        throw new DeveloperError(
            "width must be less than or equal to the maximum texture size"
        );
    }

    Check.typeOf.number.greaterThan("height", height, 0);

    if (height > ContextLimits.maximumTextureSize) {
   
        throw new DeveloperError(
            "height must be less than or equal to the maximum texture size"
        );
    }

    Check.typeOf.number.greaterThan("dimensions", depth, 0);

    if (depth > ContextLimits.maximumTextureSize) {
   
        throw new DeveloperError(
            "dimension must be less than or equal to the maximum texture size"
        );
    }

    if (!PixelFormat.validate(pixelFormat)) {
   
        throw new DeveloperError("Invalid options.pixelFormat.");
    }

    if (!PixelDatatype.validate(pixelDatatype)) {
   
        throw new DeveloperError("Invalid options.pixelDatatype.");
    }

    let initialized = true;
    const gl = context._gl;
    const textureTarget = gl.TEXTURE_3D;
    const texture = gl.createTexture();


    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(textureTarget, texture);
    let unpackAlignment = 4;
    if (defined(source) && defined(source.arrayBufferView)) {
   
        unpackAlignment = PixelFormat.alignmentInBytes(pixelFormat, pixelDatatype, width);//??
    }

    gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment);
    gl.pixelStorei(
        gl.UNPACK_COLORSPACE_CONVERSION_WEBGL,
        gl.BROWSER_DEFAULT_WEBGL
    );

    if (defined(source)) {
   
        if (defined
Cesium中实现渲染需要使用VolumeRendering模块。该模块使用积数据集(包括密度值和颜色值)来生成渲染效果。 以下是一个简单的示例代码,演示如何在Cesium中实现渲染: ```javascript var viewer = new Cesium.Viewer('cesiumContainer'); // 创建积数据集 var volume = new Cesium.Volume({ slices: [ // 每个切片包含一个二维密度数组和一个二维颜色数组 { densities: [0.1, 0.2, 0.3, ...], colors: [new Cesium.Color(1.0, 0.0, 0.0, 0.1), new Cesium.Color(0.0, 1.0, 0.0, 0.2), new Cesium.Color(0.0, 0.0, 1.0, 0.3), ...] }, { densities: [0.4, 0.5, 0.6, ...], colors: [new Cesium.Color(1.0, 0.0, 0.0, 0.4), new Cesium.Color(0.0, 1.0, 0.0, 0.5), new Cesium.Color(0.0, 0.0, 1.0, 0.6), ...] }, ... ], dimensions: new Cesium.Cartesian3(100, 100, 100) }); // 创建渲染器 var renderer = new Cesium.VolumeRenderer({ volume: volume, transferFunction: new Cesium.TransferFunction({ colorMap: Cesium.ColorMapType.HOT }), clippingPlanes: new Cesium.ClippingPlaneCollection({ planes: [ new Cesium.ClippingPlane(new Cesium.Cartesian3(1.0, 0.0, 0.0), 10.0), new Cesium.ClippingPlane(new Cesium.Cartesian3(-1.0, 0.0, 0.0), 10.0), new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 1.0, 0.0), 10.0), new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, -1.0, 0.0), 10.0), new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, 1.0), 10.0), new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -1.0), 10.0) ] }) }); // 将渲染器添加到场景中 viewer.scene.primitives.add(renderer); ``` 以上示例代码创建了一个积数据集,并使用HOT颜色映射和六个裁剪面创建了一个渲染器。你可以根据需要更改密度值、颜色值、颜色映射和裁剪面来生成不同的渲染效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cesium进阶学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值