一般创建一个WebGL
程序,一般需要4个步骤:
但是对于Treee.js却有所不同,其使用面向对象的方式来构建程序,包含3个基本对象:场景(scene)、 相机(camera)、渲染器(renderer)。拿电影来类比的话,场景对应于整个布景空间,相机是拍摄镜头,渲染器用来把拍摄好的场景转换成胶卷。场景和相机代表了3D官场空间和数据模型,渲染器则包含了WebGL绘图上下文和着色器。
const content = document.querySelector('#root');
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, content.clientWidth/content.clientHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( content.clientWidth, content.clientHeight );
content.appendChild( renderer.domElement );
上面的代码构建了scene,camera和renderer,这里我们创建了常用的远景相机(PerspectiveCamera)。其第一个属性75设置的是视角,第二个属性设置的是相机拍摄面的长宽比(一般用元素的款除以高,否则会出现挤压变形)。最后两个属性则为近裁剪面和远裁剪面,可以结合下图理解。
完整代码示例
const content = document.querySelector('#root');
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, content.clientWidth/content.clientHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( content.clientWidth, content.clientHeight );
content.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
var render = function () {
requestAnimationFrame( render ); //这里我们使用``requestAnimationFrame``来循环调用
cube.rotation.x += 0.01;
cube.rotation.z += 0.01;
renderer.render(scene, camera);
};
render()
在上述代码中我们通过requestAnimationFrame
函数,其作用是相浏览器发起一个执行某函数的请求,什么时候会执行由浏览器决定,一般默认报纸60FPS的频率,大约16.7ms执行一次.通过类似于setInterval
函数也可以实现这样的效果。
Tree.js使用matrices 来宾阿玛3D变换–位置平移,旋转和缩放。每个3D对象有一个 matrix用来保存改对象的位置、旋转和缩放因子。
有两个方法完成对象的矩阵变换:
object.position.copy(start_position);
object.quaternion.copy(quaternion);
对于参数matrixAutoUpdate
属性将被设置为true,这样矩阵会被自动重新计算。如果对象是静止的或者需要手动控制计算过程来获得更好的性能,可以将该属性设置为false:
object.matrixAutoUpdate = false;
在修改玩对象属性后,手动更新矩阵:
object.updateMatrix();
object.matrix.setRotationFromQuaternion(quaternion);
object.matrix.setPosition(start_position);
object.matrixAutoUpdate = false;
注意,在这种情况下,matrixAutoUpdate 必须 被设置为 false,并且你应该要确保 不去 调用 updateMatrix。 否则手动修改会被自动计算所覆盖。
使用OrbitControls.js
实现对元素进行旋转、缩放。
OrbitControls.js
空间支持鼠标做中右键操作和键盘方向键操作,其代码实现比较简单, 只需将上的renderer.render(scene,camera)
用下述代码替换即可
render();
console.dir(THREE.OrbitControls)
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.addEventListener("change", render);
OrbitControls.js
控件提供了一个构造函数THREE.OrbitControls(),把一个相机对象作为参数的时候执行new THREE.OrbitControls(camera,renderer.domElement)
,浏览器会自动检测鼠标、键盘的变化,并且同时调用render()
重新渲染,这样threejs就会使用相机新的位置或角度数据进行渲染。
提供以下三种场景操作:
插入新的集合体与前面的创建立方体的类似,需要创建一个几何体对象和一个材质对象,然后将两个参数作为网格模型构造函数Mesh()
的参数创建一个网格模型,然后再使用场景对象scene
的方法add()
把网格模型mesh
加入场景中。
下面的代码同时绘制了立方体、球体和圆柱对应的网格模型,同时用两个方法修改模型的位置,若不修改模型的位置则默认在(0,0,0)的原点。
/ 立方体网格模型
var geometry1 = new THREE.BoxGeometry(100, 100, 100);
var material1 = new THREE.MeshLambertMaterial({
color: 0x0000ff
}); //材质对象Material
var mesh1 = new THREE.Mesh(geometry1, material1); //网格模型对象Mesh
scene.add(mesh1); //网格模型添加到场景中
// 球体网格模型
var geometry2 = new THREE.SphereGeometry(60, 40, 40);
var material2 = new THREE.MeshLambertMaterial({
color: 0xff00ff
});
var mesh2 = new THREE.Mesh(geometry2, material2); //网格模型对象Mesh
mesh2.translateY(120); //球体网格模型沿Y轴正方向平移120
scene.add(mesh2);
// 圆柱网格模型
var geometry3 = new THREE.CylinderGeometry(50, 50, 100, 25);
var material3 = new THREE.MeshLambertMaterial({
color: 0xffff00
});
var mesh3 = new THREE.Mesh(geometry3, material3); //网格模型对象Mesh
// mesh3.translateX(120); //球体网格模型沿Y轴正方向平移120
mesh3.position.set(120,0,0);//设置mesh3模型对象的xyz坐标为120,0,0
scene.add(mesh3); //
AxisHelper
使用辅助三维坐标系AxisHelper
,可以直接调用创建一个三维坐标系,然后通过add方法添加到场景中。
// 创建辅助坐标系,并设置坐标系大小为250
var axisHelper = new THREE.AxisHelper(250);
scene.add(axisHelper);
前面的案例中对应的网格模型材质只设置了一个颜色,实际渲染的时候往往会设置其它的参数,比如实现玻璃效果要设置材质透明度,一些光亮的表面要添加高光效果。
在创建场景的材质对象时,添加opacity
和transparent
属性,opacity
代表透明度,transparent
表示是否开启透明效果,用下面代码创建正方体的网格模型的材质,可以看到正方体变成半透明效果。
var material = new THREE.MeshBasicMaterial( {
color: "red",
opacity: 0.7,
transparent: true,
} );
材质属性 | 简介 |
---|---|
color | 材质颜色 |
wireframe | 将几何图形渲为线框 |
opacity | 透明度设置,0表示完全透明,1表示完全不透明 |
transparent | 是否开启透明,默认不开启 |
材质类型 | 功能 |
---|---|
MeshBasicMaterial | 基础网格材质,不受光照影响的材质 |
MeshLambertMaterial | Lambert网格材质,与光照有反应,漫反射 |
MeshPhongMaterial | 高光Phong材质,与光照有反应 |
MeshStandardMaterial | PBR物理材质,相比较高光Phong材质可以更好的模拟金属、玻璃等效果 |
值得注意的是除了第一个材质其它只有在有点光源时才会有对应的颜色
光源 | 简介 |
---|---|
AmbientLight | 环境光 |
PointLight | 点光源 |
DirectionalLight | 平行光,比如太阳光 |
SpotLight | 聚光源 |
环境光创建
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);
点光源创建
var point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300); //点光源位置
scene.add(point);
为了更好的理解顶点的概念,我们自定义了一个几何体,并将其渲染为两个三角形的效果(如下图所示)。
下面我们通过Tree.js 的API自定义了具有六个顶点数据的几何体。
var geometry = new THREE.BufferGeometry(); //创建一个Buffer类型几何体对象
//类型数组创建顶点数据
var vertices = new Float32Array([
0, 0, 0, //顶点1坐标
10, 0, 0,
0, 20, 0,
0, 0, 10,
0, 0, 30,
20, 0, 10,
]);
// 创建属性缓冲区对象
var attribue = new THREE.BufferAttribute(vertices, 3);
geometry.attributes.position = attribue;
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff, //三角面颜色
side: THREE.DoubleSide //两面可见
}); //材质对象
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
在上述的代码中,我们定义了六个顶点并用网格模型进行渲染。
对于网格模型Mesh
,几何体将以我们定义的三个一组的顶点渲染为三角形,若要渲染为顶点,我们只需用Points
进行渲染,几何体的每个顶点位置都会渲染出一个正方形的点区域,并且课设置大小。
var material = new THREE.PointsMaterial({
color: 0x0000ff,
size: 10.0,
}); //材质对象
var mesh = new THREE.Points(geometry, material);
scene.add(mesh);
与点模型类似,我们可以修改几何体为Line
模型来得到一条从第一个自定义顶点出发到最后一个顶点的线。
var material = new THREE.LineBasicMaterial({
color: "yellow", //三角面颜色
}); //材质对象
var mesh = new THREE.Line(geometry, material);
scene.add(mesh);
通常几何体的位置坐标和几何体的顶点颜色数据是一一对应的。
每个顶点设置一种颜色
我们在上一小结的顶点渲染的案例中修改为每个顶点设置不同的颜色。
我们只需添加自定义的顶点颜色同时修改材质的颜色属性
vertexColors
即可。
var colors = new Float32Array([
1, 0, 0, //顶点1颜色
0, 1, 0, //顶点2颜色
0, 0, 1, //顶点3颜色
1, 1, 0, //顶点4颜色
0, 1, 1, //顶点5颜色
1, 0, 1, //顶点6颜色
]);
// 设置几何体attributes属性的颜色color属性
geometry.attributes.color = new THREE.BufferAttribute(colors, 3);
var material = new THREE.PointsMaterial({
vertexColors: THREE.VertexColors,
size: 10.0,
}); //材质对象
上面的案例是针对顶点渲染的,其实对网格模型和先模型也是类似的。最后会得到以下效果
出现渐变的效果是因为Tree.js通过底层WebGL进行渲染时会对顶点的颜色进行插值计算。
实际开发项目的时候,可能会加载外部模型,有些时候需要获取模型几何体的顶点数据,如果想获取几何体的顶点数据首先要熟悉three.js几何体BoxGeometry
和BufferGeometry
的结构。
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
console.log(geometry);
console.log('几何体顶点位置数据',geometry.vertices);
console.log('三角行面数据',geometry.faces);
案例
通过下面代码修改BoxGeometry
的三角形顶点颜色的数据,可以渲染出来如下效果。
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
// 遍历几何体的face属性
geometry.faces.forEach(face => {
// 设置三角面face三个顶点的颜色
face.vertexColors = [
new THREE.Color(0xffff00),
new THREE.Color(0xff00ff),
new THREE.Color(0x00ffff),
]
});
var material = new THREE.MeshBasicMaterial({
vertexColors: THREE.FaceColors,
}); //材质对象Material
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
同样的我们也可以删除正方体的部分表面,通过添加geometry.faces.pop();
,可以删除几何体的部分表面。
通过前面的案例我们对Geometry
对象有了一定的了解,我们可以通过修改其属性达到对几何体的操控。Tree.js也封装一系列的方法,通过scale
、translate
、rorateX
等方法对几何体本身进行缩放、平移、旋转等几何变换,而这些变换的本质都是改变几何体顶点位置的坐标数据。我们可以将几何体变换前后的顶点数据进行输出比对查看几何体的顶点位置坐标是否发生变化。
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
// 几何体xyz三个方向都放大2倍
geometry.scale(2, 2, 2);
// 几何体沿着x轴平移50
geometry.translate(50, 0, 0);
// 几何体绕着x轴旋转45度
geometry.rotateX(Math.PI / 4);
// 居中:偏移的几何体居中
geometry.center();
console.log(geometry.vertices);
BufferGeometry和几何体Geometry 一样具有.scale()
、.rotateZ()
、.rotateX()
等几何体变换的方法。
注意
注意网格模型Mesh
进行缩放旋转平移变换和几何体Geometry
可以实现相同的渲染效果,但是网格模型Mesh
进行这些变换不会影响几何体的顶点位置坐标,网格模型缩放旋转平移变换改变的是模型的本地矩阵、世界矩阵。
e.log(geometry.vertices);
[BufferGeometry](http://www.yanhuangxueyuan.com/threejs/docs/index.html#api/zh/core/BufferGeometry)和几何体[Geometry](http://www.yanhuangxueyuan.com/threejs/docs/index.html#api/zh/core/Geometry) 一样具有`.scale()`、`.rotateZ()`、`.rotateX()`等几何体变换的方法。
**注意**
注意网格模型`Mesh`进行缩放旋转平移变换和几何体`Geometry`可以实现相同的渲染效果,但是网格模型`Mesh`进行这些变换不会影响几何体的顶点位置坐标,网格模型缩放旋转平移变换改变的是模型的本地矩阵、世界矩阵。