demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>First Scene</title>
<script type="text/javascript" src="js/three.js"></script>
<style>
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<!-- Div which will hold the Output -->
<div id="WebGL-output"></div>
<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">
// once everything is loaded, we run our Three.js stuff.
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
// create a render and set the size
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xeeeeee));
renderer.setSize(window.innerWidth, window.innerHeight);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
wireframe: true,
});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// position the cube
cube.position.set(-4, 3, 0);
// add the cube to the scene
scene.add(cube);
// position and point the camera to the center of the scene
camera.position.set(-30, 40, 30);
camera.lookAt(scene.position);
// add the output of the renderer to the html element
document
.getElementById("WebGL-output")
.appendChild(renderer.domElement);
// render the scene
renderer.render(scene, camera);
}
window.onload = init;
</script>
</body>
</html>
几何体
three.js 提供了许多种几何体。
使用几何体的方法大致如下
1
2
3
4
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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 平面: PlaneGeometry
PlaneGeometry(width : Float, height : Float, widthSegments : Integer, heightSegments : Integer)
// 圆面: CircleGeometry
CircleGeometry(radius : Float, segments : Integer, thetaStart : Float, thetaLength : Float)
// 圆环: RingGeometry
RingGeometry(innerRadius : Float, outerRadius : Float, thetaSegments : Integer, phiSegments : Integer, thetaStart : Float, thetaLength : Float)
// 长方体: BoxGeometry
BoxGeometry(width : Float, height : Float, depth : Float, widthSegments : Integer, heightSegments : Integer, depthSegments : Integer)
// 球体: SphereGeometry
SphereGeometry(radius : Float, widthSegments : Integer, heightSegments : Integer, phiStart : Float, phiLength : Float, thetaStart : Float, thetaLength : Float)
// 柱体: CylinderGeometry(顶部半径, 底部半径, 高度, 半径片段数, ...)
CylinderGeometry(radiusTop : Float, radiusBottom : Float, height : Float, radialSegments : Integer, heightSegments : Integer, openEnded : Boolean, thetaStart : Float, thetaLength : Float)
// 椎体: ConeGeometry
ConeGeometry(radius : Float, height : Float, radialSegments : Integer, heightSegments : Integer, openEnded : Boolean, thetaStart : Float, thetaLength : Float)
// 正八面体: OctahedronGeometry(半径, 细节数)
OctahedronGeometry(radius : Float, detail : Integer)
// 正十二面体: DodecahedronGeometry(半径, 细节数)
DodecahedronGeometry(radius : Float, detail : Integer)
// 正二十面体: IcosahedronGeometry(半径, 细节数)
IcosahedronGeometry(radius : Float, detail : Integer)
thetaStart
- 旋转体的初始角,默认值 0
thetaLength
- 旋转体的终止角,默认值 2PI
光源
基础光源
环境光
环境光无差别的照亮场景中的物体,环境光没有方向,因此也不能用来投射阴影。
1
2
3
// AmbientLight( color : Integer, intensity : Float )
var light = new THREE.AmbientLight(0x404040); // soft white light
scene.add(light);
点光
点光由一个点向所有方向投射光线,点光可以投射阴影。
1
PointLight( color : Integer, intensity : Float, distance : Number, decay : Float )
intensity
光线强度,越大越强,默认 1。
distance
光线强度变为 0 的距离,设置为 0,则光线不会终止,默认 0。
decay
衰退,随着距离增加光线的衰退量,默认 1。
1
2
3
var light = new THREE.PointLight(0xff0000, 1, 100);
light.position.set(50, 50, 50);
scene.add(light);
聚光
聚光由一点向一个方向投射光线,光照范围大致是个圆锥,聚光可以投射阴影。
1
SpotLight( color : Integer, intensity : Float, distance : Float, angle : Radians, penumbra : Float, decay : Float )
1
2
3
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(100, 1000, 100);
scene.add(spotLight);
方向光
DirectionalLight
- 方向光
特殊光源
材质
贴图
1
2
3
4
5
6
7
var loader = new THREE.TextureLoader();
loader.load("models/wood.JPG", function (texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat = new THREE.Vector2(30, 30);
floor.material.map = texture;
floor.material.needsUpdate = true;
});
凹凸贴图
Bumping Map
法线贴图
Normal Mapping
粒子
动画
选择物体
选择物体可以使用 THREE.Raycaster
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 使用一个二维向量存储鼠标的位置
var mouse = new THREE.Vector2();
// 存储被选择的物体
var INTERSECTED;
var raycaster = new THREE.Raycaster();
function onDocumentMouseMove(event) {
event.preventDefault();
// 将坐标标准化到 (-1,1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
// 注册dom事件
document.addEventListener("mousemove", onDocumentMouseMove, false);
function animate() {
// setFromCamera(坐标, 射线原点)
raycaster.setFromCamera(mouse, camera);
// intersectObjects(需要判断相交的对象, 是否递归)
var intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
INTERSECTED = intersects[0].object;
// ...
}
// ...
}
// ...
加载模型
加载 obj 模型
Three.js 提供了加载 obj 模型的 js 库 OrbitControls.js
,导入即可使用。
1
<script type="text/javascript" src="js/OBJLoader.js"></script>
核心代码十分简单
1
2
3
4
var loader = new THREE.OBJLoader();
loader.load("models/house.obj", function (object) {
scene.add(object);
});
加载带 mtl 的 obj 模型
效果如图所示,代码也不复杂。
首先导入加载 obj,mtl 的 js 库。
1
2
<script type="text/javascript" src="js/OBJLoader.js"></script>
<script type="text/javascript" src="js/MTLLoader.js"></script>
接着调用 MTLLoader 加载 mtl,在 mtl 的回调中再调用 OBJLoader 加载 obj 即可。
1
2
3
4
5
6
7
8
9
10
11
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load("models/BrickRound.mtl", function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.load("models/BrickRound.obj", function (object) {
object.scale.set(2, 2, 2);
scene.add(object);
});
});
注意一下我们使用的 mtl 文件,最后一行绑定了纹理贴图。
# Blender MTL File: 'BrickRound.blend'
# Material Count: 1
newmtl Material.001_BrickRound0105_5_SPEC.png
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd models/BrickRound0105_5_SPEC.png
加载 fbx 格式的模型
特效
我们可以使用 EffectComposer
给画面添加一些特效。而 EffectComposer
可以使用多个 Pass
来生成画面。
首先导入 EffectComposer
库,由于 EffectComposer
依赖于 CopyShader
和 ShaderPass
,所以这两个也要加上。
1
2
3
<script src="js/CopyShader.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script src="js/postprocessing/EffectComposer.js"></script>
接着就可以添加一些特效 Pass
,可选的 Pass
有很多,每个 Pass
需要的参数和可调的参数都各不相同。
1
2
<script src="js/postprocessing/OutlinePass.js"></script>
<script src="js/postprocessing/RenderPass.js"></script>
EffectComposer
的使用方法大致如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var composer = new THREE.EffectComposer(renderer);
// 添加一个RenderPass,用于输入secen的场景
var renderPass = new THREE.RenderPass(scene, camera);
composer.addPass(renderPass);
// 添加一个OutlinePass 增加描边特效
var outlinePass = new THREE.OutlinePass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
scene,
camera
);
// outlinePass 需要设置选中的物体
outlinePass.selectedObjects = [obj3d];
// 设置描边颜色
outlinePass.visibleEdgeColor.set("#ff0000");
composer.addPass(outlinePass);
// 添加一个 ShaderPass(CopyShader) 将 outlinePass 的结果输出
var copyPass = new THREE.ShaderPass(THREE.CopyShader);
composer.addPass(copyPass);
// 注意给最后的Pass设置renderToScreen,否则不会输出到屏幕
copyPass.renderToScreen = true;
另外我们还需要修改渲染循环使用 composer.render()
代替 renderer.render()
。
1
2
3
4
function animate() {
requestAnimationFrame(animate);
composer.render();
}
最终结果如下:
不是所有的 Pass
都会输出到屏幕,我们可以使用 ShaderPass(CopyShader)
来将结果输出。
有多个 RenderPass
时,要注意把 clear
设置为 false
。
多场景
显示多个场景,可以使用 div 将页面分割。
在渲染方法中, 调用 renderer.setScissorTest( true );
使裁剪生效。
调用 renderer.setViewport
设置渲染范围,
调用 renderer.setScissor
设置裁剪范围,
最后调用 renderer.render( scene, camera );
渲染页面。
有多个场景的情况下,上述代码将调用多次。
1
2
3
4
5
6
7
8
9
renderer.setScissorTest(true);
renderer.setViewport(left, top, width, height);
renderer.setScissor(left, top, width, height);
var camera = scene.userData.camera;
var update = scene.userData.update;
if (update != null && update instanceof Function) {
update(delta);
}
renderer.render(scene, camera);
对于 Controls 类,通常使用第二个参数传入 DOM 限制作用范围。
1
var controls = new THREE.OrbitControls(camera, scene.userData.element);
另外我们还需要注意 <canvas>
<div>
的层次,如果 <div>
被 <canvas>
遮挡,事件可能无法传递。这时我们可以使用 CSS 的 z-index
来提高 div 的层次,注意 z-index
只有在 position
存在时才会生效(position 为默认值 static 时,z-index 不生效)。
1
2
3
4
#main {
position: relative;
z-index: 1;
}
模型格式说明
obj 文件
mtl 文件
Material Library
mtl 文件是由多个这样的片段组成的
newmtl wire_224198087
Ns 32
d 1
Tr 0
Tf 1 1 1
illum 2
Ka 0.8784 0.7765 0.3412
Kd 0.8784 0.7765 0.3412
Ks 0.3500 0.3500 0.3500
map_Kd BrickRound.png
这一行描述的是材质的名称,在这里是 wire_224198087
newmtl wire_224198087
这一段描述了颜色和光照信息
Ns 32
d 1
illum 2
Ka 0.8784 0.7765 0.3412
Kd 0.8784 0.7765 0.3412
Ks 0.3500 0.3500 0.3500
Ka
指定环境光的反射率,其中 a 是 ambient。
Kd
指定漫反射的反射率,其中 d 是 diffuse。
Ks
指定镜面反射的反射率,其中 s 是 specular。
Ka,Kd,Ks 后面的三个数分别对应 r g b ,取值范围为 0 到 1。
illum
指定光了照模式,有 0 到 10 共计 11 中模式, 用来指定是否启用颜色,环境光,反射,透明,阴影等等。
d
dissolve,溶解,大致是指透明度,1 完全不透明,0 完全透明。
Ns
镜面反射指数,通常值在 0 到 1000 之间,值越大,镜面感越强,值很小时几乎观察不到镜面反射。
Ni
光密度,范围通常在 1 到 10 之间,值为 1 时,代表光穿过物体时不发生弯曲,值越大,弯曲越多,玻璃的光密度大约是 1.5。
描述贴图的信息通常以 map 作为前缀
map_Ka -s 1 1 1 -o 0 0 0 -mm 0 1 chrome.mpc
map_Kd -s 1 1 1 -o 0 0 0 -mm 0 1 chrome.mpc
map_Ks -s 1 1 1 -o 0 0 0 -mm 0 1 chrome.mpc
map_Ns -s 1 1 1 -o 0 0 0 -mm 0 1 wisp.mps
map_d -s 1 1 1 -o 0 0 0 -mm 0 1 wisp.mps
disp -s 1 1 .5 wisp.mps
decal -s 1 1 1 -o 0 0 0 -mm 0 1 sand.mps
bump -s 1 1 1 -o 0 0 0 -bm 1 sand.mpb
map_Kd 渲染时,纹理的值会乘以 Kd
map_Kd 的格式为 map_Kd -options args filename
options 大致有如下这些
-blendu on | off
-blendv on | off
-clamp on | off
-imfchan r | g | b | m | l | z
-mm base gain
-o u v w
-s u v w
-t u v w
-texres value
-o
纹理偏移量
-s
纹理的缩放值,用来拉伸或是收缩纹理,默认值 1 1 1
u
是纹理水平方向上的值
v
是纹理垂直方向上的值
w
是 3D 纹理的深度值
fbx 文件
fbx 是 Autodesk
公司所拥有的格式 (目前 3ds max 和 maya 都是 Autodesk 公司的产品)。
fbx 有两个版本,一个是 ASCII 的,一个是二进制的,二进制的版本没有公开的规格说明,官方提供了读写 sdk。
FBX 可以存储 模型,材质,贴图,动画,光线,相机等等。
词汇
English | 中文 |
---|---|
rigged | 绑定骨骼的 |
seamless | 无缝的 |
术语
FXAA
Fast Approximate Anti-Aliasing 快速近似抗锯齿
OpenGL Programming/Post-Processing
Post-processing are effects applied after the main OpenGL scene is rendered.