i3dm实例三维模型(翻译)

实例三维模型

贡献者

翻译

综述

实例三维模型 是用于高效流化和渲染大量三维模型(称为实例,模型通常很少有变化)的瓦片格式。最简单的例子就是不同地点的树木实例。每一个实例都索引同一个模型,但却拥有不同的属性,比如位置。在使用核心3D Tiles标准语言时,每一个实例就是一个要素。

除了树木,实例三维模型也适用于外部的要素,比如消防栓、窨井盖、路灯和交通灯等地物。也可以实例化内部要素,比如螺钉、阀门和电气外线出口

组合瓦片可以包含不同类型的实例模型,比如同时包含树木和交通灯等。

实例三维模型能很好的映射到ANGLE_instanced_arrays 拓展模块,用于WebGL的高效渲染。

结构

一个瓦片有文件头和紧随其后的文件主体部分构成。

图1: 实例三维模型结构(虚线代表可选填字段)

Header

头部有28字节,包含下列字段:

字段名

数据类型

描述

magic

4-byte ANSI string

"i3dm". 用来指定arraybuffer为实例三维模型瓦片

version

uint32

实例三维模型的版本号。目前为1

byteLength

uint32

整体瓦片的大小,以字节为单位

featureTableJSONByteLength

uint32

JSON格式的要素表格的长度,以字节为单位

featureTableBinaryByteLength

uint32

二进制要素表格的字节长度,如果featureTableJSONByteLength0,这个值也为0

batchTableJSONByteLength

uint32

JSON格式的批表格的长度,以字节为单位。0代表没有批表格

batchTableBinaryByteLength

uint32

二进制批表格的字节长度,如果batchTableJSONByteLength0,这个值也为0

gltfFormat

uint32

说明文件体内glTF字段的格式。0代表url1代表嵌入二进制glTF。参考下文的glTF部分。

featureTableJSONByteLength为0,或者没有glTF,则瓦片就不会被渲染。

文件体紧随文件头之后,包含三个字段:Feature Table, Batch Table和glTF.

要素表格

包含用于生成实例模型i3dm 语义的值

语法

实例语法

这类语法映射到用于生成实例要素的数组。这些数组的长度必须相等,而且与实例数目相同

如果一个语义A依赖另一个语义B,那么语义B必须定义。如果SCALE 和 SCALE_NON_UNIFORM两者都定义了,那么两者都会被应用。如果POSITION 和 POSITION_QUANTIZED 在实例中都定义了,会使用较高精度的POSITION。如果NORMAL_UP,NORMAL_RIGHT,NORMAL_UP_OCT32P和NORMAL_RIGHT_OCT32P在实例中都定义了,更高精度的NORMAL_UP和NORMAL_RIGHT会被使用。

语义

数据类型

描述

是否必填

POSITION

float32[3]

包含实例位置x,y,z笛卡尔坐标的三元素数组

:white_check_mark: 是的,除非定义了POSITION_QUANTIZED

POSITION_QUANTIZED

uint16[3]

包含实例位置x,y,z网格化笛卡尔坐标的三元素数组

:white_check_mark: 是的,除非定义了POSITION

NORMAL_UP

float32[3]

定义实例上方向的单位向量

:red_circle: 否,除非定义了NORMAL_RIGHT

NORMAL_RIGHT

float32[3]

定义实例右方向的单位向量

:red_circle: 否,除非定义了NORMAL_UP

NORMAL_UP_OCT32P

uint16[2]

定义32位精度的实例上方向单位向量(oct-encoded

:red_circle: 否,除非定义了NORMAL_RIGHT_OCT32P

NORMAL_RIGHT_OCT32P

uint16[2]

定义32位精度的实例上方向单位向量(oct-encoded,将Cartesian3变成Cartesian2),必须与上方向正交

:red_circle: 否,除非定义了NORMAL_UP_OCT32P

SCALE

float32

定义缩放比例的数字(每个轴都相同)

:red_circle: 

SCALE_NON_UNIFORM

float32[3]

定义缩放比例的三元数组(三个轴不同)

:red_circle: 

BATCH_ID

unit16

batchId 可以用于从Batch Table中获取 元数据

:red_circle: 

全局语义

全局语义应用于所有实例

语义

数据类型

描述

是否必填

INSTANCES_LENGTH

uint32

实例的数目,用于生成实例属性中的数组对象

:white_check_mark: 

QUANTIZED_VOLUME_OFFSET

float32[3]

定义网格体的(QUANTIZED_VOLUME)偏移量的三元数组

:red_circle:否,除非定义了POSITION_QUANTIZED

QUANTIZED_VOLUME_SCALE

float32[3]

定义网格体的(QUANTIZED_VOLUME)缩放比例的三元数组

:red_circle:否,除非定义了POSITION_QUANTIZED

实例方向

实例的方向由一组标准正交基(上方向和右方向)的单位向量定义。如果NORMAL_UP和NORMAL_RIGHT,或者NORMAL_UP_OCT32P和NORMAL_RIGHT_OCT32P 没有定义,实例默认会根据tileset的transform属性和实例的WGS84经纬度坐标,使用东北上参考系的方向。法向量就是tileset中transform属性的逆转置矩阵。

 标准基中的x 向量映射为转换后基的右向量。y向量则是映射为上向量。z 向量为前向量。但是前向量通常都是上向量和右向量的叉积,所以此处省略。

图 2:标准基中的盒子模型

box standard basis

图 3:旋转后的盒子模型

box rotated basis

Oct-encoded法向量

如果实例没有定义NORMAL_UP 和 NORMAL_RIGHT,它的方向可以定义在oct-encoded法向量,NORMAL_UP_OCT32P 和 NORMAL_RIGHT_OCT32P之中。这种用oct-encoding定义上方向和右方向的方法在 A Survey of Efficient Representations of Independent Unit Vectors by Cigolle et
al.
中详细描述了。在Cesium的AttributeCompression 模块可以找到这种编码和解码的应用实例代码。

实例位置

在应用其他transform矩阵之前,POSITION 定义了实例的位置。

量化位置

如果实例没有POSITION 属性,它的位置也可以定义在POSITION_QUANTIZED ,该属性定义了相对于网格体的位置信息。 如果POSITION 和 POSITION_QUANTIZED 都没有定义,那就不会创建实例。

网格体由offset 和 scale 定义,用于将网格化的位置映射到模型空间中。

图 4:基于offset 和 scale 的网格体

quantized volume

Offset存储在全局语义QUANTIZED_VOLUME_OFFSET中,scale 存储在全局语义QUANTIZED_VOLUME_SCALE中。如果两者没有定义,那POSITION_QUANTIZED 不能使用。

网格化位置可以通过以下公式映射到模型空间:

POSITION = POSITION_QUANTIZED * QUANTIZED_VOLUME_SCALE +
QUANTIZED_VOLUME_OFFSET

实例缩放

通过SCALE 和 SCALE_NON_UNIFORM 语义可以实现缩放。 SCALE 用于各轴缩放比例相等的缩放,而SCALE_NON_UNIFORM 用于x, y, 和 z 轴缩放比例相互独立的缩放。

例子

下面的例子展示了如果为要素表格生成JSON和二进制buffer。

只有位置信息

在这个小例子中,我们在单位长度的方格四个顶点各放置了一个实例,方向默认。

var featureTableJSON = {  
    INSTANCES\_LENGTH : 4,
    POSITION : {
        byteOffset : 0
    }
};
var featureTableBinary = new Buffer(new Float32Array([  
    0.0, 0.0, 0.0,
    1.0, 0.0, 0.0,
    0.0, 0.0, 1.0,
    1.0, 0.0, 1.0
]).buffer);

网格化位置和 Oct-Encoded法向量

在这个例子中,四个实例的朝向由oct-encoded的上向量 [0.0, 1.0, 0.0] 和右向量 [1.0, 0.0, 0.0]定义,被放置在x,z轴范围为-250到250的网格体中

var featureTableJSON = {  
    INSTANCES_LENGTH : 4,
    QUANTIZED_VOLUME_OFFSET : [-250.0, 0.0, -250.0],
    QUANTIZED_VOLUME_SCALE : [500.0, 0.0, 500.0],
    POSITION_QUANTIZED : {
        byteOffset : 0
    },
    NORMAL_UP_OCT32P : {
        byteOffset : 24
    },
    NORMAL_RIGHT_OCT32P : {
        byteOffset : 40
    }
};

var positionQuantizedBinary = new Buffer(new Uint16Array([  
    0, 0, 0,
    65535, 0, 0,
    0, 0, 65535,
    65535, 0, 65535
]).buffer);

var normalUpOct32PBinary = new Buffer(new Uint16Array([  
    32768, 65535,
    32768, 65535,
    32768, 65535,
    32768, 65535
]).buffer);

var normalRightOct32PBinary = new Buffer(new Uint16Array([  
    65535, 32768,
    65535, 32768,
    65535, 32768,
    65535, 32768
]).buffer);

var featureTableBinary = Buffer.concat([positionQuantizedBinary, normalUpOct32PBinary, normalRightOct32PBinary]);  

批表格Batch Table

批表格是一个包含JSON的 UTF-8格式字符串。它紧跟着文件,可以用TextDecoder JavaScript
API从arraybuffer中提取出来,然后用 JSON.parse转换成JavaScript对象。

对象中每个属性都是一个数组,数组长度与header.batchLength相等。数组元素可以是任意JSON数据类型,包括对象和数组。元素也可以是null。

一个实例的batchId 用于获取每个数组中对应元素的属性。比如,下面实例中包含两个实例的属性:

{
    "id" : ["unique id", "another unique id"],
    "displayName" : ["Tree species", "Another tree species"],
    "yearPlanted" : [1999, 2003],
    "location" : [{"x" : 1, "y" : 2}, {"x" : 3, "y" : 4}]
}

 batchId = 0 的实例,属性如下:

id[0] = 'unique id';  
displayName[0] = 'Tree species';  
yearBuilt[0] = 1999;  
location[0] = {x : 1, y : 2};  

batchId = 1 的属性为:

id[1] = 'another unique id';  
displayName[1] = 'Another tree species';  
yearBuilt[1] = 2003;  
location[1] = {x : 3, y : 4};  

glTF

glTF字段写在batch table之后(如果header.batchTableByteLength为0,则在文件头之后)。

glTF 是WebGL的实时数据格式,Binary glTF 是glTF的拓展,为glTF定义了一个二进制容器。实例三维模型使用glTF
1.0标准和KHR_binary_glTF 拓展.

header.gltfFormat 定义glTF字段的格式。0代表glTF字段是

  • 一个utf-8字符串,包含glTF模型的url

当header.gltfFormat是1时,glTF字段为

  • 包含二进制glTF的二进制大对象

以上两个实例中,header.gltfByteLength 内包含的都是glTF字段的字节长度。

文件拓展名

.i3dm

MIME类型

application/octet-stream

资源

  1. A Survey of Efficient Representations of Independent Unit Vectors by
    Cigolle et al.

  2. Mesh Geometry Compression for Mobile Graphics by Jongseok Lee et
    al.

  3. Cesium AttributeCompression module
    for oct-encoding

应用实例

Cesium

由经纬度和高度生成方向向量

var position = Cartesian3.fromRadians(longitude, latitude, height);  
// Compute the up vector
var up = new Cartesian3();  
var transform = Transforms.eastNorthUpToFixedFrame(position);  
var rotation = new Matrix3();  
Matrix4.getRotation(transform, rotation);  
// In east-north-up, the up vector is stored in the z component
Matrix3.multiplyByVector(rotation, Cartesian3.UNIT\_Z, up);  
// Compute the right and forward vectors
var right = new Cartesian3();  
var forward = Cartesian3.clone(Cartesian3.UNIT\_Z);  
// You can change the orientation of a model by rotating about the y-axis first
var orient = new Matrix3();  
var orientAngle = CesiumMath.fromDegrees(90.0);  
Matrix3.fromRotationY(orientAngle, orient);  
Matrix3.multiplyByVector(orient, forward, forward);  
// Cross up and forward to get right
Cartesian3.cross(up, forward, right);  
Cartesian3.normalize(right, right);  

为实例构建一个模型矩阵

// Cross right and up to get forward
var forward = new Cartesian3();  
Cartesian3.cross(right, up, forward);  
// Place the basis into a rotation matrix
var rotation = new Matrix3();  
Matrix3.setColumn(rotation, 0, right, rotation);  
Matrix3.setColumn(rotation, 1, up, rotation);  
Matrix3.setColumn(rotation, 2, forward, rotation);  
// Get the scale
var scale = Matrix3.IDENTITY.clone();  
if (defined(uniformScale)) {  
    Matrix3.multiplyByScalar(scale, uniformScale, scale);
}
if (defined(nonUniformScale)) {  
    Matrix3.multiplyByVector(scale, nonUniformScale, scale);
}
// Generate the model matrix
var modelMatrix = new Matrix4();  
Matrix4.fromTranslationRotationScale(new TranslationRotationScale(position, rotation, scale),modelMatrix);  
浙ICP备16041529号-1