一、概念
3D世界的紋理由圖片組成。將紋理以一定的規則映射到幾何體上,一般是三角形上,那么這個幾何體就有紋理皮膚了。
那么在threejs中,或者任何3D引擎中,紋理應該怎么來實現呢?首先應該有一個紋理類,其次是有一個加載圖片的方法,將這張圖片和這個紋理類捆綁起來。
在threejs中,紋理類由THREE.Texture表示,其構造函數如下所示:
THREE.Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy )
各個參數的意義是:
- Image:這是一個圖片類型,基本上它有ImageUtils來加載,如下代碼var image = THREE.ImageUtils.loadTexture(url); // url 是一個http://xxxx/aaa.jpg 的類似地址,javascript沒有從本地加載數據的能力,所以沒有辦法從您電腦的C盤加載數據。
- Mapping:是一個THREE.UVMapping()類型,它表示的是紋理坐標。
- wrapS:表示x軸的紋理的回環方式,就是當紋理的寬度小於需要貼圖的平面的寬度的時候,平面剩下的部分應該以何種方式貼圖的問題。
- wrapT:表示y軸的紋理回環方式。 magFilter和minFilter表示過濾的方式,這是OpenGL的基本概念。
- format:表示加載的圖片的格式,這個參數可以取值THREE.RGBAFormat,RGBFormat等。THREE.RGBAFormat表示每個像素點要使用四個分量表示,分別是紅、綠、藍、透明來表示。RGBFormat則不使用透明,也就是說紋理不會有透明的效果。
- type:表示存儲紋理的內存的每一個字節的格式,是有符號,還是沒有符號,是整形,還是浮點型。不過這里默認是無符號型(THREE.UnsignedByteType)。
- anisotropy:各向異性過濾。使用各向異性過濾能夠使紋理的效果更好,但是會消耗更多的內存、CPU、GPU時間。
紋理坐標:
在正常的情況下,你在0.0到1.0的范圍內指定紋理坐標。我們來簡單看一下紋理坐標如下圖:
當我們用一幅圖來做紋理的時候,那么這幅圖就隱示的被賦予了如圖一樣的紋理坐標,這個紋理坐標將被對應到一個形狀上。
二、實例
<!DOCTYPE html> <html lang="en"> <head> <title></title> <meta charset="utf-8"> <style> body { margin: 0px; background-color: #000000; overflow: hidden; } </style> </head> <body> <script src="../js/three.js"></script> <script> var camera, scene, renderer; var mesh; init(); animate(); function init() { renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); // camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 ); camera.position.z = 400; scene = new THREE.Scene(); var geometry = new THREE.PlaneGeometry( 500, 300, 1, 1 ); geometry.vertices[0].uv = new THREE.Vector2(0,0); geometry.vertices[1].uv = new THREE.Vector2(2,0); geometry.vertices[2].uv = new THREE.Vector2(2,2); geometry.vertices[3].uv = new THREE.Vector2(0,2); // 紋理坐標怎么弄 var texture = THREE.ImageUtils.loadTexture("textures/a.jpg",null,function(t) { }); var material = new THREE.MeshBasicMaterial({map:texture}); var mesh = new THREE.Mesh( geometry,material ); scene.add( mesh ); // window.addEventListener( 'resize', onWindowResize, false ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } function animate() { requestAnimationFrame( animate ); renderer.render( scene, camera ); } </script> </body> </html>
上面的代碼,一共完成了4件事情:
- 畫一個平面
- 為平面賦予紋理坐標
- 加載紋理
- 將紋理應用於材質
1.畫一個平面
var geometry = new THREE.PlaneGeometry( 500, 300, 1, 1 );
2.為平面賦予紋理坐標
平面有4個頂點,所以我們只需要指定4個紋理坐標就行了。紋理坐標由頂點的uv成員來表示,uv被定義為一個二維向量THREE.Vector2(),我們可以通過如下代碼來為平面定義紋理:
geometry.vertices[0].uv = new THREE.Vector2(0,0); geometry.vertices[1].uv = new THREE.Vector2(1,0); geometry.vertices[2].uv = new THREE.Vector2(1,1); geometry.vertices[3].uv = new THREE.Vector2(0,1);
注意,4個頂點分別對應了紋理的4個頂點。還要注意(0,0),(1,0),(1,1),(0,1)他們之間的順序是逆時針方向。大家在給平面賦紋理坐標的時候也要注意方向,不然three.js是分不清楚的。
3.加載紋理
紋理作為一張圖片,可以來源於互聯網,或者本地服務器,但是就是不能來源於類似C:\pic\a.jpg這樣的本地路徑。這是因為javascript沒有加載本地路徑文件的權限。
這里加載紋理使用了上面介紹的loadTexture函數,代碼如下:
var texture=THREE.ImageUtils.loadTexture("textures/a.jpg",null,function(t) { });
這個函數的第一個參數是一個相對路徑,表示與您的網頁之間的相對路徑。相對路徑對應了一個紋理圖片textures/a.jpg。
第二個參數為null,表示時候要傳入一個紋理坐標參數,來覆蓋前面在geometry中的參數。
第三個表示一個回調函數,表示成功加載紋理后需要執行的函數,參數t是傳入的texture。
4.將紋理應用於材質
加載好紋理,萬事俱備了,只需要將紋理映射到材質就可以了。我們這里使用了一個普通的材質THREE.MeshBasicMaterial,材質中有一個map屬性,可以直接接受紋理,我們可以這樣定義一個帶紋理的材質:
var material = new THREE.MeshBasicMaterial({map:texture});
ok,接下來的事情就簡單了,直接將紋理甩給Mesh,同時也別忘了Mesh也需要geometry,他們曖昧的關系如下:
var mesh = new THREE.Mesh( geometry,material );
最后的最后,將這個mesh加入場景中:
scene.add( mesh );