WebGL是在浏览器中实现三维效果的一套规范
什么是threejs,很简单,你将它理解成
three+js
就可以了。three表示3D的意思
,js表示javascript的意思
。那么合起来,three.js
就是使用javascript
来写3D程序
的意思。
Three.js
也是运行在浏览器
上的。使用Three.js之前,首先在
<head>
部分,需要引入外部文件Three.js
。
<head>
<script type="text/javascript" src="three.js"></script>
</head>
WebGL 的渲染是需要HTML5 中的
Canvas
元素的,你可以手动在HTML的<body>
部分中使用canvas标签,或者让Three.js帮你生成。这两种选择,一般没有多大差别。我们先手动定义一个canvas标签:
<body onload="init();">
<canvas id="canvasId" width="800" height="600"></canvas>
</body>
在js里面定义一个函数,将所有执行的代码放在函数里,在html加载完成后,执行该函数
function init{
// 所有需要执行的代码
}
一个典型的Three.js程序,至少应该包括四个部分:渲染器(renderer)
、场景(scene)
、相机(camera)
、以及场景中创建的物体
。
渲染器
决定了渲染的结果
应该画在页面的什么元素上面
,并且以怎样的方式来绘制
。渲染器将会和canvas元素进行绑定,如果之前
<html>
标签中,定义了id为canvasId的canvas标签
,那么renderer可以这样写:
var renderer = new THREE.WebGLRenderer({
canvas : document.getElementById('canvasId');
});
如果想要
Three.js生成Canvas元素
的时候,在html中就不需要在定义一个canvas标签了,直接在javascript代码中写道:
var renderer = new THREE.WebGLRenderer();
renderer.setSize = (800,600);
document.body.appendChild(renderer.domElement);
setSize
是为canvas元素设置宽高,document.body.appendChild(renderer.domElement)
是将渲染器对应的Canvas元素添加到body
中。我们可以使用下面的代码(用于清除画面的颜色)将背景色设置为黑色:
renderer.setClearColor(0x000000);
在Three.js中
添加物体
都是添加到场景
中的,因此它相当于一个大容器。一般说,场景里没有很复杂的操作,只要new一个对象就可以了,然后将物体添加到场景中即可。
var scene = new THREE.Scene();
在介绍照相机之前,我们先来介绍一下坐标系。
这里我们定义一个
透视相机
(相机也需要添加到场景中):
var camera = new THREE.PerspectiveCamera(45, 4 / 3, 1, 1000);
// 设置相机的位置
camera.position.set(0,0,5);
// 将相机添加到场景中
scene.add(camera);
这里我们先介绍一个长方体,创建一个x、y、z方向长度分别为1、2、3的长方体,并设置为红色。
var geometry = new THREE.CubeGeometry(1,2,3);
var material = new THREE.MeshBasicMaterial({
color: 0xff0000;
});
var cube = new THREE.Mesh(geometry,material);
scene.add(cube);
new THREE.CubeGeometry(); 表示调用一个几何体
CubeGeometry
是一个正方体
或者长方体
,究竟是什么,由它的3个参数所决定CubeGeometry(width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, sides)
width:立方体x轴的长度
height:立方体y轴的长度
depth:立方体z轴的深度,也就是长度
想一想大家就明白,以上3个参数就能够确定一个立方体。
剩下的几个参数就要费解和复杂一些了,不过后面我们会自己来写一个立方体,到时候,你会更明白这些参数的意义,这里你可以将这些参数省略。
new THREE.MeshBasicMaterial(); 表示的是物体的材质
var material = new THREE.MeshBasicMaterial({
color: 0xff0000;
});
一定不要忘了,将物体添加到场景
在定义了场景中的物体,设置好的照相机之后,渲染器就知道如何渲染出二维的结果了。这时候,我们只需要调用渲染器的渲染函数,就能使其渲染一次了。
renderer.render(scene, camera);
<html lang="en">
<head>
<meta charset="UTF-8">
<title>长方体</title>
<script src="three.js"></script>
</head>
<body onload="init();">
<script>
function init() {
// 渲染器
var renderer = new THREE.WebGLRenderer();
// canvas元素设置宽高
renderer.setSize = (800, 600);
// 渲染器对应的Canvas元素添加到<body>中。
document.body.appendChild(renderer.domElement);
// 清除画面的颜色
renderer.setClearColor(0x000000);
// 场景
var scene = new THREE.Scene();
// 相机
// 定义一个透视相机
var camera = new THREE.PerspectiveCamera(45, 4 / 3, 1, 1000);
// 设定相机的位置
camera.position.set(0, 0, 5);
// 将相机添加到场景中
scene.add(camera);
// 物体
var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3), new THREE.MeshBasicMaterial({
color: 0xff0000
}));
scene.add(cube);
// 渲染
renderer.render(scene, camera);
}
</script>
</body>
</html>
canvas元素的默认宽高为300/150
下面介绍下Three.js官网文档中的一些重要的对象,在你需要寻求帮助时,就能够知道关键词是什么。
Cameras(照相机,控制投影方式)
Core(核心对象)
Lights(光照)
Loaders(加载器,用来加载特定文件)
Materials(材质,控制物体的颜色、纹理等)
Math(和数学相关的对象)
Objects(物体)
Renderers(渲染器,可以渲染到不同对象上)
Renderers / Renderables
Scenes(场景)
Textures(纹理)
Extras
Extras / Animation
Extras / Cameras
Extras / Core
Extras / Geometries(几何形状)
Extras / Helpers
Extras / Objects
Extras / Renderers / Plugins
Extras / Shaders
我们看到,Three.js功能是十分丰富的,一时间想全部掌握有些困难。在接下来的章节中,我们将会先详细介绍照相机、几何形状、材质、物体等入门级知识;然后介绍使用动画、模型导入、加入光照等功能;最后,对于学有余力的读者,我们将介绍着色器,用于更高级的图形渲染。
本章将介绍照相机的概念,以及如何使用Three.js设置相应的参数。
在图形学中,照相机可没有生活中的照相机那么简单
投影方式
的不同,照相机又分为正交投影照相机
与透视投影照相机
。我们需要为自己的程序选择合适的照相机。这两者分别是什么,以及两者有何差异,我们将在下节中作介绍。举个简单的例子来说明正交投影与透视投影照相机的区别。使用
透视投影照相
机获得的结果是类似人眼在真实世界中看到的有“近大远小”
的效果(如下图中的(a));而使用正交投影照相机
获得的结果就像我们在数学几何学课上老师教我们画的效果,对于三维空间内平行的线
,投影到二维空间中也一定是平行的
(如下图中的(b))。
一般说来,对于制图、建模软
通常使正交投影
,这样不会因为投影而改变物体比例;而对于其他大多数应用
,通常使用 透视投影
,因为这更接近人眼的观察效果。当然,照相机的选择并没有对错之分,你可以更具应用的特性,选择一个效果更佳的照相机。
正交投影照相机(
Orthographic Camera
)
THREE.OrthographicCamera(left, right, top, bottom, near, far)
这六个参数分别代表正交投影照相机拍摄到的空间的六个面的位置,这六个面围成一个长方体,我们称其
视景体(Frustum)
。只有在视景体内部(下图中的灰色部分)的物体才可能显示在屏幕上,而视景体外的物体会在显示之前被裁减掉。
为了保持照相机的横竖比例,需要保证
(right - left)与(top - bottom)
的比例与Canvas宽度与高度的比例(800/600)
一致。
// [2-(-2)] / [1.5-(-1.5)] = canvas.width/canvas.height
var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10) // left right top bottom near far
near与far
都是指到照相机位置在深度平面的位置,而照相机不应该拍摄到其后方的物体,因此这两个值应该均为正值
。为了保证场景中的物体不会因为太近或太远而被照相机忽略,一般near的值设置得较小
,far的值设置得较大
,具体值视场景中物体的位置等决定。
下面我们通过一个具体的例子来了解正交投影照相机的设置
基本设置
设置照相机:
var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10);
camera.poaition.set(0,0,5);
scene.add(camera);
在原点处创建一个边长为1的正方体,为了和透视效果做对比,这里我们使用wireframe
而不是实心的材质,以便看到正方体后方的边:
var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({
color: 0xff0000,
wireframe: true
})
);
scene.add(cube);
效果图:
长宽比例
这里,我们的Canvas宽度是800px,高度是600px,照相机水平方向距离4,垂直方向距离3,因此长宽比例保持不变。为了试验长宽比例变化时的效果,我们将照相机水平方向的距离减小为2(right-left = 2):
var camera = new THREE.OrthographicCamera(-1, 1, 1.5, -1.5, 1, 10);
照相机位置
接下来,我们来看看照相机位置对渲染结果的影响。在之前的例子中,我们将照相机设置在(0, 0, 5)位置,而由于照相机默认是
面向z轴负方向
放置的,所以能看到在原点处的正方体。现在,如果我们将照相机向右移动1个
单位:
var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10);
// 向右移动一个单位的位置
camera.position.set(1, 0, 5);
正交投影摄像机在设置时,是否需要保证
left 和 right 互为相反数
呢?
下面,我们将原本的参数(-2, 2, 1.5, -1.5, 1, 10)
改为(-1, 1, 1.5, -1.5, 1, 10)
,即,将视景体设置得更靠右:
var camera = new THREE.OrthographicCamera(-1, 3, 1.5, -1.5, 1, 10);
camera.position.set(0, 0, 5);
效果图(与之前相机向右的效果是一样的)
换个角度
到目前为止,我们使用照相机,都是
沿着Z轴负方向
观察的,因此看到的都是一个正方形,现在我们尝试一下仰望
这个正方体,改变照相机的位置:
// x轴:4; y轴:-3; z轴:5
camera.position.set(4, -3, 5);
照相机默认是沿着z轴的负方向观察的,因此观察不到正方体,只看到一片黑。我们可以通过
lookAt函数
指定它看着原点方向
:
camera.lookAt(new THREE.Vector3(0, 0, 0));
lookAt函数
接收的是一个THREE.Vector3
的实例千万不能写成camera.lookAt(0,0,0)
。透视投影照相机(
Perspective Camera
)
THREE.PerspectiveCamera(fov, aspect, near, far)
让我们通过一张透视照相机投影的图来了解这些参数。
灰色
的部分是视景体
,是可能被渲染
的物体所在的区域。fov
是视景体竖直方向上
的张角
(是角度制而非弧度制),如侧视图所示。aspect
等于width / height
,是照相机水平方向和竖直方向长度的比值
,通常设为Canvas的横纵比例
。near和far
分别是照相机到视景体
最近、最远
的距离,均为正值
,且far应大于near
。下面我们通过一个例子来学习透视投影照相机
基本设置
设置透视投影照相机,这里Canvas长800px
,宽600px
,所以aspect
设为800 / 600
:
var camera = new THREE.PerspectiveCamera(45, 800 / 600, 1, 10);
camera.position.set(0, 0, 5);
scene.add(camera);
设置一个在原点处的边长为1的正方体:
var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({
color: 0xff0000,
wireframe: true
})
);
scene.add(cube);
效果图:
全部的12条边
,而且有近大远小
的效果,这也就是与正交投影的区别。竖直张角
接下来,我们来看下fov
的改变对渲染效果的影响。我们将原来的45改为60
:
var camera = new THREE.PerspectiveCamera(60, 800 / 600, 1, 10);
camera.position.set(0, 0, 5);
scene.add(camera);
效果图:
竖直张角
设置得更大
时,视景体变大了
,因而正方体
相对于整个视景体
的大小就变小
了,看起来正方形就显得变小了。改变fov
并不会
引起画面横竖比例
的变化,而改变aspect
则会
改变横竖比例。在计算机世界里,3D世界由点组成,两个点能组成一条直线,三个不在一条直线上的点,就能组成一个三角面,
无数的三角面
就能组成各种各样的物体
,如下图:
网络模型
叫做Mesh模型
。给物体贴上皮肤
,或者专业点就叫做纹理
,那么这个物体就活灵活现了。最后无数的物体就组成了我们的3D世界。在三维空间中的某一个点可以用一个坐标点来表示。一个坐标点由
x
,y
,z
三个分量构成。在three.js中,点可以在右手坐标系中表示:空间几何中,点可以用一个向量来表示,在Three.js中也是用一个
向量
来表示的
THREE.Vector3 = function ( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
};
Vector3
是定义在THREE
下面的一个类
。以后要用Vector3
,就必须要加THREE前缀
。当然Three.js的设计者,也可以不加THREE这个前缀,但是他们预见到,Three.js引擎中会有很多类型,最好给这些类型加一个前缀,以免与开发者的代码产生冲突。函数
。这个函数有3个参数
,分别代表x坐标
,y坐标
和z坐标
的分量。函数体内的代码将他们分别赋值给成员变量x,y,z
。看看上面的代码,中间使用了一个“||”(或)运算符
,就是当x=null或者undefine
时,this.x
的值应该取0
。在3D世界中
点
可以用THREE.Vector3D
来表示。
现在来看看怎么定义个点,假设有一个点x=4,y=8,z=9。你可以这样定义它:
var point1 = new THREE.Vecotr3(4,8,9);
另外你也可以使用set
方法,代码如下:
var point1 = new THREE.Vector3();
point1.set(4,8,9);
两个不重合的点能够决定一条直线。在three.js中,也可以通过定义两个点,来画一条直线。
1、首先,声明一个几何体geometry
几何体里面有个vertices变量
,可以用来存放点
var geometry = new THREE.Geometry();
// 几何体里面有个vertices变量,可以用来存放点
2、定义一种线条的材质,使用THREE.LineBasicMaterial
类型来定义,它接受一个集合作为参数
,其原型如下:
THREE.LineBasicMaterial(parameters);
parameters
是定义材质外观的对象,它包含多个属性来定义材质,这些属性是:
颜色
,用16进制表示,默认都是白色宽度
,默认是1个单位宽度两端的外
观,默认是圆角端点
,当线条较粗的时候才能看到效果连接点处的外观
,默认是“round”,表示圆角。线条材质
是否使用顶点颜色
,这是一个boolean值。意思是,线条各部分的颜色会根据顶点的颜色来进行插值。我们这里使用了顶点颜色
vertexColors: THREE.VertexColors
,就是线条的颜色
会根据顶点
来计算。
var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
注意: 关于线宽的坑,WebGLRender
渲染方式是不之持绘制线宽的,要想支持,需要将渲染方式设置为CanvasRenderer
3、接下来,定义两种颜色,分别表示线条两个端点的颜色,
var color1 = new THREE.Color( 0x444444 ),
color2 = new THREE.Color( 0xFF0000 );
4、定义2个顶点的位置,并放到geometry
中,代码如下:
var p1 = new THREE.Vector3(-100,0,100);
var p2 = new THREE.Vector3(100,0,-100);
geometry.vertices.push(p1);
geometry.vertices.push(p2);
5、为4中定义的2个顶点,设置不同的颜色,代码如下所示:
geometry.colors.push( color1, color2 );
geometry中colors表示顶点的颜色
,必须材质中vertexColors等于THREE.VertexColors
时,颜色才有效
,如果vertexColors等于THREE.NoColors时,颜色就没有效果了。那么就会去取材质中color的值,这个很重要。
6、定义一条线。
定义线条,使用THREE.Line类
,代码如下所示:
var line = new THREE.Line( geometry, material, THREE.LinePieces );
第一个参数是几何体geometry
,里面包含了2个顶
点和顶点的颜色
。
第二个参数是线条的材质
,或者是线条的属性,表示线条以哪种方式取色。
第三个参数是一组点的连接方式。
7、然后,将这条线加入到场景中,代码如下:
scene.add(line);
8、整体代码:
<script type="text/javascript">
function init() {
// 渲染器
var renderer = new THREE.WebGLRenderer();
// 设定渲染器尺寸
renderer.setSize(800, 600);
// 添加到dom
document.body.appendChild(renderer.domElement);
// 重绘时颜色
renderer.setClearColor(0xffffff); // 场景
var scene = new THREE.Scene();
// 相机
var camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
// 设定相机位置
camera.position.set(0, -25, 0);
// 相机看向
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);
// 定义一个物体
// new 一个模型
var geometry = new THREE.Geometry();
// 定义模型的类型是线段 并且设置其材质
var material = new THREE.LineBasicMaterial({
// 使用顶点颜色
vertexColors: true
});
// 定义两个颜色
var color1 = new THREE.Color(0x444444),
color2 = new THREE.Color(0xff0000);
// 新建两个点
var p1 = new THREE.Vector3(-1, 0, 1);
var p2 = new THREE.Vector3(1, 0, -1);
// 将新建的两个点添加到几何体中
geometry.vertices.push(p1);
geometry.vertices.push(p2);
// 将两个颜色添加到几何体中
geometry.colors.push(color1, color2);
// new 一条线
var line = new THREE.Line(geometry, material, THREE.LinePieces);
scene.add(line);
// 渲染
renderer.render(scene, camera);
}
</script>
效果图:
在Threejs中,一条线由点,材质和颜色组成。
点由THREE.Vector3
表示,Threejs中没有提供单独画点的函数,它必须被放到一个THREE.Geometry
形状中,这个结构中包含一个数组vertices
,这个vertices就是存放无数的点(THREE.Vector3)的数组
。
1、为了绘制一条直线,首先我们需要定义两个点
var p1 = new THREE.Vector3( -1, 0, 1 );
var p2 = new THREE.Vector3( 1, 0, -1 );
2、声明一个THREE.Geometry
,并把点加进去
var geometry = new THREE.Geometry();
geometry.vertices.push(p1);
geometry.vertices.push(p2);
geometry.vertices的能够使用push方法
,是因为geometry.vertices
是一个数组
。这样geometry中就有了2个点了。
3、然后我们需要给线加一种材质,THREE.LineBasicMaterial
。
var material = new THREE.LineBasicMaterial();
4、最终我们通过THREE.Line
绘制了一条线:
var line = new THREE.Line( geometry, material, THREE.LinePieces );
我们要画一个网格的坐标,那么我们就应该找到线的点。把网格虚拟成正方形,在正方形边界上找到几个等分点,用这些点两两连接,就能够画出整个网格来。
1、定义两个点
// 在x轴上定义两个点p1(-500,0,0),p2(500,0,0)。
geometry.vertices.push( new THREE.Vector3( - 500, 0, 0 ));
geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ));
2、算法
for ( var i = 0; i <-= 20; i ++ ) {
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial({ color: 0x000000, opacity: 0.2 }));
line.position.z = ( i * 50 ) - 500;
scene.add( line );
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ));
line.position.x = ( i * 50 ) - 500;
line.rotation.y = 90 * Math.PI / 180; // 旋转90度
scene.add( line );
}
3、完整代码
<script type="text/javascript">
function init() {
// 渲染器
var renderer = new THREE.WebGLRenderer();
// 设定渲染器尺寸
renderer.setSize(800, 600);
// 添加到dom
document.body.appendChild(renderer.domElement);
// 重绘时颜色
renderer.setClearColor(0x000000); // 场景
var scene = new THREE.Scene();
// 相机
var camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
// 设定相机位置
camera.position.set(0, -25, 0);
// 相机看向
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);
// 定义一个几何体
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(-2, 0, 0));
geometry.vertices.push(new THREE.Vector3(2, 0, 0));
// for循环出来六条线段
for (var i = 0; i <= 5; i++) {
// 定义竖着的线段
var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
color: 0xffffff,
}));
// 每条线段之间的间隔为0.8,-2是为了达到田字格的效果
line.position.z = (i * 0.8) - 2;
scene.add(line);
// 定义横着的线段
var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
color: 0xffffff,
opacity: 0.2
}));
line.position.x = (i * 0.8) - 2;
line.rotation.y = 90 * Math.PI / 180;
scene.add(line);
// 渲染
renderer.render(scene, camera);
}
}
</script>
效果图:
在创建物体时,需要传入两个参数,一个是
几何形状(Geometry)
,另一个是材质(Material)
,这一章将着重介绍几何形状的创建,第6章介绍材质,第7章介绍如何使用两者创建网格。
顶点信息
。WebGL需要程序员指定每个顶点的位置,而在Three.js中,可以通过指定一些特征
来创建几何形状,例如使用半径
创建一个球体
,从而省去程序员一个个指定顶点的工作量。手动定义
顶点位置
和面片信息
组成几何形状。虽然这形状的名字叫做立方体(
CubeGeometry
),但其实是长方体,也就是长宽高可以设置不同的值:
new THREE.CubeGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)
width
是x方向
上的长度;height
是y方向
上的长度;depth
是z方向上
的长度;后三个参数分别是在三个方向上的分段数
,如widthSegments为3
的话,代表x方向上水平分为三份
。一般情况下不需要分段的话,可以不设置后三个参数,后三个参数的缺省值为1。其他几何形状中的分段也是类似的,下面不做说明。长宽高
创建立方体直观简单,如:new THREE.CubeGeometry(1, 2, 3);
可以创建一个x方向长度为1
,y方向长度为2
,z方向长度为3
的立方体。
// 调用渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(800, 600);
document.body.appendChild(renderer.domElement);
renderer.setClearColor(0x000000);
// 调用场景
var scene = new THREE.Scene();
// 调用相机
var camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
camera.position.set(25, 25, 25);
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);
// 新建一个几何体(长方体)
var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3), new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
}));
scene.add(cube);
为了更好地表现参数效果,我们在场景中用长度为3
的红、绿、蓝
线段分别表示x、y、z
三个轴(这里不需要深究,后面会详细介绍):
// 封装一个坐标系函数
function drawAxes(scene) {
// x-axis
var xGeo = new THREE.Geometry();
xGeo.vertices.push(new THREE.Vector3(0, 0, 0));
xGeo.vertices.push(new THREE.Vector3(3, 0, 0));
var xMat = new THREE.LineBasicMaterial({
color: 0xff0000
});
var xAxis = new THREE.Line(xGeo, xMat);
scene.add(xAxis);
// y-axis
var yGeo = new THREE.Geometry();
yGeo.vertices.push(new THREE.Vector3(0, 0, 0));
yGeo.vertices.push(new THREE.Vector3(0, 3, 0));
var yMat = new THREE.LineBasicMaterial({
color: 0x00ff00
});
var yAxis = new THREE.Line(yGeo, yMat);
scene.add(yAxis);
// z-axis
var zGeo = new THREE.Geometry();
zGeo.vertices.push(new THREE.Vector3(0, 0, 0));
zGeo.vertices.push(new THREE.Vector3(0, 0, 3));
var zMat = new THREE.LineBasicMaterial({
color: 0x00ccff
});
var zAxis = new THREE.Line(zGeo, zMat);
scene.add(zAxis);
}
// 在init 函数里调用这个函数 即可在屏幕上显示一个坐标系了
drawAxes(scene);
在设置材质,并添加到场景之后具体的效果是:
默认位置
是原点
,对于立方体而言,是其几何中心在原点的位置。分段
根据THREE.CubeGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)
,的后三个参数,为这个长方体分段:
// x轴分两段 y轴分两段 z轴分三段
new THREE.CubeGeometry(1, 2, 3, 2, 2, 3)
效果图:
六个面
进行分段,而不是对立方体的体素分段,因此在立方体的中间是不分段
的,只有六个侧面
被分段。这里的平面(
PlaneGeometry
)其实是一个长方形,而并非是数学意义上无限大的平面:
new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
其中,width
是x方向
上的长度;height
是y方向
上的长度;后两个参数同样表示分段
。
new THREE.PlaneGeometry(2, 4);
创建的平面在x轴和y轴所在平面内:
var plane = new THREE.Mesh(
new THREE.PlaneGeometry(2, 4),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(plane);
效果图:
x轴和z轴
所在的平面内,可以通过物体的旋转
来实现,具体的做法将在下面章节介绍到。球体(
SphereGeometry
)的构造函数是:
new THREE.SphereGeometry(radius, segmentsWidth, segmentsHeight, phiStart, phiLength, thetaStart, thetaLength)
radius
是半径
;segmentsWidth
表示经度上的切片数
;segmentsHeight
表示纬度上的切片数
;phiStart
表示经度开始的弧度
;phiLength
表示经度跨过的弧度
;thetaStart
表示纬度开始的弧度
;thetaLength
表示纬度跨过的弧度
。分段
首先,我们来理解下segmentsWidth
和segmentsHeight
。使用var sphere = new THREE.SphereGeometry(2, 8, 6)
可以创建一个半径为2
,经度
划分成8
份,纬度
划分成6
份的球体
:
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(2, 8, 6),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(sphere);
效果图:
new THREE.SphereGeometry(2, 8, 16)
的效果如图:new THREE.SphereGeometry(3, 18, 12)
的效果如图:segmentsWidth
相当于经度被切成了几瓣
,而segmentsHeight
相当于纬度被切成了几层
。因为在图形底层的实现中,并没有曲线
的概念,曲线都是由多个折线近似
构成的。对于球体而言,当这两个值较大
的时候,形成的多面体
就可以近似
看做是球体了。经度弧度
new THREE.SphereGeometry(2, 8, 6, Math.PI / 2, Math.PI / 3)
表示起始经度为Math.PI / 6
,经度跨度为Math.PI / 3
。
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(2, 8, 6, Math.PI / 2, Math.PI / 3),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(sphere);
效果图:
SegmentsWidth
为8
意味着对于经度从Math.PI / 2
跨过Math.PI / 3
的区域内
划分为8
块,而不是整个球体的经度划分成8块后再判断在此经度范围内的部分。纬度弧度
理解了经度之后,纬度可以同理理解。new THREE.SphereGeometry(2, 8, 6, 0, Math.PI * 2, Math.PI / 6, Math.PI / 3)
意味着纬度从Math.PI / 6
跨过Math.PI / 3
:
var sphere = new THREE.Mesh(
// 经度起始弧度为0度,经度跨度为 180*2
new THREE.SphereGeometry(2, 8, 6, 0, Math.PI * 2, Math.PI / 6, Math.PI / 3),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(sphere);
效果图:
我们再来看一个经度纬度
都改变了起始位置和跨度
的例子:new THREE.SphereGeometry(2, 8, 6, Math.PI / 2, Math.PI, Math.PI / 6, Math.PI / 2)
:
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(2, 8, 6, Math.PI / 2, Math.PI, Math.PI / 6, Math.PI / 2),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(sphere);
效果图:
圆形(
CircleGeometry
)可以创建圆形或者扇形
,其构造函数是:
new THREE.CircleGeometry(radius, segments, thetaStart, thetaLength)
这里的参数跟绘制圆是一样的,我们再来熟悉一下。radius
是半径;segments
表示切片数;thetaStart
表示纬度开始的弧度
;thetaLength
表示纬度跨过的弧度
。
看个例子: new THREE.CircleGeometry(3, 18, Math.PI / 3, Math.PI / 3 * 4)
可以创建一个在x轴和y轴所在平面的三分之二圆的扇形:
var circle = new THREE.Mesh(
new THREE.CircleGeometry(2, 18, Math.PI / 3, Math.PI / 3 * 4),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(circle);
效果图:
圆柱体(
CylinderGeometry
)的构造函数是:
new THREE.CylinderGeometry(radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded)
radiusTop
与radiusBottom
分别是顶面和底面的半径
,由此可知,当这两个参数设置为不同的值时,实际上创建的是一个圆台;height
是圆柱体的高度
;radiusSegments
与heightSegments
可类比球体中的分段
,一个表示底面、顶面的分段,另一个表示环面的分段;openEnded
是一个布尔值,表示是否没有顶面和底面,缺省值为false
,表示有顶面和底面。标准圆柱体
new THREE.CylinderGeometry(1.5, 1.5, 3, 18, 3)
创建一个顶面与底面半径都为2,高度为4的圆柱体:
var cylinder = new THREE.Mesh(
new THREE.CylinderGeometry(1.5, 1.5, 3, 18, 3),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(cylinder);
效果图:
圆台
顶面、底面半径不一致的时候,即是一个圆台。将底面半径设为2
创建一个圆台:new THREE.CylinderGeometry(1.5, 2, 3, 18, 3)
:
var cylinder = new THREE.Mesh(
new THREE.CylinderGeometry(1.5, 2, 3, 18, 3),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(cylinder);
效果图:
无底面、顶面
openEnded
为true
的时候,将无底面、顶面。new THREE.CylinderGeometry(1.5, 1.5, 3, 18, 3, true)
将创建一个没有顶面与底面的圆柱:
var cylinder = new THREE.Mesh(
new THREE.CylinderGeometry(1.5, 1.5, 3, 18, 3, true),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(cylinder);
效果图:
正四面体(
TetrahedronGeometry
)、正八面体(OctahedronGeometry
)、正二十面体(IcosahedronGeometry
)的构造函数较为类似,分别为:
// 正四面体
new THREE.TetrahedronGeometry(radius, detail)
// 正八面体
new THREE.OctahedronGeometry(radius, detail)
// 正二十面体
new THREE.IcosahedronGeometry(radius, detail)
radius
是半径;detail
是细节层次(Level of Detail
)的层数,对于大面片数模型,可以控制在视角靠近物体时,显示面片数多的精细模型,而在离物体较远时,显示面片数较少的粗略模型。这里我们不对detail多作展开,一般可以对这个值缺省。正四面体
new THREE.TetrahedronGeometry(2.5)
创建一个半径
为2.5
的正四面体:
var tetrahedron = new THREE.Mesh(
new THREE.TetrahedronGeometry(2.5),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(tetrahedron);
效果图:
正八面体
new THREE.OctahedronGeometry(2.5)
创建一个半径为2.5
的正八面体:
var octahedron = new THREE.Mesh(
new THREE.OctahedronGeometry(2.5),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(octahedron);
效果图:
正二十面体
new THREE.IcosahedronGeometry(2.5)
创建一个半径为2.5
的正二十面体:
var icosahedron = new THREE.Mesh(
new THREE.IcosahedronGeometry(2.5),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(icosahedron);
效果图:
圆环面(
TorusGeometry
)就是甜甜圈的形状,其构造函数是:
new THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments, arc)
radius
是圆环半径;tube
是管道半径;radialSegments
与tubularSegments
分别是两个分段数,详见上图;arc
是圆环面的弧度,缺省值为Math.PI * 2
。粗糙圆环面
new THREE.TorusGeometry(2, 0.7, 4, 8)
创建一个粗糙的圆环面:
var torus = new THREE.Mesh(
new THREE.TorusGeometry(2, 0.7, 4, 8),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(torus);
效果图:
精细圆环面
new THREE.TorusGeometry(2, 0.7, 12, 18)
创建一个较为精细的圆环面:
var torus = new THREE.Mesh(
new THREE.TorusGeometry(2, 0.7, 12, 18),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(torus);
效果图:
部分圆环面
new THREE.TorusGeometry(2, 0.7, 4, 8, Math.PI / 3 * 2)
创建部分圆环面:
var torus = new THREE.Mesh(
new THREE.TorusGeometry(2, 0.7, 4, 8, Math.PI / 3 * 2),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(torus);
效果图:
如果说圆环面是甜甜圈,那么圆环结(
TorusKnotGeometry
)就是打了结的甜甜圈,其构造参数为:
new THREE.TorusKnotGeometry(radius, tube, radialSegments, tubularSegments, p, q, heightScale)
前四个参数在圆环面中已经有所介绍,p
和q
是控制其样式
的参数,一般可以缺省,如果需要详细了解,请学习圆环结的相关知识;heightScale
是在z轴
方向上的缩放。
new THREE.TorusKnotGeometry(2, 0.5, 32, 8)
默认样式的圆环结:
var torus = new THREE.Mesh(
new THREE.TorusKnotGeometry(1.6, 0.4, 32, 8),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(torus);
效果图:
文字形状(
TextGeometry
)可以用来创建三维的文字形状。
使用文字前,需要下载和引用额外的字体库。字体库在three.js Github master/examples/fonts目录下,下载里面的
json
文件,放在你的目录下,然后加载。
这里,我们就以helvetiker字体
为例。我们在刚刚的字体库目录下,下载helvetiker_regular.typeface.json
文件放在你的目录下,然后用以下方法加载:
// 调用一个字体加载函数
var loader = new THREE.FontLoader();
loader.load('helvetiker_regular.typeface.json', function(font) {
var mesh = new THREE.Mesh(
new THREE.TextGeometry('Hello', {
font: font,
size: 1,
height: 1
}),
new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
})
);
scene.add(mesh);
// 写在loader函数里面 否则不显示
renderer.render(scene,camera);
});
注意:
73dev
版本的three.js,执行代码的时候发现报错,可能是还没有添加这个功能,所以建议去下载最新版本的three.js。webstorm
编辑器,因为它打开html文件时,就是以本地服务器的方式打开的。或者在cmd命令行中输入live-server
,但需要配置,具体方法请点这里。创建文字形状的流程和之前介绍的基本几何形状是类似的,其构造函数是:
new THREE.TextGeometry(text, parameters)
其中,text
是要显示的文字字符串,parameters
是以下参数组成的对象:
size
:字号大小,一般为大写字母的高度height
:文字的厚度curveSegments
:弧线分段数,使得文字的曲线更加光滑font
:字体,默认是'helvetiker'
,需对应引用的字体文件weight
:值为'normal'
或'bold'
,表示是否加粗style
:值为'normal'
或'italics'
,表示是否斜体bevelEnabled
:布尔值,是否使用倒角,意为在边缘处斜切bevelThickness
:倒角厚度bevelSize
:倒角宽度创建一个三维文字
new THREE.TextGeometry("hello", {size: 1, height: 1})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello</title>
<script type="text/javascript" src="js/three.js"></script>
</head>
<body onload="init()">
<script>
function init() {
// 调用一个渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(800, 600);
document.body.appendChild(renderer.domElement);
renderer.setClearColor(0x000000);
// 调用场景
var scene = new THREE.Scene();
// 调用相机
var camera = new THREE.OrthographicCamera(-4, 4, 3, -3, 0.1, 100);
camera.position.set(5, 5, 20);
camera.lookAt(new THREE.Vector3(1.1, 0, 0));
scene.add(camera);
// 定义材质
var material = new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
});
// 加载文字模板
var loader = new THREE.FontLoader();
loader.load('helvetiker_regular.typeface.json', function(font) {
// 中文字符不能解析
var mesh = new THREE.Mesh(new THREE.TextGeometry('hello', {
font: font,
size: 1,
height: 1
}), material);
scene.add(mesh);
// 渲染一定要在load函数里面
renderer.render(scene, camera);
})
}
</script>
</body>
</html>
我们可以改变材质和添加光照来改变显示效果(灯光、材质不必深究,后面会细讲)
// 将材质改为lambert材质
var material = new THREE.MeshLambertMaterial({
color: 0xffff00
});
// 加上一束方向光
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 0, 0.5);
scene.add(light);
效果图:
对于Three.js没有提供的形状,可以通过自定义形状来创建。
由于自定义形状需要手动指定每个顶点位置,以及顶点连接情况,如果该形状非常复杂,程序员计算量就会比较大。这种情况,建议使用建模工具,创建好之后,再通过three.js导入到场景中,这样会十分高效、方便。
自定义形状使用的是Geometry
类,它是其他如CubeGeometry、SphereGeometry等几何形状的父类,其构造函数是:
new THREE.Geometry()
我们以创建一个梯台为例,首先,初始化一个几何形状,然后设置顶点位置以及顶点连接情况。
geometry
创建点的时候都是push
到数组vertices
里面的Face3
的方法将3
点连成一个三角面
看代码
// 初始化几何形状
var geometry = new THREE.Geometry();
// 设置顶点的位置
// 顶部4个点
geometry.vertices.push(new THREE.Vector3(-1, 2, -1));
geometry.vertices.push(new THREE.Vector3(1, 2, -1));
geometry.vertices.push(new THREE.Vector3(1, 2, 1));
geometry.vertices.push(new THREE.Vector3(-1, 2, 1));
// 底部4顶点
geometry.vertices.push(new THREE.Vector3(-2, 0, -2));
geometry.vertices.push(new THREE.Vector3(2, 0, -2));
geometry.vertices.push(new THREE.Vector3(2, 0, 2));
geometry.vertices.push(new THREE.Vector3(-2, 0, 2));
// 设置顶点连接情况
// 顶面
geometry.faces.push(new THREE.Face3(0, 1, 3));
geometry.faces.push(new THREE.Face3(1, 2, 3));
// 底面
geometry.faces.push(new THREE.Face3(4, 5, 6));
geometry.faces.push(new THREE.Face3(5, 6, 7));
// 四个侧面
geometry.faces.push(new THREE.Face3(1, 5, 6));
geometry.faces.push(new THREE.Face3(6, 2, 1));
geometry.faces.push(new THREE.Face3(2, 6, 7));
geometry.faces.push(new THREE.Face3(7, 3, 2));
geometry.faces.push(new THREE.Face3(3, 7, 0));
geometry.faces.push(new THREE.Face3(7, 4, 0));
geometry.faces.push(new THREE.Face3(0, 4, 5));
geometry.faces.push(new THREE.Face3(0, 5, 1));
效果图:
总结:
new THREE.Vector3(-1, 2, -1)
创建一个矢量
,作为顶点位置追加到geometry.vertices
数组中。new THREE.Face3(0, 1, 3)
创建一个三个顶点组成的面片
,追加到geometry.faces
数组中。三个参数分别是四个顶点在geometry.vertices
中的序号。材质(
material
),是独立于物体顶点信息之外的与渲染效果相关的属性。通过设置材质可以改变物体颜色、纹理贴图、光照模式等。
基本材质
、两种基于光照模型材质
、法向量作为材质
、 图像作为材质
。使用基本材质(
BasicMaterial
)的物体,渲染后物体的颜色,始终为该材质的颜色
,不会由于光照产生明暗、阴影效果。如果没有指定材质的颜色,则颜色是随机的,构造函数如下:
new THREE.MeshBasicMaterial(opt)
其中参数opt
可以缺省,或者为包含各属性的值。如,为一个黄色正方体添加一个1不透明度 (opacity)
:
new THREE.MeshBasicMaterial({
color: 0xffff00,
opacity: 0.75
});
示例代码: