示例程式碼託管在:http://www.github.com/dashnowords/blogs
部落格園地址:《大史住在大前端》原創博文目錄
華為雲社群地址:【你要的前端打怪升級指南】
一. 大作業說明
通讀完上一篇博文中提及的教程,覺得應該搞個大作業鞏固一下所學的知識,想起剛上映的漫威宇宙第三階段收官之作《蜘蛛俠·英雄遠征》,於是決定仿一個MARVEL
的片頭動畫作為three.js
的課後練習,使用的版本是R104
版本。本節先來解決視訊貼圖的問題。
二.基本思路
簡易片頭動畫的實現思路如下,除了正常的舞臺元素外,需要背景音樂,使用THREE.AudioLoader
就可以從後後臺載入音樂,舞臺中主要的實體元素是MARVEL
這幾個字母的立體模型,可以使用THREE.TextGeometry
來進行建模(【Three.js繪製字型模型】),它要求先載入字型檔案,然後才能例項化,參考官方文件的實現就可以了。有了字型模型以後,還需要一些影片素材貼在字型模型上,THREE.VideoTexture
可以解決這個問題(【Three.js使用VideoTexture實現視訊Video更新紋理】),它可以將HTML中的<video>
標籤引入的資源作為表面紋理通過材料例項的map
引數與之關聯在一起,然後貼在幾何體表面,最後要解決的問題就是鏡頭的變化了,看過漫威電影的同學都知道,片頭動畫最後一部分的畫面先是鏡頭後退,然後MARVEL
幾個字母逐漸翻轉過來,這個效果的實現方式很多,可以調整相機引數,也可以調整物體引數,建議自己動手時各種方法都嘗試一下。為了熟悉更多特性,筆者自己在實現中使用正交相機,通過調整正交相機的視場寬度來模擬鏡頭後退動畫(在透視相機下可以直接調整相機的Z軸座標實現類似的效果),然後通過設定幾何體的位移和旋轉來模擬鏡頭的移動。
三.視訊紋理表面修復——UV對映
3.1 問題描述
整個大作業中最難處理的就是視訊紋理貼圖的部分,所以本篇先來搞定這個知識點。如果使用THREE.js
提供的Geometry
基本不會遇到什麼問題,例如上圖中的示例,就將視訊素材貼在了立方體的各個面上,然而當你使用其他帶有一些自定義性質的幾何體例項,比如自己畫了一個shape
然後拉伸成為拉伸體
,或者本次大作業中需要使用的TextGeometry
字型模型時。視訊貼圖就直接失效了。同樣尺寸的立方體,如果用THREE.BoxGeometry
來生成例項,表面就可以直接貼視訊,如果使用shape
畫一個矩形再拉伸成同樣尺寸的實體,視訊就無法正常覆蓋在模型表面,如下圖所示:
仔細看你會發現圖片邊界的地方有發光的條,將細節放大後可以看到下面的場景:
可以看到,視訊實際上的確是覆蓋在立方體表面了,但只是佔了很小的一塊,所以需要針對這種情況進行模型紋理修復,使視訊可以覆蓋幾何體的單個表面。
3.2 紋理貼圖的基本原理-UV對映
在Three.js
中,幾何體是通過點
和面
的特徵構建起來的,如果將一個幾何體例項物件在控制檯列印出來,就可以看到儲存端點座標資訊的vertexs
和儲存面資訊的faces
陣列。當你構建一個立方體時,會發現它的faces
屬性陣列中有12個面的資訊,因為Three.js
中預設使用三角面片來構建幾何體,一個矩形表面需要用兩個三角面片來構建,(你可以將立方體材料material
中傳入 wireframe:true
來看到立方體的線框圖),faces
陣列中每一個面中儲存的是構建這個三角面的3個點的位置資訊。
紋理貼圖座標也稱為UV
座標,它的貼圖原理是這樣的,首先將貼圖素材x軸和y軸的長度以0-1來標記,那麼使用3個座標範圍在[0~1,0~1]的點就可以在圖形素材中以三角形剪裁出需要的部分,同理使用4個座標範圍在[0~1,0~1]的點,就可以在圖形素材中以四邊形剪裁出需要的部分,以此類推,如下圖所示:
右圖中白色三角形的三個頂點在歸一化座標系中的座標值已經列出,將[0.2,0.2],[0.2,0.8],[0.7,0.2]
這三個座標點資訊填充到對應的UV
對映陣列中後,Three.js
就會用這個三角形區域來對一個三角面進行貼圖。由於預設面是三角面,所以我們通過例項化3個THREE.Vector2(x,y)
物件來表示從素材中擷取的三角形區域,得到了素材後要如何將它與三角面的頂點座標對應起來呢?這就引出了本節中的關鍵概念——UV對映矩陣。
大部分高大上的概念都離不開一個土掉渣的實現,
UV對映矩陣
也不例外。
由於貼圖素材是三個點,幾何體某個三角面也是有三個頂點,如果不限制索引,那麼就可能存在很多種貼圖結果:
為了保證貼圖素材的方向,它們之間就有存在一個對應關係,否則最後渲染的紋理可能就是倒著的或者旋轉90°的影象,所以UV對映矩陣
中儲存的依然是上例中右圖的三個點,但預設索引和構成幾何體指定面的三個頂點的索引相對應,這就唯一限定了擷取表面到幾何體三角面的貼圖樣式。
3.3 關鍵示例程式碼
完整的示例可以從附件或開頭處的github
程式碼倉中獲取,示例是一個express
工程,npm install
裝一下依賴,跑起來之後訪問localhost:3333
就可以看到。
//重構UV Mapping
function rebuildUVMapping() {
//在紋理素材上標記關鍵點
let pos = [
new THREE.Vector2(0,0.1),
new THREE.Vector2(1,0.1),
new THREE.Vector2(1,0.9),
new THREE.Vector2(0,0.9),
]
//uv對映的紋理存放在幾何體例項的下面這個屬性中
let uvs = geometry.faceVertexUvs[0];
//背面
//生成網格時材料可以傳陣列,materialIndex可以為不同面指定不同的材質,本例中對應不同的視訊片段
geometry.faces[0].materialIndex = 4;
geometry.faces[1].materialIndex = 4;
//重構UV對映關係矩陣
uvs[0] = [pos[1], pos[0], pos[3]];
uvs[1] = [pos[3], pos[2], pos[1]];
//正面
geometry.faces[2].materialIndex = 0;
geometry.faces[3].materialIndex = 0;
uvs[2] = [pos[3], pos[0], pos[1]];
uvs[3] = [pos[1], pos[2], pos[3]];
//標記uv對映是可更新的
geometry.uvsNeedUpdate = true;
}
四.小結
視訊紋理是本例中最難的部分了,下一篇中筆者將構建字型模型,並加入鏡頭轉換,完成整個預期的動畫,敬請關注,也希望感興趣的小夥伴一起交流。