本文所使用到的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
複製代碼