本文所使用到的demo演示:javascript
计算机图形学中的纹理既包括一般意义上物体表面的纹理即便物体表面呈现凹凸不平的沟纹,同时也包括在物体的光滑表面上的彩色图案,所谓的纹理映射就是在物体的表面上绘制彩色的图案。php
在three.js中使用纹理能够实现不少不一样的效果,可是最基本的就是为网格体的每一个像素指定颜色。等同于将一张纹理图片应用在一个几何体的材质上。css
使用的方式很简单,只须要设置
material.map = 须要设置的纹理对象
纹理对象的得到方式也很简单,只须要使用THREE.TextureLoader().load(url)函数就能够为url指定路径的纹理图片建立一个对象。具体的使用方式以下:html
1 let texture = new THREE.TextureLoader().load("../../../Image/metal-rust.jpg");
2 let material = new THREE.MeshBasicMaterial();
3 material.map = texture;
4 let geometry = new THREE.BoxGeometry(10,10,10);
5 let cube = new THREE.Mesh(geometry,material);
6 scene.add(cube);
复制代码
其中,"../../../Image/metal-rust.jpg"是我使用的纹理的路径,图片就是下面这一张
java
就拿tga格式举例,咱们要加载tga格式的纹理,首先须要引用TGALoader.js这个文件,而后建立一个tga格式的加载器
let loader = new THREE.TGALoader();
咱们就可使用loader这个加载器,像上面同样的加载tga格式的纹理了。
具体代码以下:git
1 let loader = new THREE.TGALoader();
2 let texture = loader.load("../../../Image/crate_color8.tga");
3 let material = new THREE.MeshBasicMaterial();
4 material.map = texture;
5 let geometry = new THREE.BoxGeometry(10,10,10);
6 let cube = new THREE.Mesh(geometry,material);
7 scene.add(cube);
复制代码
下面是我使用的tga格式的纹理图片(只能上传截图,tga格式图片的这里上传不了)
github
凹凸纹理用于为材质添加厚度与深度,如字面意思同样,可让材质看上去是凹凸不平的。凹凸贴图只包含像素的相对高度,像素的密集程度定义凹凸的高度,因此想要让物体好看,首先仍是应该设置一个普通的纹理,再在这个基础上添加一个凹凸纹理,就能够实现凹凸不平的物体效果。
凹凸贴图的建立方法很简单,和普通纹理相似,只是咱们设置的不是map,而是bumpMap
material.bumpMap = 须要设置的纹理对象web
特别须要注意的是,这里的材质只能使用MeshPhongMaterial,凹凸贴图才会有效果。
具体的设置方法以下:算法
1 let geom = new THREE.BoxGeometry(10, 10, 10);
2
3 //建立普通纹理材质
4 let texture = new THREE.TextureLoader().load("../../../Image/stone.jpg");
5 let material = new THREE.MeshPhongMaterial({
6 map:texture
7 });
8 cube = new THREE.Mesh(geom,material);
9 cube.position.set(-7,0,0);
10 scene.add(cube);
11
12 //建立凹凸纹理材质
13 let bumpTexture = new THREE.TextureLoader().load("../../../Image/stone-bump.jpg");
14 let bumpMaterial = new THREE.MeshPhongMaterial({
15 map:texture,
16 bumpMap:bumpTexture,
17 bumpScale:2
18 });
19 bumpCube = new THREE.Mesh(geom,bumpMaterial);
20 bumpCube.position.set(7,0,0);
21 scene.add(bumpCube);
复制代码
其中material.bumpScale能够设置凹凸的高度,若是为负值,则表示的是深度。浏览器
运行程序截图以下:
左边材质的是普通的纹理贴图,右边的材质是带有凹凸纹理的,当前bumpScale设置的是2,二者看上去有比较明显的不一样
咱们能够发现,凹凸图只包含了像素的相对高度,没有任何的倾斜的方向信息,因此使用凹凸纹理能表达的深度信息有限,若是想用实现更多的细节可使用下面介绍的法向贴图。
法向贴图保存的不是高度的信息,而是法向量的信息,咱们使用法向贴图,只须要不多的顶点和面就能够实现很丰富的细节。
一样的,实现法向贴图和凹凸贴图也很相似,只须要设置
material.normalMap = 须要设置的纹理对象
一样也是在MeshPhongMaterial材质中才有效果,还要注意的一点是设置normalScale指定材质的凹凸程度时,normalScale须要接受的是一个THREE.Vector2类型
具体的代码以下:
1 let geom = new THREE.BoxGeometry(10, 10, 10);
2
3 //建立普通纹理材质
4 let texture = new THREE.TextureLoader().load("../../../Image/plaster.jpg");
5 let material = new THREE.MeshPhongMaterial({
6 map:texture
7 });
8 cube = new THREE.Mesh(geom,material);
9 cube.position.set(-7,0,0);
10 scene.add(cube);
11
12 //建立凹凸纹理材质
13 let normalTexture = new THREE.TextureLoader().load("../../../Image/plaster-normal.jpg");
14 let normalMaterial = new THREE.MeshPhongMaterial({
15 map:texture,
16 normalMap:normalTexture,
17 normalScale:new THREE.Vector2(1,1)
18 });
19 normalCube = new THREE.Mesh(geom,normalMaterial);
20 normalCube.position.set(7,0,0);
21 scene.add(normalCube);
复制代码
场景以下图,右边的是带有法向纹理的物体,明显感受出材质的细节多出来了不少。
若是咱们想在场景中添加阴影,three.js给咱们提供了renderer.shadowMapEnabled = true这个办法,可是这对于资源的消耗是很大的。若是咱们只是须要对静态的物体添加阴影效果,咱们就有一种开销很小的办法,那就是光照贴图。
光照贴图是预先渲染好的阴影贴图,能够用来模拟真实的阴影。咱们能使用这种技术建立出分辨率很高的阴影,而且不会损耗渲染的性能。由于是提早根据场景渲染好的,因此只对静态的场景有效。
好比下面这张光照贴图:
1 let lightMap = new THREE.TextureLoader().load("../../../Image/lm-1.png");
2 let map = new THREE.TextureLoader().load("../../../Image/floor-wood.jpg");
3 //建立地板
4 let planeGeo = new THREE.PlaneGeometry(95,95,1,1);
5 planeGeo.faceVertexUvs[1] = planeGeo.faceVertexUvs[0];
6 let planeMat = new THREE.MeshBasicMaterial({
7 color:0x999999,
8 lightMap:lightMap,//在地板的材质上添加光照贴图
9 map:map//地板的普通纹理材质
10 });
11 let plane = new THREE.Mesh(planeGeo,planeMat);
12 plane.rotation.x = -Math.PI / 2;
13 plane.position.y = 0;
14 scene.add(plane);
15
16 //建立大的cube
17 var boxGeo = new THREE.BoxGeometry(12,12,12);
18 var material = new THREE.MeshBasicMaterial();
19 material.map = new THREE.TextureLoader().load("../../../Image/stone.jpg");
20 var box = new THREE.Mesh(boxGeo,material);
21 box.position.set(0.9,6,-12);
22 scene.add(box);
23
24 //建立小的cube
25 var boxGeo = new THREE.BoxGeometry(6, 6, 6);
26 var material = new THREE.MeshBasicMaterial();
27 material.map = new THREE.TextureLoader().load("../../../Image/stone.jpg");
28 var box = new THREE.Mesh(boxGeo,material);
29 box.position.set(-13.2, 3, -6);
30 scene.add(box);
复制代码
其中,planeGeo.faceVertexUvs[1] = planeGeo.faceVertexUvs[0] 这句话是咱们须要明确的指定光照贴图的uv映射(将纹理的哪一部分应用在物体表面)这样才能将光照贴图的使用和其余的纹理分别开来。
planeGeo.faceVertexUvs保存的就是几何体面的uv映射信息,咱们将faceVertexUvs[0]层的信息保存到faceVertexUvs[1]层
faceVertexUvs的官方文档解释:
.faceVertexUvs : Array
Array of face UV layers, used for mapping
textures onto the geometry. Each UV layer is an array of UVs matching
the order and number of vertices in faces.
运行结果如图:
高光是光源照射到物体而后反射到人的眼睛里时,物体上最亮的那个点就是高光,高光不是光,而是物体上最亮的部分。
而高光贴图就是高光贴图是反应光线照射在物体表面的高光区域时所产生的环境反射,它的做用是反映物体高光区域效果。
经过高光贴图,咱们能够为材质建立一个闪亮的、色彩明快的贴图。高光贴图的黑色部分会暗淡,而白色的部分会比较的亮。
建立高光贴图的方法也和前面差很少
material.specularMap= 须要设置的纹理对象
具体的代码以下:
1 let map = new THREE.TextureLoader().load("../../../Image/Earth.png");
2 let specularMap = new THREE.TextureLoader().load("../../../Image/EarthSpec.png");
3 let normalMap = new THREE.TextureLoader().load("../../../Image/EarthNormal.png");
4 let sphereMaterial = new THREE.MeshPhongMaterial({
5 map:map,
6 specularMap:specularMap,
7 normalMap:normalMap,
8 normalScale:THREE.Vector2(2,2),
9 specular:0x0000ff,
10 shininess:2
11 });
12 let sphereGeometry = new THREE.SphereGeometry(30,30,30);
13 let sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);
14 scene.add(sphere);
复制代码
这段代码建立了一个球体,并为球体的材质贴上了普通纹理,法向纹理和高光纹理,其中specular属性能够决定反光的颜色,shininess能够决定发光的亮度。
运行出来的样子以下:
能够看到,海洋的地方比较亮,而大陆的的颜色相对较暗。
用到的几张纹理图:
若是咱们想要在场景中建立反光的物体,一般会使用光线追踪的算法,可是这对cpu的消耗是巨大的,可是环境贴图就给咱们创造了更容易的方法,咱们只须要使用给物体的材质贴上环境贴图,就能够模拟反光的效果。
首先咱们的场景须要有一个环境,这个环境咱们可使用CubeTextureLoader()来建立。在前面的文章里曾经介绍过如何建立360度全景的环境,这个CubeTextureLoader()和那里面用到的实际上是同样的,只是版本的更替,如今更多使用这个函数。
具体用法是:
1 let cubeMap = new THREE.CubeTextureLoader().setPath(
2 "../../../Image/MapCube/Bridge2/").load(
3 [
4 'posx.jpg',
5 'negx.jpg',
6 'posy.jpg',
7 'negy.jpg',
8 'posz.jpg',
9 'negz.jpg'
10 ]);
11 scene = new THREE.Scene();
12 scene.background = cubeMap;
复制代码
在前面的文章已经介绍过,这里就再也不赘述。
建立cubeMap所用到的图片在http://www.humus.name/index.php?page=Textures能够直接下载。
咱们有了一个能够反射的环境之后,就能够开始为咱们的物体建立材质贴图了。
建立材质贴图的方式和前面仍是差很少
material.envMap = scene.background;
scene.background就是咱们刚刚所建立的场景的背景,这样材质的环境贴图就至关于贴上了周围环境,从摄像机去看物体的话,看上去就是对环境有一个反射的效果了。
建立的代码以下:
1 function initObject()
2 {
3 let material = new THREE.MeshPhongMaterial();
4 material.envMap = scene.background;
5 let boxGeometry = new THREE.BoxGeometry(5,50,50);
6 let box = new THREE.Mesh(boxGeometry,material);
7 box.position.set(-70,0,-10);
8 box.rotation.y-=Math.PI/2;
9 scene.add(box);
10 let sphereGeometry = new THREE.SphereGeometry(30,30,30);
11 let sphere = new THREE.Mesh(sphereGeometry,material);
12 sphere.position.set(70,0,-10);
13 scene.add(sphere);
14 }
复制代码
和前面的代码没有太大的区别,这里主要是建立了两个物体,都使用的相同环境贴图的材质。
运行的结果:
值得注意的是,咱们使用环境贴图建立的材质仅仅静态的环境贴图。咱们只能看到物体上面有周围环境的反射,看不到物体对其余物体的反射。
若是咱们要看到物体对其余物体的反射,咱们可使用一个新的对象——cubeCamera
建立cubeCamera的方法很简单.
let cubeCamera = new THREE.CubeCamera(0.1, 2000, 2048); scene.add(cubeCamera);
其中:
第一个参数0.1是相机的近裁剪距离 第二个参数2000是相机远裁剪距离 第三个参数2048是相机分辨率
使用THREE.CubeCamera能够为场景中所要渲染的物体建立快照,并使用这些快照建立CubeMap对象。可是须要确保摄像机被放置在THREE.Mesh网格上你所想显示反射的位置上。例如,咱们想在球体的中心显示反射,因为球体所处的位置是(0, 0, 0),因此咱们没有显示的指定THREE.CubeCamera的位置。咱们只是将动态反射应用于球体上,因此把它的envMap设置为cubeCamera.renderTarget
即material.envMap = cubeCamera.renderTarget;
简单来讲,就是把咱们所要显示反射的“镜子”的material.envMap设置为cubeCamera.renderTarget,同时还要把cubeCamera的位置设置到镜子的位置,cubeCamera.position.copy(镜子.position);
代码以下:
1 let loader = new THREE.STLLoader();
2 loader.load("../../../asset/LibertStatue.obj.stl",function (bufferGeometry)
3 {
4 let material = new THREE.MeshBasicMaterial();
5 material.envMap=scene.background;
6 obj = new THREE.Mesh(bufferGeometry,material);
7 obj.scale.set(50,50,50);
8 scene.add(obj);
9 });//加载stl模型
10
11 let cubeMaterial = new THREE.MeshPhongMaterial();
12 cubeMaterial.envMap = cubeCamera.renderTarget;
13 let boxGeometry = new THREE.BoxGeometry(3, 400, 400);
14 let box = new THREE.Mesh(boxGeometry, cubeMaterial);
15 box.position.set(0, 0, -300);
16 box.rotation.y -= Math.PI / 2;
17 scene.add(box);
18 cubeCamera.position.copy(box.position);
复制代码
这段代码中,咱们从外部加载了一个stl格式的模型,也能够就使用简单的几何体来演示。下面的一部分代码就建立了能够反射的镜子。
最后,咱们还须要在render()中添加cubeCamera.update(renderer, scene)用cubeCamera进行渲染
1function render()
2 {
3 if(obj) obj.rotation.y+=0.02;
4 cubeCamera.update(renderer, scene);
5 stats.update();
6 renderer.clear();
7 requestAnimationFrame(render);
8 renderer.render(scene, camera);
9 }
复制代码
运行后的状况以下:
以上就是介绍的所有类型的纹理。
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Environment Map</title>
6 <script src="../../../import/three.js"></script>
7 <script src="../../../import/stats.js"></script>
8 <script src="../../../import/Setting.js"></script>
9 <script src="../../../import/OrbitControls.js"></script>
10 <script src="../../../import/STLLoader.js"></script>
11 <style type="text/css">
12 body {
13 border: none;
14 cursor: pointer;
15 width: 100%;
16 height: 1000px;
17 /*全屏显示的设置*/
18 margin: 0;
19 overflow: hidden; /*消除浏览器的滚动条*/
20
21 }
22
23 /*加载动画*/
24 #loading {
25 width: 100%;
26 height: 850px;
27 background-color: #333333;
28 }
29
30 #spinner {
31 width: 100px;
32 height: 100px;
33 position: fixed;
34 top: 50%;
35 left: 50%;
36 }
37
38 .double-bounce1, .double-bounce2 {
39 width: 100%;
40 height: 100%;
41 border-radius: 50%;
42 background-color: #67CF22;
43 opacity: 0.6;
44 position: absolute;
45 top: 0;
46 left: 0;
47 -webkit-animation: bounce 2.0s infinite ease-in-out;
48 animation: bounce 2.0s infinite ease-in-out;
49 }
50
51 .double-bounce2 {
52 -webkit-animation-delay: -1.0s;
53 animation-delay: -1.0s;
54 }
55
56 @-webkit-keyframes bounce {
57 0%, 100% {
58 -webkit-transform: scale(0.0)
59 }
60 50% {
61 -webkit-transform: scale(1.0)
62 }
63 }
64
65 @keyframes bounce {
66 0%, 100% {
67 transform: scale(0.0);
68 -webkit-transform: scale(0.0);
69 }
70 50% {
71 transform: scale(1.0);
72 -webkit-transform: scale(1.0);
73 }
74 }
75 </style>
76</head>
77<body onload="Start()">
78<!--加载动画的div-->
79<div id="loading">
80 <div id="spinner">
81 <div class="double-bounce1"></div>
82 <div class="double-bounce2"></div>
83 </div>
84</div>
85<script>
86 let camera, renderer, scene, cubeCamera, light;
87 let controller;
88
89 function initThree()
90 {
91 //渲染器初始化
92 renderer = new THREE.WebGLRenderer({
93 antialias: true
94 });
95 renderer.setSize(window.innerWidth, window.innerHeight);
96 renderer.setClearColor(0x333333);
97 document.body.appendChild(renderer.domElement);//将渲染添加到body中
98 //初始化摄像机,这里使用透视投影摄像机
99 camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 10000);
100 camera.position.set(20, 15, 200);
101 camera.up.x = 0;//设置摄像机的上方向为哪一个方向,这里定义摄像的上方为Y轴正方向
102 camera.up.y = 1;
103 camera.up.z = 0;
104 camera.lookAt(0, 0, 0);
105
106 let cubeMap = new THREE.CubeTextureLoader().setPath("../../../Image/MapCube/Bridge2/").load(
107 [
108 'posx.jpg',
109 'negx.jpg',
110 'posy.jpg',
111 'negy.jpg',
112 'posz.jpg',
113 'negz.jpg'
114 ]);
115 scene = new THREE.Scene();
116 scene.background = cubeMap;
117
118 cubeCamera = new THREE.CubeCamera(0.1, 1000, 2048);
119 scene.add(cubeCamera);
120 //相机的移动
121 controller = new THREE.OrbitControls(camera, renderer.domElement);
122 controller.target = new THREE.Vector3(0, 0, 0);
123
124 light = new THREE.AmbientLight(0xffffff);
125 light.position.set(-50, -50, -50);
126 scene.add(light);
127 }
128
129 let obj;
130 function initObject()
131 {
132 let loader = new THREE.STLLoader();
133 loader.load("../../../asset/LibertStatue.obj.stl",function (bufferGeometry)
134 {
135 let material = new THREE.MeshBasicMaterial();
136 material.envMap=scene.background;
137 obj = new THREE.Mesh(bufferGeometry,material);
138 obj.scale.set(50,50,50);
139 scene.add(obj);
140 console.log(obj);
141 });
142 let cubeMaterial = new THREE.MeshPhongMaterial();
143 cubeMaterial.envMap = cubeCamera.renderTarget;
144 let boxGeometry = new THREE.BoxGeometry(3, 400, 400);
145 let box = new THREE.Mesh(boxGeometry, cubeMaterial);
146 box.position.set(0, 0, -300);
147 box.rotation.y -= Math.PI / 2;
148 scene.add(box);
149 cubeCamera.position.copy(box.position);
150 document.getElementById('loading').style.display = 'none';
151 }
152 //渲染函数
153 function render()
154 {
155 if(obj) obj.rotation.y+=0.02;
156 cubeCamera.update(renderer, scene);
157 stats.update();
158 renderer.clear();
159 requestAnimationFrame(render);
160 renderer.render(scene, camera);
161 }
162
163 //功能函数
164 function setting()
165 {
166 loadFullScreen();
167 loadAutoScreen(camera, renderer);
168 loadStats();
169 }
170
171 //运行主函数
172 function Start()
173 {
174 initThree();
175 initObject();
176 setting();
177 render();
178 }
179</script>
180</body>
181</html>
182
复制代码