標籤:three.js 圖形 webgl 三維 web3d
商域無疆 (http://blog.csdn.net/omni360/)
本文遵循“署名-非商業用途-保持一致”創作公用協議
轉載請保留此句:商域無疆 - 本部落格專註於 敏捷開發及移動和物聯裝置研究:資料視覺效果、GOLANG、Html5、WEBGL、THREE.JS,否則,出自本部落格的文章拒絕轉載或再轉載,謝謝合作。
俺也是剛開始學,好多地兒肯定不對還請見諒.
以下代碼是THREE.JS 源碼檔案中objects/Mesh.js檔案的注釋.
更多更新在 : https://github.com/omni360/three.js.sourcecode
/** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author jonobr1 / http://jonobr1.com/ *//*///Mesh對象,最終的網格對象,有高人把圖形學建模比作是製作燈籠,先用Geometry建立燈籠的架構,然後將材質material貼在架構上,最後形成的總體燈籠,就是Mesh對象.下面看一下Mesh對象的用法和具體實現./// 用法:var geometry = new THREE.Geometry(1,1,1);//建立geometry對象(燈籠的架構),/////有一下可選對象BoxGeometry,CircleGeometry,CubeGeometry,CylinderGeometry,DodecahedronGeometry,ExtrudeGeometry,IcosahedronGeometry,/////LatheGeometry,OctahedronGeometry,ParametricGeometry,PlaneGeometry,PolyhedronGeometry,RingGeometry,ShapeGeometry,SphereGeometry,/////TetrahedronGeometry,TextGeometry,TorusGeometry,TorusKnotGeometry,TubeGeometry/// var material = new THREE.Material({color: 0xffff00});//建立材質對象(燈籠的表面)/////有以下可選對象LineBasicMaterial,LineDashedMaterial,Material,MeshBasicMaterial,MeshDepthMaterial,MeshFaceMaterial,/////MeshLambertMaterial,MeshNormalMaterial,MeshPhongMaterial,PointCloudMaterial,RawShaderMaterial,ShaderMaterial,/////SpriteCanvasMaterial,SpriteMaterial/// var mesh = new THREE.Mesh(geometry, material);//建立mesh(燈籠)對象,並將geometry對象(燈籠的架構)和material對象(燈籠的表面)傳遞給mesh(燈籠)對象/// scene.add(mesh); //將mesh(燈籠)添加到情境中.*////<summary>Mesh</summary>///<param name ="geometry" type="THREE.Geometry">Geometry對象(燈籠的架構)</param>///<param name ="material" type="THREE.Material">Material對象(材質對象)</param>///<returns type="Mesh">返回Mesh對象</returns>THREE.Mesh = function ( geometry, material ) {THREE.Object3D.call( this );//調用Object3D對象的call方法,將原本屬於Object3D的方法交給當前對象Mesh來使用.this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();//將參數geometry賦值給mesh對象的geometry屬性this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } );//將參數material賦值給mesh對象的material屬性/*///MorphTargets///原文地址:http://www.tuicool.com/articles/rYzuuu///先睹為快//////MorphTargets允許物體發生變形。如果該物體的geometry有 $n$ 個頂點,那麼MorphTargets允許你再指定 $n$ 個, $2n$ 個, $3n$ 個甚至更多個頂點///(比如,$ p\cdot n$ 個),同時mesh對象提供一個數組morphTargetInfluences(公式中$ f_{j} $表示morphTargetInfluences[j]),具有 $p$ 個元素,///每個元素取值在0-1之間。渲染這個物體的時候,某個頂點 $V_{i}$ 的位置其實變了,成了://////$$V_{i}=V_{i}+\sum_{j=0}^{p}f_{j}\cdot (V_{j,i}-V_{i})$$//////舉個簡單的例子,一個立方體有8個頂點, MorphTargets又指定了8個頂點,立方體的一個頂點為(1,1,1),而在 MorphTargets中與之對應的頂點為(2,2,2),///那麼當 morphTargetInfluences[0]為0.5的時候,實際渲染的時候該頂點的位置就成了(1.5,1.5,1.5)。這樣做的好處是顯而易見的,你可以通過簡單地調整/// morphTargetInfluences數組來使物體形變,只要之前你設定好了。//////向物體加入morphTargets的方法很簡單://////var geometry = new THREE.CubeGeometry(100,100,100);/// var material = new THREE.MeshLambertMaterial({color:0xffffff, morphTargets:true});////// var vertices = [];/// for(var i=0; i<geometry.vertices.length; i++)/// {/// var f = 2;/// vertices.push(geometry.vertices[i].clone());/// vertices[i].x *= f;/// vertices[i].y *= f;/// vertices[i].z *= f;/// }/// geometry.morphTargets.push({name:'target0', vertices:vertices});/// 在其他什麼地方(比如animate()或render()方法中)改變morphTargetInfluences,實在方便//////var s = 0;///function render()///{/// s += 0.03;/// mesh.morphTargetInfluences[0] = Math.abs(Math.sin(s));/// ...///}///最關鍵的問題是,我相信,這個功能是通過著色器來完成的。我閱讀過一些簡單的著色器,因此我發現在著色器中完成這件事實在太合適了。///如果某個geometry有幾千甚至上萬個頂點,使用JavaScript逐個計算變形後頂點的位置會造成很大壓力,而顯卡大規模並行計算的能力很適合處理這個任務///(畢竟每個頂點是獨立地)。 */this.updateMorphTargets();//更新目標變形,不影響geometry對象。};/*****************************************************下面是Mesh對象的方法屬性定義,繼承自Object3D**************************************************/THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );/*///updateMorphTargets方法將geometry對象的morphTargets屬性複製到this.morphTargetInfluences屬性,不影響geometry對象本身。*////<summary>updateMorphTargets</summary>///<returns type="Mesh">返回新的Mesh對象</returns>THREE.Mesh.prototype.updateMorphTargets = function () {if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) {//判斷geometry對象是否有目標變形數組。this.morphTargetBase = - 1;//給Mesh對象設定morphTargetBase屬性,並初始化為-1.this.morphTargetForcedOrder = [];//給Mesh對象設定morphTargetForcedOrder屬性數組,初始化[].this.morphTargetInfluences = [];//給Mesh對象設定morphTargetInfluences屬性數組,初始化為[].this.morphTargetDictionary = {};//給Mesh對象設定morphTargetDictionary屬性,初始化為}{}.for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) {//遍曆geometry對象this.morphTargetInfluences.push( 0 );this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;//將geometry.morphTargets屬性數組中的值,一一賦值給morphTargetDictionary[]屬性數組。}}};/*///getMorphTargetIndexByName方法通過參數name獲得儲存在morphTargetDictionary[]屬性數組中的變形目標索引或者說是。*////<summary>getMorphTargetIndexByName</summary>///<param name ="name" type="String">MorphTarget儲存的名字</param>///<returns type="Mesh">返回參數name所對應的頂點</returns>THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) {if ( this.morphTargetDictionary[ name ] !== undefined ) {//如果morphTargetDictionary[name]屬性對象存在return this.morphTargetDictionary[ name ];//返回該對象。}console.log( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' );//提示使用者,該對象不存在。傳回值是0.return 0;};/*///raycast方法用來獲得當前對象與射線(參數raycaster)的交點.raycaster.intersectObject會調用這個方法。主要是用來進行碰撞檢測,/// 在選擇情境中的對象時經常會用到,判斷當前滑鼠是否與對象重合用來選擇對象./// NOTE:raycast方法中參數intersects參數用來儲存交點的集合,格式如下///intersects.push( {//////distance: distance,///point: intersectionPoint,///indices: [ a, b, c ],///face: null,///faceIndex: null,///object: this//////} );///*////<summary>raycast</summary>///<param name ="raycaster" type="THREE.Raycaster">射線對象</param>///<param name ="intersects" type="ObjectArray">交點的集合</param>///<returns type="ObjectArray">交點的集合</returns>THREE.Mesh.prototype.raycast = ( function () {var inverseMatrix = new THREE.Matrix4();//聲明一個4x4矩陣,用來放置逆矩陣var ray = new THREE.Ray();//聲明全域射線對象var sphere = new THREE.Sphere();//聲明全域球體對象var vA = new THREE.Vector3();//聲明3維向量,vAvar vB = new THREE.Vector3();//聲明3維向量,vBvar vC = new THREE.Vector3();//聲明3維向量,vCreturn function ( raycaster, intersects ) {var geometry = this.geometry;// Checking boundingSphere distance to ray// 檢查geometry對象的球體邊界到射線的距離。if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();sphere.copy( geometry.boundingSphere );sphere.applyMatrix4( this.matrixWorld );if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {//調用光線跟蹤的isIntersectionSphere方法,判斷geometry對象的球體邊界是否與射線相交。return;//如果不相交,返回。}// Check boundingBox before continuing//檢查geometry對象的立方體邊界到射線距離inverseMatrix.getInverse( this.matrixWorld );ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );if ( geometry.boundingBox !== null ) {if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) {//調用光線跟蹤的isIntersectionBox方法,判斷geometry對象的立方體邊界是否與射線相交。return;}}//如果geometry對象是BufferGeometry對象if ( geometry instanceof THREE.BufferGeometry ) {var material = this.material;if ( material === undefined ) return;//如果沒有材質,返回var attributes = geometry.attributes;var a, b, c;var precision = raycaster.precision;//精度因子if ( attributes.index !== undefined ) {//如果bufferGeometry對象的attributes.index屬性不為undefinedvar indices = attributes.index.array;var positions = attributes.position.array;var offsets = geometry.offsets;if ( offsets.length === 0 ) {offsets = [ { start: 0, count: indices.length, index: 0 } ];}for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) {//根據定義bufferGeomentry存放的格式,遍曆attributes屬性.找到頂點資料儲存地區var start = offsets[ oi ].start;var count = offsets[ oi ].count;var index = offsets[ oi ].index;for ( var i = start, il = start + count; i < il; i += 3 ) {//根據定義bufferGeomentry存放的格式,遍曆attributes屬性.找到頂點資料a = index + indices[ i ];b = index + indices[ i + 1 ];c = index + indices[ i + 2 ];vA.set(positions[ a * 3 ],positions[ a * 3 + 1 ],positions[ a * 3 + 2 ]);vB.set(positions[ b * 3 ],positions[ b * 3 + 1 ],positions[ b * 3 + 2 ]);vC.set(positions[ c * 3 ],positions[ c * 3 + 1 ],positions[ c * 3 + 2 ]);if ( material.side === THREE.BackSide ) {//如果材質的side屬性為BackSidevar intersectionPoint = ray.intersectTriangle( vC, vB, vA, true );//調用intersectTriangle方法判斷是否與參數VC,VB,VA組成的Triangle三角形對象相交,如果相交返回交點.如果不想交返回null,這裡最後一個參數true,表示判斷背面} else {var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );//調用intersectTriangle方法判斷是否與參數VA,VB,VC組成的Triangle三角形對象相交,如果相交返回交點.如果不想交返回null,這裡最後一個參數,表示判斷正面}if ( intersectionPoint === null ) continue;//如果沒有交點,跳出迴圈intersectionPoint.applyMatrix4( this.matrixWorld );//將非null的交點應用全局座標變換var distance = raycaster.ray.origin.distanceTo( intersectionPoint );//計算射線原點到交點的距離if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;//如果距離小於精度因子,或小於射線的近端,或大於射線的遠端,跳出迴圈intersects.push( {//將相交的對象,頂點索引,距離,交點儲存到intersects屬性數組中distance: distance,//距離point: intersectionPoint,//交點indices: [ a, b, c ],//頂點在attribute屬性中的索引face: null,//面faceIndex: null,//面所在屬性數組中的索引object: this //對象} );}}} else {//如果bufferGeometry對象的attributes.index屬性為undefinedvar positions = attributes.position.array;for ( var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9 ) {//找到所有的頂點位置屬性a = i;b = i + 1;c = i + 2;vA.set(positions[ j ],positions[ j + 1 ],positions[ j + 2 ]);vB.set(positions[ j + 3 ],positions[ j + 4 ],positions[ j + 5 ]);vC.set(positions[ j + 6 ],positions[ j + 7 ],positions[ j + 8 ]);if ( material.side === THREE.BackSide ) {//如果材質的side屬性為BackSidevar intersectionPoint = ray.intersectTriangle( vC, vB, vA, true );//調用intersectTriangle方法判斷是否與參數VC,VB,VA組成的Triangle三角形對象相交,如果相交返回交點.如果不想交返回null,這裡最後一個參數true,表示判斷背面} else {var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );//調用intersectTriangle方法判斷是否與參數VA,VB,VC組成的Triangle三角形對象相交,如果相交返回交點.如果不想交返回null,這裡最後一個參數,表示判斷正面}if ( intersectionPoint === null ) continue;//如果沒有交點,跳出迴圈intersectionPoint.applyMatrix4( this.matrixWorld );//將非null的交點應用全局座標變換var distance = raycaster.ray.origin.distanceTo( intersectionPoint );//計算射線原點到交點的距離if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;//如果距離小於精度因子,或小於射線的近端,或大於射線的遠端,跳出迴圈intersects.push( {//將相交的對象,頂點索引,距離,交點儲存到intersects屬性數組中distance: distance,//距離point: intersectionPoint,//交點indices: [ a, b, c ],//頂點在attribute屬性中的索引face: null,//面faceIndex: null,//面所在屬性數組中的索引object: this //對象} );}}} else if ( geometry instanceof THREE.Geometry ) {//如果geometry對象是THREE.Geometry類型var isFaceMaterial = this.material instanceof THREE.MeshFaceMaterial;var objectMaterials = isFaceMaterial === true ? this.material.materials : null;var a, b, c, d;var precision = raycaster.precision;//精度因子var vertices = geometry.vertices;for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) {//遍曆geometry對象的所有面var face = geometry.faces[ f ];var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : this.material;if ( material === undefined ) continue;//如果沒有材質,跳出迴圈a = vertices[ face.a ];b = vertices[ face.b ];c = vertices[ face.c ];if ( material.morphTargets === true ) {//如果有變性目標屬性var morphTargets = geometry.morphTargets;var morphInfluences = this.morphTargetInfluences;vA.set( 0, 0, 0 );vB.set( 0, 0, 0 );vC.set( 0, 0, 0 );for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {//將所有的頂點按照變形資料做變換var influence = morphInfluences[ t ];if ( influence === 0 ) continue;var targets = morphTargets[ t ].vertices;vA.x += ( targets[ face.a ].x - a.x ) * influence;vA.y += ( targets[ face.a ].y - a.y ) * influence;vA.z += ( targets[ face.a ].z - a.z ) * influence;vB.x += ( targets[ face.b ].x - b.x ) * influence;vB.y += ( targets[ face.b ].y - b.y ) * influence;vB.z += ( targets[ face.b ].z - b.z ) * influence;vC.x += ( targets[ face.c ].x - c.x ) * influence;vC.y += ( targets[ face.c ].y - c.y ) * influence;vC.z += ( targets[ face.c ].z - c.z ) * influence;}vA.add( a );vB.add( b );vC.add( c );a = vA;b = vB;c = vC;}if ( material.side === THREE.BackSide ) {//如果材質的side屬性為BackSidevar intersectionPoint = ray.intersectTriangle( c, b, a, true );//調用intersectTriangle方法判斷是否與參數VC,VB,VA組成的Triangle三角形對象相交,如果相交返回交點.如果不想交返回null,這裡最後一個參數true,表示判斷背面} else {var intersectionPoint = ray.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide );//調用intersectTriangle方法判斷是否與參數VA,VB,VC組成的Triangle三角形對象相交,如果相交返回交點.如果不想交返回null,這裡最後一個參數,表示判斷正面}if ( intersectionPoint === null ) continue;//如果沒有交點,跳出迴圈intersectionPoint.applyMatrix4( this.matrixWorld );//將非null的交點應用全局座標變換var distance = raycaster.ray.origin.distanceTo( intersectionPoint );//計算射線原點到交點的距離if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue;//如果距離小於精度因子,或小於射線的近端,或大於射線的遠端,跳出迴圈intersects.push( {distance: distance,//距離point: intersectionPoint,//交點face: face,//面faceIndex: f,//面索引object: this //對象} );}}};}() );/*clone方法///clone方法複製一個Mesh網格對象.*////<summary>clone</summary>///<param name ="object" type="Object3D">接收複製的Object3D對象</param>///<param name ="recursive" type="boolean">是否對子物件一一進行複製</param>///<returns type="Ray">返回Mesh網格對象.</returns>THREE.Mesh.prototype.clone = function ( object, recursive ) {if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material );THREE.Object3D.prototype.clone.call( this, object, recursive );//繼承Object3D的clone方法return object;//返回Mesh網格對象.};
商域無疆 (http://blog.csdn.net/omni360/)
本文遵循“署名-非商業用途-保持一致”創作公用協議
轉載請保留此句:商域無疆 - 本部落格專註於 敏捷開發及移動和物聯裝置研究:資料視覺效果、GOLANG、Html5、WEBGL、THREE.JS,否則,出自本部落格的文章拒絕轉載或再轉載,謝謝合作。
以下代碼是THREE.JS 源碼檔案中objects/Mesh.js檔案的注釋.
更多更新在 : https://github.com/omni360/three.js.sourcecode
three.js 源碼注釋(五十九)objects/Mesh.js