搜尋
首頁web前端js教程Three.js原始碼閱讀筆記(物件是如何組織的)_基礎知識

這是Three.js原始碼閱讀筆記第三篇。之前兩篇主要是關於核心物件的,這些物件主要圍繞著向量vector3物件和矩陣matrix4物件展開的,關注的是空間中的單一頂點的位置和變化。這篇將主要討論Three.js中的物體是如何組織的:即如何將頂點、表面、材質組合成為一個具體的物件。
Object::Mesh
這個建構子建構了一個空間中的物體。之所以叫「網格」是因為,實際上具有體積的物體基本上都是建模成為「網格」的。

複製代碼 代碼如下:

THREE.Mesh = function ( geometry, material ) {
THREE.Object3D.call( this );
this.geometry = geometry;
this.material = ( material !== undefined ) ? material : new THREE.MeshBasicMaterial( {omrand: Mathaterial(om) * 0xffffff, wireframe: true } );
/* 一些其他的與本節無關的內容*/
}

實際上,Mesh類別只有兩個屬性,表示幾何形體的geometry物件和表示材質的material物件。 geometry物件在上一篇文章中已經介紹過,還有部分派生類別會在這篇部落格文章中介紹(透過這些衍生類別的建構過程,能更清楚地了解到Mesh物件的工作原理);matrial物件及其衍生類別也將在這篇筆記中介紹。 Mesh對象的這兩個屬性相互緊密關聯,geometry對像中的face數組中,每個face對象的materialIndex用來匹配material屬性對象,face對象的vertexUVs數組用以依次匹配每個頂點在數組上的取值。值得注意的是,Mesh只能有一個material物件(不知這樣設計的意圖何在),如果需要用到多個材質,應將材質依照materialIndex順序初始化在geometry本身的materials屬性中。
Geometry::CubeGeometry
這個建構子建立了一個立方體物件。
複製程式碼 程式碼如下:

THREE.. widthSegments, heightSegments, depthSegments ) {
THREE.Geometry.call( this );
var scope = this;
this.width = width;
this.height;
this.width = width;
this.height = height. depth = depth;
var width_half = this.width / 2;
var height_half = this.height / 2;
var depth_half = this.depth / 2;
var depth_half = this.depth / 2;
/* 略去* 🎜>buildPlane( 'z', 'y', - 1, - 1, this.depth, this.height, width_half, 0 ); // px
/* 略去*/
function buildPlane( u , v, udir, vdir, width, height, depth, materialIndex ) {
/* 略去*/
}
this.computeCentroids();
this.mergefVertices(); };

建構子做的最重要的事在buildPlane。這個函數最重要的事情就是對scope的操作(上面的程式碼區塊中,scope就是this),包括:呼叫scope.vertices.push(vector)將頂點加入geometry物件;呼叫scope.faces.push(face)將表面加入到geometry對象,呼叫scope.faceVertexUvs[i].push(uv)方法將對應於頂點的材質座標加入geometry物件。程式碼的大部分都是關於產生立方體的邏輯,這些邏輯很容易理解,也很容易擴展到其他類型的geometry物件。

建構子的參數包括長、寬、高和三個方向的分段數。所謂分段,就是說如果將widthSeqments等三個參數都設定為2的話,那麼每個面將被表現成2×2=4個面,整個立方體由24個表面組成,正如同網格一樣。
複製程式碼 程式碼如下:

函數buildPlane( u, v, udir, vdir, 寬度, 高度, 深度, MaterialIndex ) {
var w, ix, iy,
gridX =scope.widthSegments,
gridY scope.heightSegments,
width_half = 寬度/ 2,
height_half = 高度/ 2,
offset =scope.vertices.length;
if ( (( u === 'x' & v == = 'y' ) || ( u === 'y' && v === 'x' ) ) {w = 'z';}
else if ( ( u === 'x' && v = == 'z' ) || ( u === 'z' && v === 'x' ) ) {w = 'y'; gridY = range.depthSegments;} else if ( ( u === 'z ' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {w = 'x';gridX = range.depthSegments;}
var gridX1 = gridX 1,
gridY1 = gridY 1,
segment_width = 寬度/ gridX,
segment_height = 高度/ gridY,
正常= 新三.Vector3();
> 0 ? 1 : - 1;
for ( iy = 0; iy for ( ix = 0; ix var vector = new THREE.Vector33 ();
向量[u]=(ix*segment_width-width_half)*udir;
向量[v]=(iy*segment_height-height_half)*vdir;
向量[ w ] = 深度;
scope.vertices.push(向量);
}
}
for ( iy = 0; iy for ( ix = 0; ix var a = ix gridX1 * 我;
var b = ix gridX1 * ( iy 1 );
var c = ( ix 1 ) gridX1 * ( iy 1 );
var d = ( ix 1 ) gridX1 * iy;
var face = new THREE.Face4(a 偏移量, b 偏移量, c 偏移量, d 偏移量);
face.normal.copy(正常) ;
face.vertexNormals.push( 正常. 克隆(), 正常. 克隆(), 正常. 克隆(), 正常. 克隆() );
face.materialIndex =materialIndex;
scope.facesface>face.materialIndex =materialIndex;
scope.facesfaces .push( 臉);
scope.faceVertexUvs[ 0 ].push( [
new THREE.UV( ix / gridX, 1 - iy / gridY ),
new THREE.UV( ix / gridX, ix / gridX, 1 - ( iy 1 ) / gridY ),
new THREE.UV( ( ix 1 ) / gridX, 1- ( iy 1 ) / gridY ),
new THREE.UV( ( ix 1 ) / gridX, 1 - iy / gridY )
]);
}
}
}


除了一個大部分物件都有clone()方法,CubeGeometry沒有其他的方法,其他的XXXGeometry物件也大抵如此。沒有方法說明該物件負責組織和資料存儲,以及如何利用這些資料產生三維場景和動畫,封裝在另外的物件中定義的。 Geometry::CylinderGeometry

顧名思義,該建構子建立一個圓柱體(或圓台)物件。 代碼如下:


THREE .CylinderGeomeius = fightius, Botius radiusSegments, heightSegments, openEnded ) {
/* 略*/
}


有了CubeGeometry建構函式的基礎,自己也應該能夠實作CylinderGeometry,我們只需要注意一下建構子各參數的意義。 radiusTop和radiusBottom表示頂部和底部的半徑,height表示高度。 radiusSegments定義了將圓週橫斷面多少份(該數字越大,圓柱更圓), heightSegments定義了需要將整個身高高度縮短多少份,openEnded指定是否產生頂面和底面。

源碼中還有兩點註解的:該模型的本地原點是中關節炎的中點,而不是重心之類的,遇到上圓面的高度(y軸值)是height/ 2,下圓面是-height/2,這一點對圓柱體來說沒有差異,但對於上下半徑不同的圓台體存在差異了;還有該模型的頂面和採用Groundface3 類型表面,而第三種Face4 類型表面。 Geometry::SphereGeometry

這個建構子建立一個球體。 程式碼如下:


THREE.SphereGeometry = f腳>

THREE.SphereGeometry = function (Seg.S)w. phiStart, phiLength, thetaStart, thetaLength ){
/* 略*/
}


各參數的意義:radius指定半徑,widthSegments表示「經度」分帶數量,heightSegments “緯度”分帶數目。後面四個參數是任選的,表示經度的初始值和緯度的初始值。熟悉極座標的都了解,通常用希臘文φ(phi)表示緯度圈角度(經度) ),而用θ(theta)表示經圈角度(緯度)。這四個數的預設值分別為0,2π,0,π,藉由改變它們的值,可以創造出殘缺的球面(但是邊緣必須平行) )。 源碼中,除了北極和南極的極圈內的區域是用face3類型表面,其他位置都是用face4類型表面。本地原點為球心。

幾何::PlaneGeometry
這個建構函式建立一個平面。 複製程式碼
程式碼如下:

THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ){
/* 略*/
}

各參數意義:依次為寬度>高度、寬度分段數、高度分段數。想必讀者對這種構造「格網」的方式應該很熟悉了吧。
原始碼中得到一些其他資訊:平面被構造在x-y平面上,原點即矩形中心點。
Geometry::ExtrudeGeometry
該物件現在是構造一般幾何形體的方法,但是通常我們是將建模好的物件儲存在某種格式的檔案中,並透過loader加載進來,所以似乎鮮有直接用到該函數的機會。而且這個函數看起來還是半成品,很多設定一股腦地堆在options物件裡,我也沒有仔細研究。
Material::Material
Material物件是所有其他種類Material的原型物件。
複製程式碼 程式碼如下:

THREE.Material = function (>

THREE.Material = function (>

THREE.Material = function (>
. .MaterialLibrary.push( this );
this.id = THREE.MaterialIdCount ;
this.name = '';
this.side = THREE.FrontSide;
this.opacity = 1;
this.transparent = false;
this.blending = THREE.NormalBlending;
this.blendSrc = THREE.SrcAlphaFactor;
this.blendDst = THREE.OneMinusMinus.SrcAlphaFactor;
this.blendDst = THREE.OneMinusMinus.SrcAlphaFactor;
this.blendDst = THREE.OneMinusMinus.Srcphathisation.SrcLquetDst = THREE。 AddEquation;
this.depthTest = true;
this.depthWrite = true;
this.polygonOffset = false;
this.polygonOffsetFactor = 0;
this.polygonOfmm>Units = 0; this.alphaTest = 0;
this.overdraw = false; // Boolean for fixing antialiasing gaps in CanvasRenderer
this.visible = true;
this.needsUpdate = true;
};

先看一些較為重要的屬性:
屬性opacity為一個0-1區間的值,表示透明度。屬性transparent指定是否使用透明,只有在該值為真的時候,才會將其與混合(透明是渲染像素時,待渲染值與已存在值共同作用計算出渲染後像素值,達到混合的效果) 。

屬性blending,blendSrc,blendDst,blendEquation指定了混合方式和混合源Src和混合像素已有的像元值Dst的權重指定方式。預設(如建構函式中賦的預設值),新的像元值等於:新值×alpha 舊值×(1-alpha)。

我曾經困惑為何Material類別中沒有最重要的對象,表示紋理圖片的屬性。後來我理解了,其實材質和紋理還是有差別的,只能說某種材質有紋理的,但也有材質是沒有紋理的。材質影響的是整個形體的渲染效果,例如:「對一條線渲染為5px寬,兩端點為方塊,不透明的紅色」這段描述就可以認為是材質,而沒有涉及任何紋理。 和眾多Geometry物件一樣,Material物件除了通用的clone(),dellocate()和setValues()方法,沒有其他方法。以下是兩種最基本的材質物件。

Material::LineBasicMaterial
此建構函式會建立用於渲染線狀形體的材質。 複製程式碼

程式碼如下:


THREE.LineBasicMaterial = function (Material = function (Naterial = function (Material = function) THREE.Material.call( this );
this.color = new THREE.Color( 0xffffff );
this.linewidth = 1;
this.linecap = 'round';
thisline. = 'round';
this.vertexColors = false;
this.fog = true;
this.setValues( parameters );
};


屬性和linewidth顧名思義,指線的顏色和線的寬度(線沒有寬度,這裡的寬度是用來渲染的)。
屬性linecap和linejoin指定線條端點和連接點的樣式。 屬性fog指定該種材質是否收到霧的影響。
Material::MeshBasicMaterial
此建構函式建立用於渲染Mesh表面的材質。 複製程式碼
程式碼如下:

THREE.MeshBasicMaterial = function ( parameters ) {
THREE.Material.call( this );
this.color = new THREE.Color( 0xffffff ); // emissive . map = null;
this.lightMap = null;
this.specularMap = null;
this.envMap = null;
this.combine = THREE.MultiplyOperation;
this.reflectivity = 1.reflectivity = 1.reflectivity = 1.reflectivity = 1.reflectivity = 1.reflectivity = 1.reflectivity。 ;
this.refractionRatio = 0.98;
this.fog = true;
this.shading = THREE.SmoothShading;
this.wireframe = false;
this.wireframeLiidth = 1; >this.wireframeLinecap = 'round';
this.wireframeLinejoin = 'round';
this.vertexColors = THREE.NoColors;
this.skinning = false;
this.morphTargets = fsese; 🎜>this.setValues( parameters );
};


這裡出現了最重要的紋理屬性,包括map,lightMap和specularMap,他們都是texture類型的物件。
屬性wireframe指定表面的邊界線是否渲染,如果渲染,後面的若干以wireframe開頭的屬性表示如果渲染邊界線,將如何渲染。

Texture::Texture

這個建構函式用來建立紋理物件。

複製程式碼 程式碼如下: THREE.Texture = function ( >
THREE.Texture = function ( imageS, . wrapT, magFilter, minFilter, format, type, anisotropy ) {
THREE.TextureLibrary.push( this );
this.id = THREE.TextureIdCount ;
this.name = ''; this.name = '';
this; .image = image;
this.mapping = mapping !== undefined ? mapping : new THREE.UVMapping();
this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrap; .wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;
this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
this.minter = min .LinearMipMapLinearFilter;
this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
this.format = format !== undefined ? format : THREE.RGBAFormat; undefined ? type : THREE.UnsignedByteType;
this.offset = new THREE.Vector2( 0, 0 );
this.repeat = new THREE.Vector2( 1, 1 );
this.generateMipmaps = true ;
this.premultiplyAlpha = false;
this.flipY = true;
this.needsUpdate = false;
this.onUpdate = null;
};


>最重要的屬性是image,這是一個JavaScript Image類型物件。傳入的第一個參數就是該對象,如何創建該對像在後面說。

後面的物件都是可選的,如果預設會填入預設值,而且往往都是填入預設值。
屬性magFileter和minFileter指定紋理在放大縮小時的過濾方式:最接近點、雙線性內插等。
從url產生一個texture,需要呼叫Three.ImageUtils.loadTexture(paras),函數傳回一個texture類型物件。在函數內部又呼叫了THREE.ImageLoader.load(paras)函數,這個函數內部又呼叫了THREE.Texture()建構函數,產生紋理。
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

如何使用Next.js(前端集成)構建多租戶SaaS應用程序如何使用Next.js(前端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript:探索網絡語言的多功能性JavaScript:探索網絡語言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的演變:當前的趨勢和未來前景JavaScript的演變:當前的趨勢和未來前景Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能