WebGL場景的兩種地面構造方法

来源:https://www.cnblogs.com/ljzc002/archive/2019/06/29/11105496.html
-Advertisement-
Play Games

總述:大部分3D編程都涉及到地面元素,在場景中我們使用地面作為其他物體的承載基礎,同時也用地面限制場景使用者的移動範圍,還可以通過設置地塊的屬性為場景的不同位置設置對應的計算規則。本文在WebGL平臺上藉助Babylon.js庫探索並實現了兩種地面構造方法,除了兩種確定的構造方法外,本文還包含了對一 ...


總述:
大部分3D編程都涉及到地面元素,在場景中我們使用地面作為其他物體的承載基礎,同時也用地面限制場景使用者的移動範圍,還可以通過設置地塊的屬性為場景的不同位置設置對應的計算規則。本文在WebGL平臺上藉助Babylon.js庫探索並實現了兩種地面構造方法,除了兩種確定的構造方法外,本文還包含了對一些其他選擇的探討和一些對電子游戲藝術的看法。建議在閱讀本文前,先學習3D編程入門知識和Babylon.js的官方入門教程,前者可以在 https://space.bilibili.com/25346426/channel/detail?cid=14552找到一些介紹基礎概念的視頻教程,後者可以在https://github.com/ljzc002/ljzc002.github.io/tree/master/BABYLON101找到英中對照版本,本篇文章所用到的代碼可以在https://github.com/ljzc002/ljzc002.github.io/tree/master/EmptyTalk下載。

一、方法一——使用標準地塊拼裝構造地面
1、我們先製作5種標準地塊:

標準地塊都是邊長為1的正方體,每一種標準地塊使用對應的紋理表示特定的地貌,我們可以用這些標準地塊的複製體拼接成複雜的地面構造。在本文中我使用正方體作為標準地塊,垂直排布的生成地形,你也可以使用六棱柱等其他形狀作為標準地塊,或者將地塊繞y軸旋轉一些角度後進行排布。
製作標準地塊的代碼如下:

 1         var size_per=1;//每個單元格的尺寸
 2         var obj_landtype={};
 3 //建立網格
 4         var box_grass=new BABYLON.MeshBuilder.CreateBox("box_grass",{size:size_per},scene);
 5         var box_tree=new BABYLON.MeshBuilder.CreateBox("box_tree",{size:size_per},scene);
 6         var box_stone=new BABYLON.MeshBuilder.CreateBox("box_stone",{size:size_per},scene);
 7         var box_shallowwater=new BABYLON.MeshBuilder.CreateBox("box_shallowwater",{size:size_per},scene);
 8         var box_deepwater=new BABYLON.MeshBuilder.CreateBox("box_deepwater",{size:size_per},scene);
 9         box_grass.renderingGroupId = 2;
