Three.js中自定義UV坐標貼圖舉例超詳細講解
一、背景
1.1 使用場景 / 什么時候需要這樣處理
1、當原始幾何體沒有 UV 信息
- 比如從 GeoJSON 或自定義頂點數(shù)據(jù)生成的幾何體,通常不包含默認的 uv 屬性。
- 沒有 uv 信息,紋理就無法正確貼在模型表面。
2、當默認 UV 無法滿足紋理顯示需求
- 比如自動生成的 UV 是按照立方體或球體映射方式,但你希望紋理按照 XY 平面鋪開。
- 或你想自定義紋理拉伸、縮放、裁剪方式。
3、貼圖范圍較大時
- 如將紋理貼在一個很大的面(如整個省份或城市)上,需要歸一化 UV才能完整顯示整張圖,否則紋理可能看起來被擠壓或重復。
4、材質(zhì)設(shè)置使用了 map(紋理貼圖)
- UV 坐標直接決定了紋理如何映射到頂點上,沒有正確的 UV,貼圖就是黑的或者混亂的。
1.2 這樣處理的好處
好處 | 描述 |
---|---|
? 自定義貼圖范圍 | 按照 XY 平面的實際邊界(bounding box)進行歸一化貼圖,紋理完整而且可控。 |
? 保證不同面統(tǒng)一貼圖邏輯 | 使所有面在同一坐標系下統(tǒng)一使用紋理坐標,適用于多個 Mesh 的統(tǒng)一貼圖。 |
? 更好地適應(yīng)實際地理數(shù)據(jù) | 尤其適合地圖類、建筑投影、城市地塊等從經(jīng)緯度生成的模型。 |
? 提高美觀性 | 避免貼圖拉伸、模糊、重復等問題。讓紋理在面上看起來更自然。 |
1.3 舉個例子
假如你從 GeoJSON 創(chuàng)建了一個類似湖北省的面模型,你貼上一張高清地圖 top.png 作為貼圖,如果不設(shè)置 UV:
紋理可能根本看不到。
或者整個紋理只出現(xiàn)在幾何體的一個小角落(默認 UV 不匹配)。
甚至多個面公用同一張紋理,但位置都不對。
二、紋理貼圖與 UV 的基礎(chǔ)知識
在 3D 渲染中,為了將 2D 圖片(紋理)映射到 3D 幾何體上,需要用到 UV 坐標:
概念 | 說明 |
---|---|
紋理貼圖(Texture Mapping) | 是將 2D 圖像(比如一張地圖、照片等)映射到 3D 模型表面的過程。 |
UV 坐標 | 是二維坐標系 [u, v] ,對應(yīng)圖片的水平方向(U)和垂直方向(V),取值范圍通常是 [0, 1] 。 |
目的 | 指定幾何體的哪個頂點對應(yīng)紋理圖的哪個像素點。沒有 UV,Three.js 無法知道貼圖怎么“貼”上去。 |
例子:
uv = [0,0]
表示貼圖左下角,[1,1]
表示右上角。- 如果某三角面頂點 uv 是
[0,0]
,[1,0]
,[0,1]
,那紋理就會映射為一個右角三角區(qū)域。
三、代碼逐步解析
const group = topPolygonMesh.object3d; if (!group) return; group.traverse(child => { if (child.isMesh && child.geometry && child.geometry.attributes.position) { const posAttr = child.geometry.attributes.position; const positions = posAttr.array; // 1. 計算 XY 投影平面的包圍盒 let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; for (let i = 0; i < positions.length; i += 3) { const x = positions[i]; const y = positions[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; }; const width = maxX - minX; const height = maxY - minY; // 2. 根據(jù) bbox 生成新的 UV(按 XY 貼圖) const uv = []; for (let i = 0; i < positions.length; i += 3) { const x = positions[i]; const y = positions[i + 1]; const u = (x - minX) / width; const v = (y - minY) / height; uv.push(u, v); }; // 3. 設(shè)置新的 UV child.geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uv, 2)); child.geometry.attributes.uv.needsUpdate = true; } })
使用你上面的邏輯:
- 會將湖北省這個面在 XY 平面上的投影范圍歸一化為 [0,1] × [0,1],然后貼圖正好完整覆蓋整個省份模型頂面。
- 并且每個三角面上紋理也會均勻分布,不會拉伸、重復或扭曲。
代碼每部分解析
const group = topPolygonMesh.object3d; if (!group) return; group.traverse(child => { if (child.isMesh && child.geometry && child.geometry.attributes.position) {
?? 找到場景中的所有 Mesh
,確保其包含幾何體和位置(頂點)數(shù)據(jù)。
第一步:計算 XY 平面的包圍盒
const posAttr = child.geometry.attributes.position; const positions = posAttr.array; let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; for (let i = 0; i < positions.length; i += 3) { const x = positions[i]; const y = positions[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } const width = maxX - minX; const height = maxY - minY;
?? 獲取幾何體在 XY 平面上的包圍盒
第二步:計算新的 UV 坐標
const uv = []; for (let i = 0; i < positions.length; i += 3) { const x = positions[i]; const y = positions[i + 1]; const u = (x - minX) / width; const v = (y - minY) / height; uv.push(u, v); }
?? 將 XY 坐標歸一化為 UV 區(qū)間
第三步:應(yīng)用 UV 到幾何體
child.geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uv, 2)); child.geometry.attributes.uv.needsUpdate = true;
?? 設(shè)置 UV 屬性并通知渲染系統(tǒng)更新
四、目的總結(jié)
該段代碼的最終目標是:
? 使 XY 平面上的任意不規(guī)則幾何體,都能準確、完整地顯示一張貼圖(通常是一張地圖或圖案)
五、為什么默認 UV 不適用?
場景 | 是否需要手動設(shè)置 UV |
---|---|
自定義頂點數(shù)據(jù)構(gòu)建幾何體 | ? 是的,需要自定義 UV |
從 GeoJSON/TopoJSON 轉(zhuǎn)換成幾何體 | ? 是的,沒有默認 UV |
使用 ExtrudeGeometry , ShapeGeometry | ? 有自動生成 UV,但經(jīng)常不滿足 XY 貼圖的需要 |
紋理貼圖變形嚴重或重復 | ? 需要重設(shè) UV 保證平鋪、清晰 |
總結(jié)
項目 | 內(nèi)容 |
---|---|
?? 目的 | 將紋理按照 XY 平面貼滿整個面 |
?? 原理 | 將 XY 坐標歸一化為 [0,1] 區(qū)間作為 UV |
?? 效果 | 保證紋理不變形、無縫、完整地顯示 |
?? 場景 | 地圖貼圖、建筑貼圖、非規(guī)則面紋理貼圖 |
?? 重點 | UV 反映的是“紋理坐標”,不是世界坐標 |
到此這篇關(guān)于Three.js中自定義UV坐標貼圖的文章就介紹到這了,更多相關(guān)Three.js自定義UV坐標貼圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js 能實現(xiàn)監(jiān)聽F5頁面刷新子iframe 而父頁面不刷新的方法
下面小編就為大家?guī)硪黄猨s 能實現(xiàn)監(jiān)聽F5頁面刷新子iframe 而父頁面不刷新的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11javascript事件的傳播基礎(chǔ)實例講解(35)
這篇文章主要為大家詳細介紹了javascript事件的傳播基礎(chǔ)實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02Javascript判斷文件是否存在(客戶端/服務(wù)器端)
這篇文章主要介紹了Javascript判斷文件是否存在的方法適用于客戶端、服務(wù)器端,遠程文件,示例代碼如下,需要的朋友可以參考下2014-09-09JavaScript 未結(jié)束的字符串常量常見解決方法
做JavaScript的時候,發(fā)現(xiàn)老是出現(xiàn)錯誤:“未結(jié)束的字符串常量”. 自己找了下應(yīng)該是傳參數(shù)的時候,有特殊字符引起的.網(wǎng)上也找了下,也有好多出現(xiàn)這種情況.做下總結(jié),以方便以后查閱.2010-01-01