構建 3D 的場景除了創建模型,對模型設置顏色和貼圖外,還需要有燈光的效果才能更逼真的反映真實世界的場景。這個例子我覺得既美觀又代表性很強,所以拿出來給大家分享一下。
本例地址:http://www.hightopo.com/guide/guide/core/lighting/examples/example_flowing.html
例子動圖:
上面場景中主要的知識點包括:3D 燈光以及 3D 模型的流動。
- 場景搭建
整個場景中包括 2D 場景(也就是鷹眼部分)以及 3D 場景:
dm = new ht.DataModel(); g3d = new ht.graph3d.Graph3dView(dm); g3d.setGridVisible(true);//指定是否顯示網格 g3d.setGridColor('#74AADA');//指定網格線顏色 g3d.getView().className = 'main';//設置類名 document.body.appendChild(g3d.getView());//將3d組件添加進body體中 window.addEventListener('resize', function(e) { g3d.invalidate(); }, false); g2d = new ht.graph.GraphView(dm); g2d.setAutoScrollZone(-1);//設置自動滾動區域大小,當鼠標距離拓撲邊緣小於這個值時,拓撲自動滾動(調整translateX或translateY) g2d.getView().className = 'g2d'; g2d.setEditable(true);//設置拓撲中的圖元是否可編輯 document.body.appendChild(g2d.getView()); ht.Default.callLater(g2d.fitContent, g2d, [true, 50, true]);//獲取全局下一個id編號 g3d.setHeadlightRange(2000);//燈影響范圍,默認為0代表可照射到無窮遠處,如果設置了值則光照射效果隨物體遠離光影而衰減
所有
HT
組件最根層都為一個 div 組件,可通過組件的 getView() 函數獲得,這里就是利用這種方法將 3D 和 2D 組件添加進 body 體中的。只要 3D 和 2D 共用同一個數據容器,那么數據容器中的圖元都是共用的,也就是說只要我們排布好 2D 或者 3D 中的圖元,那么剩下的那個組件中圖元的排布以及樣式都是根據排布好的組件來排布的。 - 添加燈光
場景中出現的燈光,除了會旋轉的燈光,還有就是兩個靜止的紅燈和黃燈,當旋轉的燈光照向其他地方的時候看得比較清楚:
redLight = new ht.Light();//燈類 redLight.p3(0, 0, -175);//實例變量的位置 redLight.s({ 'light.color': 'red',//燈光顏色 'light.range': 400//燈影響范圍,默認為0代表可照射到無窮遠處,如果設置了值則光照射效果隨物體遠離光影而衰減 }); dm.add(redLight);//將實例變量添加進數據容量中 rotateLight = new ht.Light(); rotateLight.s({ 'light.color': 'green', 'light.type': 'spot'//默認為point點光燈,可設置為spot聚光燈,以及directional的方向光類型 }); dm.add(rotateLight); yellowLight = new ht.Light(); yellowLight.p3(0, 0, 60); yellowLight.s({ 'light.color': 'yellow', 'light.range': 200 }); dm.add(yellowLight);
- 場景中模型的構建
首先是地板的創建,地板是一個圓形的地板,通過設置樣式 shape3d 為 cylinder,剩下的只要設置好大小、位置以及樣式等等即可:
floor = new ht.Node();//Node 節點類 floor.s3(1100, 10, 1100); floor.p3(0, -100, -110); floor.s({ 'shape3d': 'cylinder',//設置 3D 模型為圓形 'shape3d.side': 100,//默認值為0,決定3d圖形顯示為幾邊型,為0時顯示為平滑的曲面效果 'shape3d.color': 'white',//默認值為#3498DB,3d圖形整體顏色 '3d.selectable': false,//默認值為true,控制圖元在Graph3dView上是否可選中 '2d.visible': false//默認值為true,控制圖元在GraphView上是否可見 }); dm.add(floor);
接着添加地板外圍的 8 根圓柱:
for(var i=0; i<8; i++){ var angle = Math.PI*2*i/8; pillar = new ht.Node(); pillar.s({ 'shape3d': 'cylinder', 'shape3d.color': 'white', 'shape': 'circle',//多邊形類型圖元,為空時顯示為圖片 'shape.background': 'gray'//多邊形類型圖元背景 }); pillar.s3(50, 180, 50); pillar.p3(Math.cos(angle)*480, 0, -110+Math.sin(angle)*480); dm.add(pillar); }
還有就是這些“箭頭”作為貼圖的模型,各種各樣的,這里我就只解析一個,比較靠前的“波動”部分,具體的多邊形的描述請參考形狀手冊:
其中 image 的部分是通過 ht.Default.setImage 函數來創建的名為 arrow 的貼圖。shape3 = new ht.Shape();//多邊形類 dm.add(shape3); shape3.setTall(60);//設置高度 shape3.setThickness(0);//設置厚度 shape3.s({//設置樣式 'shape.background': null, 'shape.border.width': 10,//多邊形類型圖元邊框寬度 'shape.border.color': 'blue', 'all.visible': false,//六面是否可見 'front.visible': true, 'front.blend': 'blue',//前面染色顏色 'front.reverse.flip': true,//前面的反面是否顯示正面的內容 'front.image': 'arrow',//前面貼圖 'front.uv.scale': [16, 3]//前面貼圖的uv縮放,格式為[3,2] }); shape3.setPoints([//設置點數組 {x: 0, y: 0}, {x: 25, y: -25}, {x: 50, y: 0}, {x: 75, y: 25}, {x: 100, y: 0}, {x: 125, y: -25}, {x: 150, y: 0}, {x: 175, y: 25}, {x: 200, y: 0} ]); shape3.setSegments([//描述點連接樣式 1, // moveTo 3, // quadraticCurveTo 3, // quadraticCurveTo 3, // quadraticCurveTo 3 // quadraticCurveTo ]); shape3.p3(-100, 0, 100); shape3.setRotationZ(-Math.PI/2);//設置圖元在3D拓撲中沿z軸的旋轉角度(弧度制)
- 設置定時器使各個模型中的圖片“流動”以及旋轉燈光的旋轉
offset = 0; angle = 0; setInterval(function(){ angle += Math.PI/50; rotateLight.p3(400*Math.cos(angle), 70, -110+400*Math.sin(angle));//設置旋轉燈光的坐標 offset += 0.1; uvOffset = [offset, 0]; shape1.s({ 'front.uv.offset': uvOffset//前面貼圖的uv縮放,格式為[3,2] }); shape2.s({ 'front.uv.offset': uvOffset }); shape3.s({ 'front.uv.offset': uvOffset }); shape4.s({ 'front.uv.offset': uvOffset }); shape5.s({ 'shape3d.uv.offset': uvOffset,//決定3d圖形整體貼圖的uv縮放,格式為[3,2] 'shape3d.top.uv.offset': uvOffset,//決定3d圖形頂面貼圖的uv縮放,格式為[3,2] 'shape3d.bottom.uv.offset': uvOffset//決定3d圖形底面貼圖的uv縮放,格式為[3,2] }); cylinder.s({ 'shape3d.uv.offset': uvOffset }); torus.s({ 'shape3d.uv.offset': uvOffset }); }, 200);
整個例子結束,感覺就是“小代碼大效果”,代碼量少而且簡單,效果又非常不錯,大家有興趣可以去官網或者手冊中查看其它的例子。