10         box_tree.renderingGroupId = 2;
11         box_stone.renderingGroupId = 2;
12         box_shallowwater.renderingGroupId = 2;
13         box_deepwater.renderingGroupId = 2;
14         box_grass.position.y=-100*size_per;
15         box_tree.position.y=-101*size_per;
16         box_stone.position.y=-102*size_per;
17         box_shallowwater.position.y=-103*size_per;
18         box_deepwater.position.y=-104*size_per;
19         obj_landtype.box_grass=box_grass;
20         obj_landtype.box_tree=box_tree;
21         obj_landtype.box_stone=box_stone;
22         obj_landtype.box_shallowwater=box_shallowwater;
23         obj_landtype.box_deepwater=box_deepwater;
24         OptimizeMesh(box_grass);
25         OptimizeMesh(box_tree);
26         OptimizeMesh(box_stone);
27         OptimizeMesh(box_shallowwater);
28         OptimizeMesh(box_deepwater);
29 //建立材質
30         var mat_grass = new BABYLON.StandardMaterial("mat_grass", scene);//1
31         mat_grass.diffuseTexture = new BABYLON.Texture("../../ASSETS/IMAGE/LANDTYPE/grass.jpg", scene);
32         mat_grass.freeze();
33         box_grass.material=mat_grass;
34         var mat_tree = new BABYLON.StandardMaterial("mat_tree", scene);//1
35         mat_tree.diffuseTexture = new BABYLON.Texture("../../ASSETS/IMAGE/LANDTYPE/yulin.png", scene);
36         mat_tree.freeze();
37         box_tree.material=mat_tree;
38         var mat_stone = new BABYLON.StandardMaterial("mat_stone", scene);//1
39         mat_stone.diffuseTexture = new BABYLON.Texture("../../ASSETS/IMAGE/LANDTYPE/stone.png", scene);
40         mat_stone.freeze();
41         box_stone.material=mat_stone;
42         var mat_shallowwater = new BABYLON.StandardMaterial("mat_shallowwater", scene);//1
43         mat_shallowwater.diffuseTexture = new BABYLON.Texture("../../ASSETS/IMAGE/LANDTYPE/lake.png", scene);
44         mat_shallowwater.freeze();
45         box_shallowwater.material=mat_shallowwater;
46         var mat_deepwater = new BABYLON.StandardMaterial("mat_deepwater", scene);//1
47         mat_deepwater.diffuseTexture = new BABYLON.Texture("../../ASSETS/IMAGE/LANDTYPE/sea.png", scene);
48         mat_deepwater.freeze();
49         box_deepwater.material=mat_deepwater;        

這段代碼製作了“草地”、“森林”、“岩石”、“淺水”、“深水”五種標準地塊,對於地塊的網格,使用OptimizeMesh方法進行了一些顯示優化,OptimizeMesh方法內容如下:

1 function OptimizeMesh(mesh)
2     {
3         mesh.convertToFlatShadedMesh();//使用頂點顏色計算代替片元顏色計算
4         mesh.freezeWorldMatrix();//凍結世界坐標系
5         // mesh.material.needDepthPrePass = true;//啟用深度預通過
6         //mesh.convertToUnIndexedMesh();//使用三角形繪製代替索引繪製
7     }

對於地塊材質,使用freeze方法凍結了材質對象的屬性,避免渲染引擎頻繁刷新材質狀態。

2、接下來我們用草地地塊拼接一個最簡單的平原地形
地形渲染效果如下:

 

這片草原是由10201個草地地塊拼接而成的,這裡我使用了OpenGL的“多實例渲染”技術,來降低繪製大量重覆對象對計算性能的消耗,Babylon.js庫在createInstance方法中封裝了這一技術:

 1     var arr_instance=[];
 2     var segs_x=100;//橫向分段次數
 3     var segs_y=100;//縱向分段次數
 4     
 5     //以高度0為海平面,以xy00為大地原點
 6         //形成初始地塊:101*101個格子,中心格的中心是原點
 7         for(var i=0;i<=segs_x;i++)
 8         {
 9             arr_instance[i]=[];
10             for(var j=0;j<=segs_y;j++)
11             {
12                 arr_instance[i][j]=[];
13                 var instance=obj_landtype.box_grass.createInstance("ground_"+i+"_"+j+"_0");
14                 instance.mydata={i:i,j:j,k:0,landclass:obj_landtype.box_grass};
15                 instance.position=new BABYLON.Vector3((i-(segs_x/2))*size_per,0,(j-(segs_y/2))*size_per);//xz方向上都是從負向正堆疊
16                 arr_instance[i][j].push(instance);//把每個實例用全局對象保存起來
17             }
18         }

這裡我們為每個實例對象設置了一個mydata屬性,將地塊的一些信息保存到這個屬性里,以備之後的場景交互使用。

3、為單元格標記xz方向上的索引

現在每個地塊都是沿著x軸和z軸整齊排列的,為方便區分,我們將xz平面上的每個方塊位置叫做“單元格”,每個單元格中可能有多個地塊實例。每個單元格的位置以其x、z軸上的索引表示,我們現在需要一種方式將這一索引值顯示出來。
這裡我們先嘗試為每個單元格顯示一個索引文本,渲染效果如下:(可以訪問https://ljzc002.github.io/EmptyTalk/HTML/TEST/testfloor.html查看)

可以看出標記效果並不是很理想,同時數以萬計的索引文本也降低了場景的渲染速度,這種方法可能並不適用於當前的單元格標記需求,但包含的技術可能用在其他地方:

a、首先建立一個精靈管理器以及一張包含數字和減號的圖片

 1 //準備十種數字以及減號的紋理
 2     var can_temp=document.createElement("canvas");
 3         can_temp.width=132//264;
 4         can_temp.height=24;
 5         var context=can_temp.getContext("2d");
 6         context.fillStyle="rgba(0,0,0,0)";//完全透明的背景
 7         context.fillRect(0,0,can_temp.width,can_temp.height);
 8         context.fillStyle = "#ffffff";
 9         context.font = "bold 24px monospace";
10         for(var i=0;i<10;i++)
11         {
12             context.fillText(i,i*12,24);
13         }
14         context.fillText("-",120,24);
15         //context.fillText("0123456789-",0,24);//預設為半形,為了在作為精靈使用時整齊的分塊必須一個一個單獨繪製
16         var png=can_temp.toDataURL("image/png");//生成PNG圖片
17         //建立精靈管理器
18         var spriteManager = new BABYLON.SpriteManager("spriteManager", png, (segs_x+1)*(segs_y+1)*7, 24, scene);
19         spriteManager.renderingGroupId=2;
20         spriteManager.cellWidth=12;
21         spriteManager.cellHeight=24;

b、在生成地塊實例的迴圈裡加入精靈生成代碼:

 1 //添加精靈,估計地圖最大為1000*1000
 2                 var number1 = new BABYLON.Sprite("number1", spriteManager);
 3                 var number2 = new BABYLON.Sprite("number2", spriteManager);
 4                 var number3 = new BABYLON.Sprite("number3", spriteManager);
 5                 var number4 = new BABYLON.Sprite("number4", spriteManager);
 6                 var number5 = new BABYLON.Sprite("number5", spriteManager);
 7                 var number6 = new BABYLON.Sprite("number6", spriteManager);
 8                 var number7 = new BABYLON.Sprite("number7", spriteManager);
 9                 //為缺少的數位填充0,生成三位數字
10                 stri=(i+1000+"").substr(1);
11                 strj=(j+1000+"").substr(1);
12 
13                 number1.cellIndex=parseInt(stri[0]);
14                 number2.cellIndex=parseInt(stri[1]);
15                 number3.cellIndex=parseInt(stri[2]);
16                 number4.cellIndex=10;//減號
17                 number5.cellIndex=parseInt(strj[0]);
18                 number6.cellIndex=parseInt(strj[1]);
19                 number7.cellIndex=parseInt(strj[2]);
20                 //定位精靈,7個精靈垂直排列作為一條文本
21                 number1.size=0.2*size_per;
22                 number1.position=instance.position.clone();
23                 number1.position.y=2*size_per;
24                 number1.position.x+=0.3*size_per;
25                 number1.position.z+=0.3*size_per;
26 
27                 number2.size=0.2*size_per;
28                 number2.position=instance.position.clone();
29                 number2.position.y=1.8*size_per;
30                 number2.position.x+=0.3*size_per;
31                 number2.position.z+=0.3*size_per;
32                 number3.size=0.2*size_per;
33                 number3.position=instance.position.clone();
34                 number3.position.y=1.6*size_per;
35                 number3.position.x+=0.3*size_per;
36                 number3.position.z+=0.3*size_per;
37                 number4.size=0.2*size_per;
38                 number4.position=instance.position.clone();
39                 number4.position.y=1.4*size_per;
40                 number4.position.x+=0.3*size_per;
41                 number4.position.z+=0.3*size_per;
42                 number5.size=0.2*size_per;
43                 number5.position=instance.position.clone();
44                 number5.position.y=1.2*size_per;
45                 number5.position.x+=0.3*size_per;
46                 number5.position.z+=0.3*size_per;
47                 number6.size=0.2*size_per;
48                 number6.position=instance.position.clone();
49                 number6.position.y=1.0*size_per;
50                 number6.position.x+=0.3*size_per;
51                 number6.position.z+=0.3*size_per;
52                 number7.size=0.2*size_per;
53                 number7.position=instance.position.clone();
54                 number7.position.y=0.8*size_per;
55                 number7.position.x+=0.3*size_per;
56                 number7.position.z+=0.3*size_per;

考慮到計算性能,這裡使用精靈作為文本的載體(但建立了7萬多個精靈之後,幀率還是降低了很多),因為水平排列的精靈在相機水平移動時會相互遮擋,所以垂直排列精靈來降低影響,也許可以通過重設精靈的旋轉軸位置來徹底解決這一問題。

除了為每個地塊標註索引之外,我們還可以使用帶有索引的小地圖、在單獨的視口中顯示選定地塊的特寫、在選定地塊旁邊生成標記等等方式來標明地塊的索引,後文將使用在場景中放置參考物的方式來標示地塊索引。

4、生成地形起伏
我們抬升了xz平面中左下角的兩格單元格,並將這兩個單元設為“岩石”地貌:

 

a、首先建立兩個工具方法:

 1 //disposeCube(0,0)
 2     function disposeCube(i,j)//移除一個xz位置上的所有可能存在的方塊
 3     {
 4         var len=arr_instance[i][j].length;
 5         for(var k=0;k<len;k++)
 6         {
 7             var instance=arr_instance[i][j][k];
 8             instance.dispose();
 9             instance=null;
10         }
11 
12     }
13     //在指定單元格、指定高度建立指定類型的地塊
14     //createCube(0,0,2,obj_landtype.box_stone)
15     //i,j必定是整數,k可能是小數,都表示單位長度的數量
16     function createCube(i,j,k,landclass)
17     {
18         var instance=landclass.createInstance("ground_"+i+"_"+j+"_"+k);
19         instance.mydata={i:i,j:j,k:k,landclass:landclass};
20         instance.position=new BABYLON.Vector3((i-(segs_x/2))*size_per,k*size_per,(j-(segs_y/2))*size_per);//都是從負向正堆疊?-》規定每個單元格的地塊數組都是從低到高排列
21         //arr_instance[i][j].push(instance);
22         arr_instance[i][j].unshift(instance);
23     }

b、接下來修改一些被選中的單元格

我們把“被選中的單元格”放在一個數組裡,將這個數組命名為“配置數組”。

 1 //用對應的方塊填充一條路徑上所有的xz單元格,先清空單元格內原有方塊,然後在指定高度建立一個方塊
 2     // ,接著比對所有周圍方塊的高度(比對四個方向),填補漏出的部分,在填補時註意越低的方塊在數組中越靠前。
 3     //createCubePath([{i:0,j:0,k:1,landclass:obj_landtype.box_stone},{i:1,j:1,k:2.5,landclass:obj_landtype.box_stone}])
 4    
 5     function createCubePath(cubepath)
 6     {
 7         var len=cubepath.length;
 8         for(var i=0;i<len;i++)//對於每一個xz單元格
 9         {
10             var cube=cubepath[i];
11             disposeCube(cube.i,cube.j);
12             createCube(cube.i,cube.j,cube.k,cube.landclass);
13         }
14         //初次繪製後進行二次對比,初次繪製的必定是xz單元格中的最高點
15         for(var index=0;index<len;index++)
16         {
17             var cube=cubepath[index];
18             var i=cube.i;
19             var j=cube.j;
20             var k=cube.k;
21             //上右下左
22             //取四方的最高
23             var k1=999;
24             if(arr_instance[i])
25             {
26                 var arr1=arr_instance[i][j+1];
27                 if(arr1)
28                 {
29                     var ins_cube1=arr1[arr1.length-1];
30                     k1=ins_cube1.mydata.k;
31                 }
32             }
33             var k2=999;
34             if(arr_instance[i+1])
35             {
36                 var arr2=arr_instance[i+1][j];
37                 if(arr2) {
38                     var ins_cube2 = arr2[arr2.length - 1];
39                     k2=ins_cube2.mydata.k;
40                 }
41             }
42             var k3=999;
43             if(arr_instance[i])
44             {
45                 var arr3=arr_instance[i][j-1];
46                 if(arr3) {
47                     var ins_cube3=arr3[arr3.length-1];
48                     k3=ins_cube3.mydata.k;
49                 }
50             }
51             var k4=999;
52             if(arr_instance[i-1])
53             {
54                 var arr4=arr_instance[i-1][j+1];
55                 if(arr4) {
56                     var ins_cube4=arr4[arr4.length-1];
57                     k4=ins_cube4.mydata.k;
58                 }
59             }
60          
61             //在四方最高中找最低
62             var mink=Math.min(k1,k2,k3,k4);
63 
64             var len2=Math.floor((k-mink)/size_per);
65             for(var index2=1;index2<=len2;index2++)
66             {
67                 createCube(i,j,k-index2,cube.landclass);
68                 //arr_instance[i][j].unshift()
69             }
70         }
71     }

這段代碼包含兩個迴圈,第一個迴圈負責放置選中單元格中最高的那個地塊,第二個迴圈則負責填充最高地塊下麵的支撐,比如高山的山體或者深谷的谷壁。這種填充是靠比較選中的單元格和四面單元格的高度實現的,這個演算法的一個缺點是在需要生成低谷時,谷地周圍的一圈高度不變的平地也需要放入配置數組,否則地形會出現斷裂。
直接在瀏覽器控制臺中執行createCubePath([{i:0,j:0,k:1,landclass:obj_landtype.box_stone},{i:1,j:1,k:2.5,landclass:obj_landtype.box_stone}])命令即可改變地形。這裡你可能想要看到那種“在場景中拖動滑鼠地形隨之起伏”的效果,但我認為這種運行時代碼註入的控制方式反而是WebGL技術相對於傳統桌面3D程式的一大優勢,藉此我們可能實現遠超傳統ui的精細化控制。

可以訪問https://ljzc002.github.io/EmptyTalk/HTML/TEST/testfloor2.html進行測試

5、小結
綜上我們編寫了一個簡單的地形生成方法,但還有更多的工作沒有做,我們需要一些根據某種規則生成配置數組的方法、一些根據規則在同一單元格的不同高度分配不同地塊的方法(關於地形規則的制定也許可以參考這篇隨機生成行星錶面地形的文章https://www.cnblogs.com/ljzc002/p/9134272.html),對於不習慣控制台輸入的使用者還要考慮編寫實用的交互ui。這種空間上離散的地形比較適合編寫時間上離散的回合制3D場景,接下來我們將要討論更適合即時3D場景的連續地形。

二、第二種方法——改進的地面網格

1、Babylon.js內置地面網格的不足
Babylon.js內置了一種平面地面網格和一種高度圖地面網格,但這兩鐘網格存在一些不足:
甲:只能設置相同的x向分段數和z向分段數
乙:地面網格裡沒有xz索引信息,只能通過修改底層頂點位置改變地形
丙:無法表現垂直斷崖和反斜面之類變化劇烈的地形

詳細解釋一下問題丙,地面網格的頂點排布如下圖所示:

使用者將發現他無法在HAOG單元格和ABCO單元格之間生成垂直斷崖,因為地面網格使用的是“簡化的網格”,在AO兩處都各只有一個頂點,無法表現懸崖的上下兩邊,這時使用者只好使用儘量小的單元格生成儘量陡的斜坡來模擬懸崖(這一點正好和無法生成斜坡的地塊拼接法完全相反)。另一方面,每個頂點(比如頂點O)周圍的六條楞線夾角並不均勻,難以生成端正的形狀。

計劃用“條帶網格”替換地面網格來解決問題甲和問題乙,但條帶網格的頂點排布規律與地面網格類似,問題丙仍然存在。
經過觀察,問題丙只在地形變化劇烈時表現明顯,所以決定用條帶網格表現較為平緩的地形大勢(代碼里命名為ground_base),把一些專門定製的“地形附著物網格”(也就是模型)放置在ground_base之上表現劇烈變化的地形,如果需要,再使用某種方式將ground_base與地形附著物融合在一起。

2、生成類似方法一的平坦草原地形,並標註xz索引
渲染效果如下:(可以訪問https://ljzc002.github.io/EmptyTalk/HTML/TEST/testframe2.html查看效果)

括弧中是當前指示物坐標,括弧後是xz索引值

a、設置紋理重覆
與地塊拼接法不同,條帶網預設用一張紋理圖包覆全體頂點,為了能像方法一一樣一格一格的顯示地塊,對地塊材質代碼做如下修改:

1 mat_grass = new BABYLON.StandardMaterial("mat_grass", scene);//1
2         mat_grass.diffuseTexture = new BABYLON.Texture("../../ASSETS/IMAGE/LANDTYPE/grass.jpg", scene);
3         mat_grass.diffuseTexture.uScale = segs_x+1;//紋理重覆效果
4         mat_grass.diffuseTexture.vScale = segs_z+1;
5         mat_grass.freeze();

b、生成條帶網格地面

 1 var arr_path=[];//路徑數組
 2         for(var i=0;i<=segs_x+1;i++)
 3         {
 4             var posx=(i-((segs_x+1)/2))*size_per;
 5             var path=[];
 6             for(var j=0;j<=segs_z+1;j++)
 7             {
 8                 var posz=(j-((segs_z+1)/2))*size_per;
 9                 path.push(new BABYLON.Vector3(posx,0,posz));
10             }
11             arr_path.push(path);
12         }
13         ground_base=BABYLON.MeshBuilder.CreateRibbon("ground_base"
14             ,{pathArray:arr_path,updatable:true,closePath:false,closeArray:false,sideOrientation:BABYLON.Mesh.DOUBLESIDE});
15         ground_base.sideOrientation=BABYLON.Mesh.DOUBLESIDE;
16         ground_base.material=mat_grass;
17         ground_base.renderingGroupId=2;
18         ground_base.metadata={};
19         ground_base.metadata.arr_path=arr_path;
20         obj_ground.ground_base=ground_base;

註意需要把CreateRibbon方法參數中的updatable屬性設為true,否則建立條帶網格之後將不能修改地形。

 c、製作一些藍色小球作為地形參照物:

 1 //5個藍色小球
 2 var mesh_sphereup=new BABYLON.MeshBuilder.CreateSphere("mesh_sphereup",{diameter:0.5},scene);
 3         mesh_sphereup.material=mat_blue;
 4         mesh_sphereup.renderingGroupId=2;
 5         mesh_sphereup.direction=new BABYLON.Vector3(0,-1,2);
 6         mesh_sphereup.isPickable=false;
 7         mesh_sphereup.rayHelper = null;
 8         obj_plane.mesh_sphereup=mesh_sphereup;
 9         var mesh_sphereright=new BABYLON.MeshBuilder.CreateSphere("mesh_sphereright",{diameter:0.5},scene);
10         mesh_sphereright.material=mat_blue;
11         mesh_sphereright.renderingGroupId=2;
12         mesh_sphereright.direction=new BABYLON.Vector3(2,-1,0);
13         mesh_sphereright.isPickable=false;
14         mesh_sphereright.rayHelper = null;
15         obj_plane.mesh_sphereright=mesh_sphereright;
16         var mesh_spheredown=new BABYLON.MeshBuilder.CreateSphere("mesh_spheredown",{diameter:0.5},scene);
17         mesh_spheredown.material=mat_blue;
18         mesh_spheredown.renderingGroupId=2;
19         mesh_spheredown.direction=new BABYLON.Vector3(0,-1,-2);
20         mesh_spheredown.isPickable=false;
21         mesh_spheredown.rayHelper = null;
22         obj_plane.mesh_spheredown=mesh_spheredown;
23         var mesh_sphereleft=new BABYLON.MeshBuilder.CreateSphere("mesh_sphereleft",{diameter:0.5},scene);
24         mesh_sphereleft.material=mat_blue;
25         mesh_sphereleft.renderingGroupId=2;
26         mesh_sphereleft.direction=new BABYLON.Vector3(-2,-1,0);
27         mesh_sphereleft.isPickable=false;
28         mesh_sphereleft.rayHelper = null;
29         obj_plane.mesh_sphereleft=mesh_sphereleft;
30         var mesh_spheremiddle=new BABYLON.MeshBuilder.CreateSphere("mesh_spheremiddle",{diameter:0.5},scene);
31         mesh_spheremiddle.material=mat_blue;
32         mesh_spheremiddle.renderingGroupId=2;
33         mesh_spheremiddle.direction=new BABYLON.Vector3(0,-1,0);
34         mesh_spheremiddle.isPickable=false;
35         mesh_spheremiddle.rayHelper = null;
36         obj_plane.mesh_spheremiddle=mesh_spheremiddle;
37 //為每個小球綁定一個gui標簽
38         for(var key in obj_plane)
39         {
40             var label = new BABYLON.GUI.Rectangle(key);
41             label.background = "black";
42             label.height = "30px";
43             label.alpha = 0.5;
44             label.width = "240px";
45             label.cornerRadius = 20;
46             label.thickness = 1;
47             label.linkOffsetY = 30;//位置偏移量??
48             fsUI.addControl(label);
49             label.linkWithMesh(obj_plane[key]);
50             var text1 = new BABYLON.GUI.TextBlock();
51             text1.text = "";
52             text1.color = "white";
53             label.addControl(text1);
54             label.isVisible=true;
55             //label.layerMask=2;
56             label.text=text1;
57             obj_plane[key].lab=label;
58         }

可以訪問https://www.cnblogs.com/ljzc002/p/7699162.html 查看Babylon.js gui功能的中文文檔

d、根據相機的位置修改路標的位置:

 1 scene.registerAfterRender(
 2             function() {
 3                 //更新5個標記球的位置
 4                 var origin=camera0.position;
 5                 var length=200;
 6                 for(key in obj_plane)
 7                 {
 8                     var mesh=obj_plane[key];
 9                     var direction=mesh.direction;
10                     var ray = new BABYLON.Ray(origin, direction, length);
11                     /*if(mesh.rayHelper)
12                     {
13                         mesh.rayHelper.dispose();
14                     }*/
15                     //mesh.rayHelper = new BABYLON.RayHelper(ray);//這時還沒有_renderLine屬性
16                     //mesh.rayHelper._renderLine.renderingGroupId=2;
17                     //mesh.rayHelper.show(scene);//連續使用兩次show會崩潰?
18                     //難道一幀里只能用一個pick?
19                     //console.log(key);
20                     var hit = scene.pickWithRay(ray,predicate);
21                     if (hit.pickedMesh){
22                         //console.log(key+"2");
23                         mesh.isVisible=true;
24                         var posp=hit.pickedPoint;
25                         mesh.position=posp.clone();
26                         mesh.lab.isVisible=true;
27                         //顯示命中點的坐標以及命中點所在方塊的左下角的兩層索引
28                         var index_x=Math.floor((posp.x+(segs_x+1)*size_per/2)/size_per);
29                         var index_z=Math.floor((posp.z+(segs_z+1)*size_per/2)/size_per);
30                         mesh.lab.text.text="("+posp.x.toFixed(2)+","+posp.y.toFixed(2)+","+posp.z.toFixed(2)+")*"
31                             +index_x+"-"+index_z;
32                     }
33                     else
34                     {//如果沒命中地面則不顯示路標
35                         mesh.lab.isVisible=false;
36                         mesh.isVisible=false;
37                     }
38                 }
39 
40 
41 
42             }
43         )
44     function predicate(mesh){//過濾網格,只允許射線擊中地面系網格,
45         if (mesh.name.substr(0,6)=="ground"){
46             return true;
47         }
48         else
49         {
50             return false;
51         }
52 
53     }

這段代碼在每次渲染後執行,從相機出發向五個方位發射5條射線,將射線和地面的交點做為路標放置點,同時修改gui文本內容。曾經嘗試在這裡使用RayHelper功能顯示射線,但發現在未渲染前RayHelper無法設置渲染組,而渲染後的RayHelper又需立刻換成新對象,舊的渲染組屬性作廢,希望官方能夠優化rayHelper的用法,如果確實需要顯示射線,也許可以用Line功能代替rayHelper。

e、對一些選定的頂點施加矩陣變化:

在控制臺中執行TransVertex(obj_ground.ground_base,[[0,0],[0,1],[1,0]],BABYLON.Matrix.Translation(0,2,0))抬起了左下角的三個頂點:

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 伺服器配置文件分析 bin目錄下的mongod.cfg是伺服器的配置文件,文件中主要的配置參數: 1、資料庫文件的存放位置 2、伺服器日誌文件的存放位置 3、預設的IP地址、埠號 設置密碼 預設情況下,MongoDB的伺服器地址是127.0.0.1,埠號是27017,存儲資料庫管理員信息的adm ...
  • 本篇博文簡單介紹Elasticsearch中term詞條檢索、prefix首碼檢索、wildcard通配符檢索、fuzzy糾錯檢索, 以及boost分數提升等高級檢索的用法, 最後通過複雜檢索的示例, 綜合演示這些檢索語法. ...
  • 1.需求描述 我們知道Windows Cluster 都是多節點的,當虛擬IP漂移的時候,一般都是從一個節點漂移到另外一個節點。如果可以及時捕捉到舊節點信息是什麼、新節點信息是什麼對我們提供高可用的資料庫服務很重要,只有捕捉到這些信息後才可以進一步檢查相應的Job、賬號,甚至是調整相應的應用服務等。 ...
  • 一方關聯多方查詢時執行否定篩選,結果包含未通過篩選的項。 我們規定一方為父,多方為子,我們希望子未通過篩選時,結果也不出現對應的父。 ...
  • 有時候,我們需要查看存儲過程的執行計劃,那麼我們有什麼方式獲取存儲過程的歷史執行計劃或當前的執行計劃呢? 下麵總結一下獲取存儲過程的執行計劃的方法。 1:我們可以通過下麵腳本查看存儲過程的執行計劃,但是有時候,你會發現這種方式並不總是能夠獲取到存儲過程的執行計劃。 SELECT d.object_i... ...
  • 1.需求概括 我們知道,在SQL Server Alwayson 架構中,有多種虛擬IP,例如 WindowsCluster IP,ListenIP,角色高可用性IP(類似於偵聽IP)。在某些條件下,例如系統故障,會觸發虛擬IP的漂移,如何高效率、低延遲、更好地監控IP漂移情況,是我們DB的一個重要 ...
  • 版權聲明:本文為xing_star原創文章,轉載請註明出處! 本文同步自http://javaexception.com/archives/150 沉浸式主題下軟鍵盤問題 項目中,對沉浸式的處理用的是https://github.com/gyf-dev/ImmersionBar 這個開源項目,項目還 ...
  • 一、品牌項目的添加功能的思路(雙向數據綁定): 二、過濾器: 1、過濾器調用時候的格式: {{ name | 過濾器的名稱 }} 2、過濾器的定義語法: 3、定義私有過濾器有兩個條件:過濾器的名稱 和 處理函數 三、自定義指令: 樣式,只要通過指令綁定給了元素,不管這個元素有沒有被插入到頁面中去,這 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...