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实现
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
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
,代理几何体依旧是单位立方体
const options = {
modelMatrix: primitive_modelMatrix,
geometry_lxs: geometry,
data: data,
dim: dim_lxs
};
const lxs = viewer.scene.primitives.add(
new lxs_primitive(options)
);
代码地址
其中texture3D分支为以上代码