ThreeJS是WebGL的一種前端框架,UV座標的原理是一樣的。
前置知識
WebGL紋理
如果對WebGL有興趣,可以去看WebGL貼材質這篇文章簡單瞭解下。
紋理座標系統
uv其實就是紋理座標,因爲xyz已經被頂點座標佔用了,所以uvw就用來表示紋理座標。它時候貼圖影射到模型表面的依據,把表面的點與平面上的像素對應起來,一般取值在0~1;
u:圖片在顯示器水平的座標
v:垂直方向
w:垂直於顯示器表面
一般情況只是在表面貼圖,就涉及不到w,所以常稱爲uv。
ThreeJS紋理貼圖
使用紋理對象貼圖
ThreeJS本身做了封裝,貼圖十分方便,如果大家英文好可以直接去官網,嫌麻煩也可以在中文網站上直接查看教程或對象方法。(⚠️以下非完整代碼)
const w=h=64,textureW=textureH=64;
var geometry = new THREE.PlaneBufferGeometry(w, h); //矩形平面
// TextureLoader創建一個紋理加載器對象,可以加載圖片作爲幾何體紋理
var textureLoader = new THREE.TextureLoader();
// 執行load方法,加載紋理貼圖成功後,返回一個紋理對象Texture
textureLoader.load('./p.jpg', function (texture) {
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
// texture.repeat.set(2, 2);
var material = new THREE.MeshLambertMaterial({
map: texture, //設置顏色貼圖屬性值
}); //材質對象Material
var mesh = new THREE.Mesh(geometry, material); //網格模型對象Mesh
scene.add(mesh); //網格模型添加到場景中
//紋理貼圖加載成功後,調用渲染函數執行渲染操作
render();
紋理對象Texture本身是有UV旋轉偏移方法的,但是如果調整紋理UV,用本身自帶方法就需要頻繁重新貼圖。
UV座標與頂點座標的關係
原始貼圖是這樣:
它的紋理座標和頂點座標對應情況,它們是一一對應的,大概是這個樣子
如果按上圖所說,修改UV座標就會變成這樣:
UV旋轉偏移
我們明白UV座標是怎麼回事,就可以開始旋轉、偏移了。
- 通過旋轉和偏移的值構建一個矩陣,如果矩陣知識還不熟悉,可以看這篇文章複習下,我是用ThreeJS自帶的矩陣庫,因爲它只有三維和四維的,所以我把uv座標寫成了三維,即齊次座標;
- 通過取得BufferGeometry的頂點座標,再去計算原始的UV座標。不過要注意的只有是或繼承BufferGeometry的對象才能取得uv、position信息;
- 最後通過矩陣轉換每一點UV座標,設置爲BufferGeometry的UV。
//geometry可以通過mesh對象取得,pos是保存頂點座標的數組
let pos = geometry.getAttribute("position").array;
let uv = [];
const startP = [pos[0],pos[1]];
//UV偏移不能太大,一般在0到1之間
const offsetU = 0.05,offsetV = 0.02,rotation = Math.PI/4;
let m = new THREE.Matrix3();
m.setUvTransform( -offsetU, -offsetV, 1, 1, rotation, 0, 0 );
//取得正常情況下的UV座標
for (let index = 0; index*3 < pos.length; index++) {
if(index === 0) uv.push(...[0,0,1]);
else{
const currentP = [pos[index*3],pos[index*3+1]]
uv.push(...[Math.abs((currentP[0]-startP[0])/w),Math.abs((currentP[1]-startP[1])/h),1]);
}
}
let uvAttr = new THREE.BufferAttribute(new Float32Array(uv) , 3) ;
uvAttr = m.applyToBufferAttribute (uvAttr);
geometry.attributes.uv = uvAttr;
就這麼簡單的代碼就可以實現UV旋轉偏移,但是我們一般會把它封裝成函數,通過頁面上的Input調整offsetU, offsetV,rotation的值。上面幾行的代碼,效果就是這樣啦: