一、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

最低0.47元/天 解锁文章
449

被折叠的 条评论
为什么被折叠?



