Skip to content
【转载】Cesium中使用Sampler3D,3D纹理,实现体渲染

Cesium中使用Sampler3D,3D纹理,实现体渲染

Cesium目前(20221231)还不支持直接使用3D纹理,但是其实内部已经可以WebGL2,而且内置常量也有3DTexture。所以,可以通过仿Texture,来实现3D纹理的使用。

在这里插入图片描述

修改Cesium源码

引入Cesium后,需要修改@cesium\engine\Source\Renderer\createUniform.js中的createUniform方法,在case gl.SAMPLER_CUBE后增加gl.SAMPLER_3D.

WebGL基本函数

就是常用的WebGL的纹理函数,不再赘述。只说明一下Texture3D的选用参数,目前仅使用了ALPHA一个通道,且数据类型对应为UNSIGNED_BYTE,即Uint8Array.

texImage3D

WebGL2RenderingContext.texImage3D(target, level, internalformat, width, height, depth, border, format, type, srcData)

  • target: gl.TEXTURE_3D

  • level: level of detail

  • internalformat: gl.ALPHA, discards the red, green and blue components and reads the alpha component.

  • width: width of the texture

  • height: height of the texture

  • depth: depth of the texture

  • border: Must be 0

  • format: ALPHA=ALPHA. The correct combinations with internalformat are listed in this table

  • type: gl.UNSIGNED_BYTE

Texture3D实现

js
import {
    Cartesian2,
    Cartesian3,
    Check,
    createGuid,
    defaultValue,
    defined,
    destroyObject,
    DeveloperError,
    CesiumMath,
    PixelFormat,
    ContextLimits,
    MipmapHint,
    PixelDatatype,
    Sampler,
    TextureMagnificationFilter,
    TextureMinificationFilter
} from "cesium";

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

    const lxs= gl.getParameter(gl.ACTIVE_TEXTURE);
    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(source.arrayBufferView)) {
            let arrayBufferView = source.arrayBufferView;
            gl.texImage3D(
                textureTarget,
                0,
                internalFormat,
                width,
                height,
                depth,
                0,//border
                pixelFormat,
                PixelDatatype.toWebGLConstant(pixelDatatype, context),
                arrayBufferView
            );
            initialized = true;
        }
    }
    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._depth = depth;
    this._dimensions = new Cartesian3(width, height, depth);
    this._hasMinmap = false;
    this._sizeInBytes = 4;
    this._preMultiplyAlpha = false;
    this._flipY = false;
    this._initialized = initialized;
    this._sampler = undefined;

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

// Creates a texture, and copies a subimage of the framebuffer to it.
Texture3D.fromFramebuffer = function (options) {
    options = defaultValue(options, defaultValue.EMPTY_OBJECT);
    Check.defined("options.context", options.context);

    const context = options.context;
    const gl = context._gl;

    const pixelFormat = defaultValue(options.pixelFormat, PixelFormat.RGB);
    const framebufferXOffset = defaultValue(options.framebufferXOffset, 0);
    const framebufferYOffset = defaultValue(options.framebufferYOffset, 0);
    const width = defaultValue(options.width, gl.drawingBufferWidth);
    const height = defaultValue(options.height, gl.drawingBufferHeight);
    const depth = defaultValue(options.depth, 128);
    const framebuffer = options.framebuffer;

    const texture=new Texture3D({
        context:context,
        width:width,
        height:height,
        pixelFormat:pixelFormat,
        source:{
            framebuffer:defined(framebuffer)?framebuffer:context.defaultFramebuffer,
            width:width,
            height:height,
            depth:depth,
        }
    });
    return texture;
};

Object.defineProperties(Texture3D.prototype,{
    id:{
        get:function(){
            return this._id;
        }
    },
    sampler:{
        get:function(){
            return this._sampler;
        },
        set:function(sampler){
            let minificationFilter=sampler.minificationFilter;
            let magnificationFilter=sampler.magnificationFilter;
            const context=this._context;
            const pixelFormat=this._pixelFormat;
            const pixelDatatype=this._pixelDatatype;

            const gl=context._gl;
            const target=this._textureTarget;

            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(target,this._texture);
            // 3D 纹理不设置放大,缩小,重采样
            gl.texParameteri(target,gl.TEXTURE_MIN_FILTER,minificationFilter);
            gl.texParameteri(target,gl.TEXTURE_MAG_FILTER,magnificationFilter);
            gl.bindTexture(target,null);

            this._sampler=sampler;
        }
    },
    dimensions:{
        get:function(){
            return this._dimensions;
        }
    },
    width:{
        get:function(){
            return this._width;
        }
    },
    height:{
        get:function(){
            return this._height;
        }
    },
    depth:{
        get:function(){
            return this._depth;
        }
    },
    _target:{
        get:function(){
            return this._textureTarget;
        }
    }
});

Texture3D.prototype.isDestroyed=function(){
    return false;
}

Texture3D.prototype.destory=function(){
    this._context._gl.deleteTexture(this._texture);
    return destroyObject(this);
};

export {Texture3D};

使用示例

自定义Primitive参考[这篇文章](Cesium Volumn 体渲染_Bro_Of_Nagi的博客-CSDN博客_cesium噪声)

修改getTexture

图元返回3D纹理,直接返回Texture3D

js
getTexture(context) {
        if(!this.texture){
            const texture_size = Math.ceil(Math.sqrt(this.data.length));
            this.texture=new Texture3D({
                width:size,
                height:size,
                depth:size,
                context: context,
                flipY: false,
                pixelFormat: Cesium.PixelFormat.ALPHA,
                pixelDataType: Cesium.ComponentDatatype.fromTypedArray(
                    this.data
                ),
                source: {
                    width: texture_size,
                    height: texture_size,
                    arrayBufferView: this.data,
                },
                sampler: new Cesium.Sampler({
                    minificationFilter: Cesium.TextureMinificationFilter.NEAREST,
                    magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
                }),
            })
        }

        return this.texture;
    }

添加自定义Primitive

构造好Uint8array数据data,代理几何体依旧是单位立方体

js
const options = {
    modelMatrix: primitive_modelMatrix,
    geometry_lxs: geometry,
    data: data,
    dim: dim_lxs
};

const lxs = viewer.scene.primitives.add(
    new lxs_primitive(options)
);

在这里插入图片描述

代码地址

其中texture3D分支为以上代码

本文转自 https://blog.csdn.net/yue1241630499/article/details/128508130?spm=1001.2014.3001.5502,如有侵权,请联系删除。

Updated at: