网站首页 > 技术文章 正文
阅读原文:【技术分享】Threejs 核心基础类——Object3D
点击关注“八戒技术团队”,阅读更多技术干货
在学习threejs过程中,涉及到各种各样的类和方法,比如Camera、Light、Mesh等等。对于初学者而言,遇到问题只知其然而不知其所以然,面对数量庞大且关联密切的类和方法往往会犯迷糊,所以深入的理清各个类之间的联系、继承关系,以及共用的方法变得尤为重要。基于此,本文将着重介绍threejs中的基础核心类之一—Object3D。
Object3D作为threejs中最重要的基础类之一,包括上面提到的Camera、Light、Mesh在内的大量类均继承自Object3D。它为需要用到位置、方向等数据的对象提供大量的公共属性和方法。这些方法可以用来设置项目中3d对象的position、rotation等属性,或者设置多个3d对象的共用属性。只要理解这些共用的属性或方法,相同的机制会作用于继承自Object3D的其他任何类。
在本文中,我们将梳理Object3D类中较常用到的基础知识,并直接运用Object3D中的属性方法。当然,我们也将会在使用相机、网格等类的同时运用这些方法和属性,以便加深印象。与此同时,也会接触到与空间变换相关的其他重要类,比如Vector3和Euler。
一、position属性
在threejs中,通常情况下我们不会直接使用Object3D类中的方法和属性,而是通过间接地使用继承它的类中的方法和属性。如果在特殊性情况下,需要直接使用Object3D类的方法,建议通过new关键字创建THREE.Object3D的实例进行操作。
var obj = new THREE.Object3D();
// {"x":0,"y":0,"z":0}
console.log(JSON.stringify(obj.position));
obj.position.set(3, 4, 5);
// {"x":3,"y":4,"z":5}
console.log(JSON.stringify(obj.position));
上面的例子中,使用了position属性,其值是一个Vector3的实例。Vector3可以理解为三维向量,可用于表示空间中的点(x,y,z)。position属性设置的是场景中3d对象的中心点。如果是父子对象,那么子对象的位置将是相对父对象的位置设置的。
二、rotation属性
rotation是Object3D中另外一个常用的属性,它的值为Euler类的实例。Euler实例表示的是欧拉角(Euler Angles,简单来说,就是几何体自身的坐标系,相对于全局坐标系,以有序的方式绕全局坐标轴和自身坐标轴旋转所产生的一组角度,它用于描述几何体在空间中的旋转。详细内容这里不展开,有兴趣的小伙伴可以自行查阅相关资料)。当我们创建或更改Euler实例的值时,可以用set方法设置值,有4个参数,前3个为从0到2的弧度,表示相对于对应坐标轴的旋转角度,最后一个为旋转顺序,默认为XYZ。设置值的另一种方法为copy方法,复制另外一个实例的值。
接下来,将通过一段简短的代码来说明具体的使用方法。
// 建立场景
var scene = new THREE.Scene();
// 创建实例
var obj = new THREE.Object3D();
// 设置旋转角度
obj.rotation.set(0, 0, Math.PI * 1.75);
// 创建网格对象
var mesh = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshNormalMaterial()
);
// 将obj 的rotation属性值复制给mesh对象
mesh.rotation.copy(obj.rotation);
scene.add(mesh);
// 相机
var camera = new THREE.PerspectiveCamera(4, 4 / 3, .5, 100);
camera.position.set(2, 2, 2);
camera.lookAt(0, 0, 0);
// 渲染
var renderer = new THREE.WebGLRenderer();
renderer.setSize(640, 480);
document.getElementById('myDemo').appendChild(renderer.domElement);
renderer.render(scene, camera);
在本示例中,我们先创建了一个Object3D的实例obj,然后使用Euler类的set方法,为实例设置旋转角度。接着创建一个Mesh类的实例mesh,由于Mesh类继承自Object3D类,因此mesh也拥有rotation属性和对应的方法,最后我们使用mesh继承来的copy方法为自己设置与obj相同的旋转属性值。眼尖的小伙伴可能注意到了,上面的实例中用到了一个方法:lookAt,我们常常在Camera实例中见到并用来设置相机的方向,但是它依然是继承自Object3D,你甚至可以在Mesh的实例上调用此方法!它也可以被看作是设置旋转值的另一种方式,它可以接受3个参数:一组代表x,y,z数值的数字,也可以接受Vector3的实例作为参数。下面我们将用lookAt方法设置Object3D的rotation。
接下来,看看可以如何在其他继承自Object3D的类上使用lookAt方法。
var scene = new THREE.Scene();
var mesh = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshNormalMaterial()
);
scene.add(mesh);
var camera = new THREE.PerspectiveCamera(45, 4 / 3, .5, 100);
camera.position.set(2, 2, 2);
camera.lookAt(0, 0, 0);
// 很形象,转头,看向camera.position代表的位置
mesh.lookAt(camera.position);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(640, 480);
document.getElementById('demo').appendChild(renderer.domElement);
renderer.render(scene, camera);
下面,我们将结合上面讲到内容进一步给出一个简单的旋转动画示例,将涉及到Vector3实例,以及将lookAt方法运用在调整模型角度上。
// 实际上Scene也是继承自Object3D
var scene = new THREE.Scene();
// GridHelper同样是继承自Object3D
var gridHelper = new THREE.GridHelper(4, 4);
// 这里我们可以直接使用继承来的scale属性来设置缩放
gridHelper.scale.set(2.5, 2.5, 2.5);
scene.add(gridHelper);
// 不用多说Mesh依然是继承自Object3D
var box = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshNormalMaterial());
scene.add(box);
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(0.25, 20, 20),
new THREE.MeshNormalMaterial());
scene.add(sphere);
// Camera同样是继承自Object3D
var camera = new THREE.PerspectiveCamera(45, 4 / 3, .5, 100);
camera.position.set(10, 10, 10);
camera.lookAt(0, 0, 0);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(640, 480);
document.getElementById('myDemo').appendChild(renderer.domElement);
var state = {
frame: 0,
maxFrame: 100,
fps: 30,
lt: new Date(),
vector: new THREE.Vector3(3, 0, 0) // and instance of vercor3
};
var update = function (state) {
state.vector.z = -5 + 10 * state.bias;
// 用state的vector属性设置sphere的position
sphere.position.copy(state.vector);
// 让box以lookAt的方法调整旋转角度,始终面向sphere
box.lookAt(state.vector);
};
var loop = function () {
state.per = state.frame / state.maxFrame;
state.bias = 1 - Math.abs(state.per - 0.5) / 0.5;
var now = new Date();
secs = (now - state.lt) / 1000;
requestAnimationFrame(loop);
if (secs > 1 / state.fps) {
update(state);
renderer.render(scene, camera);
state.frame += state.fps * secs;
state.frame %= state.maxFrame;
state.lt = now;
}
};
loop();
在上面的例子中,我们建立了一个包含Vector3实例属性值的state对象,并通过改变state.vector的z轴值来使sphere对象平行于z轴作来回运动,并使box对象按照sphere的路径位置旋转。
三、Group
Group类用于将一系列3d实例对象组合成集合,它同样继承自Object3D,其add方法来自于Object3D,也就是说add方法存在于任何继承于Object3D的类或对象中。所以可以用这一系列的类实现实例对象分组的功能,当然也包括Object3D本身。
接下来,我们将展示一个如何创建3d对象组的示例。
var createCubeStack = function (original) {
var stack = {},
original = original || new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshNormalMaterial()),
cube;
// 直接通过Object3D创建group对象
stack.group = new THREE.Object3D();
// 定义set方法用来设置3d对象集合中元素的位置和旋转方向
stack.set = function (per) {
var bias = 1 - Math.abs(0.5 - per) / 0.5,
arr = stack.group.children,
len = arr.length;
arr.forEach(function (cube, i) {
var y = -len / 2 + i + 2 * bias;
cube.position.set(0, y, 0);
cube.rotation.set(0, Math.PI * 2 * (i / len) + Math.PI * 2 * per, 0);
});
};
// 创建多个3d对象实例
var i = 0,
len = 3,
per;
while (i < len) {
per = i / len;
cube = original.clone();
cube.position.set(0, -len / 2 + i, 0);
cube.rotation.set(0, Math.PI * 2 * per, 0);
stack.group.add(cube)
i += 1;
}
return stack;
};
上面例子中,我们创建了一个方法,用来生成并返回一个3d对象实例的group,并包含了设置group中元素位置,旋转角度的方法。接下来,我们将把此方法用到具体的场景中去:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, 4 / 3, .5, 100);
camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);
var stack = createCubeStack();
scene.add(stack.group);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(320, 240);
document.getElementById('demo').appendChild(renderer.domElement);
var frame = 0,
maxFrame = 100;
var loop = function () {
requestAnimationFrame(loop);
renderer.render(scene, camera);
stack.set(frame / maxFrame);
frame += 1;
frame = frame % maxFrame;
};
renderer.render(scene, camera);
loop();
当我们运行代码后,会得到一堆立方体,旋转、跳跃。当然直接用Object3D来设置3d实例对象的分组并不是最优的方案,毕竟人家已经明确给出了Group类用来实现分组的功能。但是我们只是想通过这个例子来进一步说明,threejs中Object3D和其他类之间的继承关系。
四、其他
在Object3D中还有很多有用的属性。比如:name属性,可以为3d实例对象设置name属性,并通过getObjectByName获取相应的实例对象;scale属性,其值是Vector3的实例,可以用来设置实例对象的缩放;userData属性,可以用来存储用户的自定义数据,方便在各个实例中读取并使用。除了列出的这些,还有很多实用的属性和方法。
本文并不打算对Object3D中所有的属性和方法作一一列举。仅仅旨在通过一些典型的方法和属性示例,来说明threejs中各个类的继承关系及如何使用共有的方法和属性,并以此为初学者提供一种新的系统性的学习思路。同时也希望本文对大家有所帮助。
希望以上内容能对有需要的人有所帮助
欢迎大家留言写下自己希望了解的技术方向
欢迎大家一起探讨交流
猜你喜欢
- 2024-11-27 cesium.js神了,让3d地图变得惊艳无比
- 2024-11-27 Unity(WebGL)与JS通讯2021最新姿势
- 2024-11-27 官方示例(十四)多线段开发3D场景测量标点距离ThingJS
- 2024-11-27 从零开始学习3D可视化之摄像机投影方式
- 2024-11-27 THREE.js:网页上的3D世界构建者
- 2024-11-27 nunustudio 基于threejs的web3d开源编辑器
- 2024-11-27 WebGL+Three.js 入门与实战,系统学习 Web3D 技术无密梅花含苞
- 2024-11-27 Three.js、Babylon.js 和 ClayGL:哪款Web3D引擎最适合你的项目?
- 2024-11-27 3D地图的开发离不开CesiumJs,效果超出你的想象。
- 2024-11-27 3D 世界的钥匙「GitHub 热点速览 v.22.08」
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)