索引
HT for 3D Web
预定义了多种三维模型,并可通过建模手册介绍的API
方式构建更多样式的模型,
同时HT
还提供了导入OBJ的3D
模型格式文件的功能。
导入OBJ
格式功能需要引入ht-obj.js
的插件扩展包,本手册的大部分例子由于需要读取OBJ
文件,浏览器存在跨域安全的限制,
因此需要通过Web
方式发布来阅读本手册,或者修改浏览器的参数,例如对于Chrome
浏览器可通过增加
--allow-file-access-from-files的启动参数。
OBJ 是一种3D
模型文件格式,几乎所有主流3D
建模工具,
如Blender
、3ds Max
和Maya
都支持OBJ
格式的导出。
OBJ
文件一般以.obj
后缀名标示,描述的是模型顶点、面以及贴图坐标等几何模型相关信息;而模型的贴图图片以及颜色等材质信息,
则由另外的MTL
材质文件描述,一般以.mtl
后缀名标示。
OBJ
文件示例片段如下,v
代表顶点信息,f
代表面信息,usemtl
代表以下面描述模型都将采用外部MTL
文件描述的material3
材质信息:
v 1.187283 0.016532 0.652852
v 1.187283 0.001827 1.045301
v 1.187283 0.155480 0.618752
v 1.187283 0.106104 1.046487
v 1.187283 0.330175 0.640612
v 1.187283 0.209969 1.085557
v 1.186590 1.499776 1.191882
usemtl material3
f 9918 9919 9920 9921
f 9919 9922 9923 9920
f 9922 9924 9925 9923
f 9924 9926 9927 9925
f 9926 9928 9929 9927
f 9928 9930 9931 9929
MTL
文件示例片段如下,材质material3
透明度d
为0.5
,kd
代表diffuse
颜色为[0.58 0.58 0.58]
,贴图路径为/SmokeAlarm.jpg
newmtl material3
d 0.5
Kd 0.58 0.58 0.588
map_Kd /SmokeAlarm.jpg
ht.Default.parseObj(objText, mtlText, params)
函数用于解析obj
和mtl
文件,
解析后返回的map
结构json
对象中,每个材质名对应一个模型信息,
模型信息格式为建模手册介绍的HT
自定义的模型格式标准。
objText
:OBJ
格式的文本内容mtlText
:MTL
格式的文本内容,无材质信息也可传入null
params
:JSON
格式控制参数param
参数说明如下:
mat
:矩阵变化参数,可对模型进行矩阵变化后导入,一般通过ht.Default.createMatrix
函数构建变化矩阵s3
:大小变化参数,格式为[sx, sy, sz]
r3
:旋转变化参数,格式为[rx, ry, rz]
rotationMode
:旋转模式参数,可取值以下值:xyz
:先进行x
轴旋转,再进行y
轴旋转,最后进行z
轴旋转xzy
:先进行x
轴旋转,再进行z
轴旋转,最后进行y
轴旋转yxz
:先进行y
轴旋转,再进行x
轴旋转,最后进行z
轴旋转yzx
:先进行y
轴旋转,再进行z
轴旋转,最后进行x
轴旋转zxy
:先进行z
轴旋转,再进行x
轴旋转,最后进行y
轴旋转zyx
:先进行z
轴旋转,再进行y
轴旋转,最后进行x
轴旋转t3
:位置变化参数,格式为[tx, ty, tz]
center
:模型是否居中,默认为false
,设置为true
则会移动模型位置使其内容居中cube
:是否将模型缩放到单位1
的尺寸范围内,默认为false
part
:默认false
,将会按照mtl
名分组,即相同材质都将被批量的组在一起;改成true
则按照group
或name
信息来进行分组。part效果见loadObj函数ignoreMtls
:忽略部分材质,默认为空代表都读取,格式为['material2', 'material3']
数组,忽略的材质不出现在返回值中ignoreTransparent
:忽略材质透明度d
属性,默认为false
代表读取d
值,读取该属性将影响返回值的transparent
和opacity
属性ignoreColor
:忽略材质颜色kd
属性,默认为false
代表读取kd
值,该属性在返回值中名称为color
ignoreImage
:忽略材质贴图map_kd
属性,默认为false
代表读取map_kd
值,该属性在返回值中名称为image
ignoreNormal
:忽略法线向量,默认为false
会读取法线向量信息,设置为true
则忽略不读取法线向量信息prefix
:图片路径前缀,即在map_kd
值之前增加的前缀,如果是相对路径则以加载obj
的html
页面的路径为参考flipY
:图片上下翻转,默认为false
,遇到模型图片上下颠倒的情况可设置该参数为true
flipFace
:默认为false
,设置为true
则代表翻转所有模型的面,也就是原来的正面变成反面,原来的反面变成正面reverseFlipMtls
:模型反面显示正面一样的内容,传入*
代表适用于全部材质,传入['material2', 'material3']
数组格式代表适用于指定材质shape3d
:如果指定了shape3d
名称,则HT
将自动将加载解析后的所有材质模型构建成数组的方式,以该名称进行注册MTL
格式还有诸多参数,目前HT
仅支持d
、kd
和map_kd
这三个分别代表透明度、颜色和贴图的参数,
关于OBJ
和MTL
的格式标准可参考这里。
map_Kd -o 0.1000 0.1200 0.0000 -s 45.0000 20.0000 0.0000 project/images/floor.jpg
如上所示的贴图参数map_Kd
,其中-o
相当于uv.offset
的贴图偏移参数;-s
相当于uv.scale
的贴图倍数参数。
对于-o
和-s
这两个属性HT
只读取前面两个参数,忽略第三个参数。图片路径会自动增加prefix
参数的前缀,
如果图片为相对路径,是相对于最终运行html
页面的相对路径,也可以设置成注册到ht.Default.setImage(name, ...)
中的图片名
如果通过ignoreNormal
设置为true
忽略法线向量,或导出的OBJ
文件不包含法线向量信息时,HT
会自动构建对应ns
法线向量信息,
但为实现特殊的表面效果,或通过较少的顶点实现平滑的界面渲染效果时常需要指定每个顶点的法线向量,
可参考Phong shading进行理解,
概述章节例子可发现,ignoreNormal
设置为true
的模型表面较为突兀棱角分明,不如读取法线向量的模型平滑。
模型几何变换顺序分别为:mat
-> s3
-> r3
-> t3
-> center
-> cube
。
一般设置s3
、r3
和t3
就能满足大部分需求,如需更复杂的矩阵变换,可通过ht.Default.createMatrix
函数构建出mat
矩阵参数,
采用mat
参数的情况常用于模型形状位置需要与数据模型值绑定的情况:
modelMap.pointer.mat = {
func: function(data){
var start = Math.PI * 0.736,
range = Math.PI * 1.46,
angle = start - range * data.a('value') / 100;
return ht.Default.createMatrix([
{ t3: [0, -75, 0] },
{ r3: [Math.PI/4, 0, 0] },
{ r3: [0, 0, angle] },
{ r3: [-Math.PI/4, 0, 0] },
{ t3: [0, 75, 0] }
]);
}
};
以上代码意思是对pointer
的表计指针模型,先通过t3: [0, -75, 0]
沿着y
轴向下移动75
,使得指针旋转点位于坐标原点上,
之后进行r3: [Math.PI/4, 0, 0]
沿着x
轴旋转Math.PI/4
的弧度,使得指针直立在xy
平面上,
然后进行r3: [0, 0, angle]
沿着z
轴旋转angle
值的角度,最后再通过r3: [-Math.PI/4, 0, 0]
和t3: [0, 75, 0]
将指针旋转和移动回原始位置,从而实现指针旋转角度与data.a('meter.value')
的数据绑定。
通过
ht.Default.parseObj
解析后的map
结构json
对象中,每个材质名对应一个模型信息,如果cube
参数为true
, 则返回的每个模型信息上将具有rawS3
的特殊参数,同时传入的param
参数也会增加rawS3
属性信息, 该值为进行单位立体化前所有模型组合的最大尺寸范围,因此每个模型中的rawS3
值是一样的。
要将OBJ
解析后的模型信息绑定到图元,需先调用 建模手册 中
模型注册
章节介绍的ht.Default.setShape3dModel(name, model)
函数进行注册,之后图元只需将style
的shape3d
属性设置为注册的名称。
以上例子构建了两辆摩托车模型,他们都读取至相同的OBJ
文件信息,名称为Separate Scooter
的摩托车,是有由一堆的Node
,
相互host
吸附成环状,当用户拖动旋转操作时感觉像一个整体,这种方式下每个部分可独立选中,染色和隐藏等操作。
如果需要整个摩托车模型上仅对应一个Node
图元,则可采用例子中标注为One Node
的摩托车方式,将所有材质对应的模型融合成一个array
数组,
通过ht.Default.setShape3dModel('scooter', array)
进行注册,这样scooter
名称的模型将具有完整的OBJ
模型信息,
参见建模手册的模型组合章节。
for(var name in modelMap){
var model = modelMap[name];
var shape3d = 'scooter:' + name;
ht.Default.setShape3dModel(shape3d, model);
array.push(model);
var node = new ht.Node();
node.s({
'shape3d': shape3d
});
node.setHost(lastNode);
...
}
ht.Default.setShape3dModel('scooter', array);
var node = new ht.Node();
node.s('shape3d', 'scooter');
Node
的s3
大小参数会影响模型的最终呈现效果,最终呈现的模型大小将由导入的OBJ
几何模型大小乘以对应的Node
的s3
大小,
也就是说如果导入的OBJ
模型的rawS3
大小为[10, 20, 30]
的尺寸,则如果Node
的s3
为[10, 5, 3]
,
则最终呈现于界面的大小为[10*10, 20*5, 30*3]
。
如果模型的大小不像受Node
的s3
的影响,可通过设置shape3d.scaleable
为false
,该值默认为true
。
因此如果模型大小受s3
影响的情况下,一般导入模型时设置cube
参数为true
,将导入的模型缩放到[1,1,1]
单元立方体内,
然后将对应的Node
图元的s3
参数设置成解析后模型的rawS3
参数,该参数代表OBJ
模型在缩放到单元立方体之前的大小。
上例加载OBJ
文件采用AJAX
的方式,通过构建两个XMLHttpRequest
对象,
通过AJAX
方式分别获取obj
和mtl
文件,在数据onload
之后再进行解析处理。
load('obj/scooter.mtl', 'obj/scooter.obj');
function load(mtlUrl, objUrl){
var xhr1 = new XMLHttpRequest();
xhr1.onload = function(e){
var mtlText = e.target.responseText;
var xhr2 = new XMLHttpRequest();
xhr2.onload = function(e){
var objText = e.target.responseText;
parse(mtlText, objText);
};
xhr2.open('GET', objUrl, true);
xhr2.send(null);
};
xhr1.open('GET', mtlUrl, true);
xhr1.send(null);
}
以下例子也实现了同样的功能,但采用了HT
提供的ht.Default.loadObj
更为便捷的函数。
ht.Default.loadObj(objUrl, mtlUrl, params)
:
objUrl
:OBJ
文件路径mtlUrl
:MTL
文件路径params
:JSON
结构参数,可设置ht.Default.parseObj(text, mtlMap, params)
第三个参数类型的控制信息,并增加以下参数sync
:是否同步参数,默认为false
代表异步加载,设置为true
代表同步加载,意味数据加载后才运行loadObj
之后的代码finishFunc: function(modelMap, array, rawS3){}
:用于加载后的回调处理,modelMap
:调用ht.Default.parseObj
解析后的返回值,若加载或解析失败则返回值为空array
:所有材质模型组成的数组rawS3
:包含所有模型的原始尺寸除了上述的加载方式,HT
还提供了更简便的加载方式,通过一个JSON
对象来加载
json
属性说明如下
modelType
:模型类型,目前HT
支持obj
格式模型,一般填写为obj
obj
:obj
文件路径mtl
:mtl
文件路径prefix
:贴图前缀路径,指定后mtl
中所有贴图路径请求都会带上这个前缀除了以上基本属性,还可以增加其它与ht.Default.parseObj(objText, mtlText, params)
中params
的参数,如增加s3
大小变化参数,可参考解析OBJ。
var dm = new ht.DataModel(),
g3d = window.g3d = new ht.graph3d.Graph3dView(dm);
g3d.setGridVisible(true);
g3d.addToDOM();
// 通过 json 文件加载
var node = new ht.Node();
node.setAnchor3d([0.5, 0, 0.5]);
node.p3(-200, 0, 0);
node.s('shape3d', 'models/equipment.json');
dm.add(node);
// 通过 json 对象加载
var model = {
"modelType": "obj",
"obj": "obj/equipment.obj",
"mtl": "obj/equipment.mtl",
"prefix": "obj/"
};
var node2 = new ht.Node();
node2.setAnchor3d([0.5, 0, 0.5]);
node2.p3(200, 0, 0);
node2.s('shape3d', model);
dm.add(node2);
序列化手册例子获取OBJ
和MTL
文本内容的方式较特殊,
将模型信息内容存放到function
的注解中,将function
转换成字符串,然后裁剪掉头尾取中间实际模型内容部分,
传统的方式一般将.obj
和.mtl
存放在服务端,客户端通过AJAX
的方式分别加载文件内容进行解析,
采用截取注解字符串的方式,可避免跨域安全访问限制问题,浏览器直接本地打开即可运行。
var scooter_mtl = getRawText(function(){/*
newmtl Black
Ns 190.196078
Ka 0.000000 0.000000 0.000000
Kd 0.000000 0.000000 0.000000
Ks 0.100000 0.100000 0.100000
Ni 1.000000
d 1.000000
illum 2
...
*/});
function getRawText(obj){
var text = String(obj);
return text.substring(14, text.length-3);
}
OBJ
导入模型的另外一种应用就是与CSGNode的结合,
特别是构建门窗等应用场景时,可用CSGNode进行挖空,
将图元style
的shape3d
属性设置为OBJ
导入注册的模型,使得图元显示为更逼真的OBJ
门窗建模效果。
结合DoorWindow类型可实现开门开窗效果,
以下示例展示了OBJ
导入模型与CSGNode和
DoorWindow两种类型结合的门窗应用场景: