前言:首先先推薦一篇博客,關於webgl原理,講的非常之通俗易懂了 圖解WebGL&Three.js工作原理 webGL可以理解為openGL ES2.0 (webGL2.0 - openGL ES3.0)的javascript綁定。所以實現的語言是javascript和opengl(最常用的跨平台圖形庫)着色語言,webgl是 HTML5中提出的新技術,是一種3D繪圖標准。 three.js是以webgl為基礎的庫,封裝了一些3D渲染需求中重要的工具方法與渲染循環。WebGL門檻相對較高,Three.js對WebGL提供的接 口進行了非常好的封裝,簡化了很多細節,大大降低了學習成本 我們可能還聽說過一個D3.js(Data-Driven Documents),是一個數據可視化的庫,技術基礎是SVG。兼容性是IE9+,官網(http://d3js.org), 從官網的example中可以看出,它跟3d視圖還是不同的。 |
我們只需要從官網上下載一個three.js,然后用script標簽引入即可。
首先注意的一點是,我們在頁面上並不需要一個canvas標簽,只需要一個盛放canvas的容器就行,canvas是three.js動態生成的。
<!DOCTYPE html> <html> <head> <title>01.01 - WebGLRenderer - Skeleton</title> <script src="../libs/three.js"></script> <style> #canvas3d{ width:800px; height:450px; margin:100px auto; } </style> </head> <body> <div id="canvas3d"></div> </body> </html>
threejs里最重要的幾個元素,如下:
<script> var renderer; //渲染器 var scene; //場景 var camera; //相機 var light; //光源 var cube; //物體 </script>
1,生成3d渲染器,設置渲染器的寬高和背景色,(通常我們可以直接獲取頁面上畫布的寬高,便於嵌入改動)
renderer = new THREE.WebGLRenderer({antialias:true}); //生成渲染器對象(antialias屬性:抗鋸齒效果為設置有效)
renderer.setClearColor(0x333333, 1.0);
//renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setSize('800', '450');
2,設置一個場景,也就是一個三維空間,用 [Scene] 類聲明一個叫 [scene] 的對象。
scene = new THREE.Scene();
3,設置一個攝像機camera,
OpenGL(WebGL)中、三維空間中的物體投影到二維空間的方式中,存在透視投影和正投影兩種相機。 透視投影就是、從視點開始越近的物體越大、遠處的物體繪制的較小的一種方式、和日常生活中我們看物體的方式是一致的。 正投影就是不管物體和視點距離,都按照統一的大小進行繪制、在建築和設計等領域需要從各個角度來繪制物體,因此這種投影被廣泛應用。在 Three.js 也能夠指定透視投影和正投影兩種方式的相機。
正交投影與透視投影的區別如上圖所示,左圖是正交投影,物體發出的光平行地投射到屏幕上,遠近的方塊都是一樣大的;右圖是透視投影,近大遠小,符合我們平時看東西的感覺。
camera坐標系
Three中使用采用常見的右手坐標系定位。
// 四個參數分別代表了攝像機的視角、寬高比、近和遠兩個視截面。 //設置透視投影的相機,默認情況下相機的上方向為Y軸,右方向為X軸,沿着Z軸朝里(視野角:fov 縱橫比:aspect 相機離視體積最近的距離:near 相機離視體積最遠的距離:far) camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 1000); // position and point the camera to the center of the scene camera.position.x = 20; camera.position.y = 18; camera.position.z = 35; camera.lookAt(scene.position); //設置視野的中心坐標
4,設置光源light
OpenGL(WebGL)的三維空間中,存在點光源和聚光燈兩種類型。 而且,作為點光源的一種特例還存在平行光源(無線遠光源)。另外,作為光源的參數還可以進行 [環境光] 等設置。 作為對應, Three.js中可以設置 [點光源(Point Light)] [聚光燈(Spot Light)] [平行光源(Direction Light)],和 [環境光(Ambient Light)]。 和OpenGL一樣、在一個場景中可以設置多個光源。 基本上,都是環境光和其他幾種光源進行組合。 如果不設置環境光,那么光線照射不到的面會變得過於黑暗.
//設置light light = new THREE.DirectionalLight(0xff0000, 1.0, 0); //設置平行光 light.position.set( 200, 200, 200 ); //設置光源向量 scene.add(light); // 追加光源到場景
5,設置物體 object
在three.js中,我們使用Mesh模型,Mesh的構造函數是這樣的:Mesh( geometry, material ) geometry是它的形狀,material是它的材質。 三維模型通常用三角形的網格來描述
對於圖中的兔子,隨着三角形數量的增加,它的表面越來越平滑/准確
我們這里是一個立方體 cube
var cubeGeometry = new THREE.BoxGeometry(20, 10, 15,2,3,1); //設置長寬高 以及對應長寬高的分段,在使用線模式({wireframe:true})進行渲染的時候可以看到效果 var cubeMaterial = new THREE.MeshNormalMaterial({wireframe : true}); //材質 cube = new THREE.Mesh(cubeGeometry, cubeMaterial); var border = new THREE.EdgesHelper( cube,0xffff00 ); //添加邊框 scene.add(cube); scene.add(border);
6,最后一步,進行渲染
//將渲染器的元素添加到頁面中 document.getElementById('canvas3d').appendChild(renderer.domElement); renderer.render(scene, camera);
完整的代碼已經上傳到github上: github(three-one) 如果你覺得我寫的對你有所幫助的話,請給我個star吧,謝謝
最后的效果圖如下:
在上面的學習基礎上,我們繼續深入的探究一下,如何給3d視圖添加動畫,紋理等;
1,首先我們在上面的基礎上,添加多個立體幾何圖形
//立方體 var cubeGeometry = new THREE.BoxGeometry(15,15,15,1,1,1); var cubeMaterial = new THREE.MeshNormalMaterial({wireframe : true}); //材質 cube = new THREE.Mesh(cubeGeometry, cubeMaterial); var border = new THREE.EdgesHelper( cube,0xffff00 ); //添加邊框 scene.add(cube); scene.add(border); //圓柱體 var cylinderGeometry = new THREE.CylinderGeometry(8, 8,10,30,30); var cylinderMaterial = new THREE.MeshNormalMaterial(); var cylinder = new THREE.Mesh(cylinderGeometry,cylinderMaterial); cylinder.position.x = -10; cylinder.position.y = -5; cylinder.position.z = 25; cylinder.castShadow = true; scene.add(cylinder); //球體 var sphereGeometry = new THREE.SphereGeometry(7, 25, 25); var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff}); var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); // position the sphere sphere.position.x = 0; sphere.position.y = 0; sphere.position.z = 0; sphere.castShadow = true; // add the sphere to the scene scene.add(sphere); //圓環 var torusGeometry = new THREE.TorusGeometry(10,3,20,20); var torusMaterial = new THREE.MeshBasicMaterial(); var tours = new THREE.Mesh(torusGeometry,torusMaterial); tours.position.x = 10; tours.position.y = -10; tours.position.z = -40; tours.castShadow = true; scene.add(tours);我們通過position屬性
調整立體幾何在scene中的位置(x,y,z)
創建幾何體時有一點強調的是,對於參數的設置,例如創建圓環的時候,
THREE.TorusGeometry(10,3,20,20)
我們第三四個參數分割比的值越大,立體幾何中拼湊的平面圖形就越多,立體幾何就越圓滑,就是上一篇博客中兔子的那個原理。
2,添加動畫
我們要針對每個幾何體添加不同的動畫,所以就需要為每個幾何體添加一個name屬性來指定,比如:
cube.name = 'cube'; cylinder.name = 'cylinder';
然后在render函數中,用getObjectByName獲取到對應的幾何體,用setInterval的思想原理,通過requestAnimationFrame函數使得幾何體動起來
scene.getObjectByName('cube').rotation.x += control.rotationSpeed; scene.getObjectByName('cube').scale.set(control.scale, control.scale, control.scale); scene.getObjectByName('cylinder').rotation.z += control.rotationSpeed2; scene.getObjectByName('tours').rotation.z += 0.05; requestAnimationFrame(render);
3,stats性能插件
stats.js用於對JavaScript進行性能檢測。
我們創建一個createStats的函數,然后在init初始化中調用它
function createStats() { var stats = new Stats(); stats.setMode(0); stats.domElement.style.position = 'absolute'; stats.domElement.style.left = '0px'; stats.domElement.style.top = '5px'; return stats; }
需要特別注意的一點是,我們需要在render函數中不斷的更新stats顯示
stats.update();
dat.gui.js用於創建菜單欄,可以用來控制場景中的各個參數來調試場景。
function addControls(controlObject){ var gui = new dat.GUI(); gui.add(controlObject,'rotationSpeed',-0.1,0.1); gui.add(controlObject, 'scale', 0.01, 2); gui.add(controlObject, 'rotationSpeed2', -0.1, 0.1); }
創建addControls函數,然后在init初始化函數中設置默認值,並調用這個函數
control = new function (){ this.rotationSpeed = 0.005; this.scale = 1; this.rotationSpeed2 = 0.05; } addControls(control);
4,添加紋理
這個首先注意的就是圖片應該是異步獲取的,所以你可以放在本地的apache中,也可以自己用nodejs非常方便的搭建一個服務器,不然的話,他就會報錯,跨域了。
var texture = new THREE.ImageUtils.loadTexture("http://10.1.26.29:84/Brick-2399.jpg"); torusMaterial.map = texture;
最后的效果圖如下:
完整的代碼:github(threejs-two) 如果你覺得我寫的對你有幫助的話,請給我個star吧,謝謝,我會繼續更新下去的
5,最后,從完整的代碼中,我們可以看出,關於材質,我們也是調用了不同的函數,這里總結一下材質
材質種類:
MeshBasicMaterial:為幾何體賦予一種簡單的顏色,或者顯示幾何體的線框
MeshDepthMaterial:根據網格到相機的距離,該材質決定如何給網格染色
MeshNormalMaterial:根據物體表面的法向量計算顏色
MeshFaceMaterial:這是一種容器,可以在該容器中為物體的各個表面上設置不同的顏色
MeshLambertMaterial:考慮光照的影響,可以創建顏色暗淡,不光亮的物體
MeshPhongMaterial:考慮光照的影響,可以創建光亮的物體
ShaderMaterial:使用自定義的着色器程序,直接控制頂點的放置方式,以及像素的着色方式。
LineBasicMaterial:可以用於THREE.Line幾何體,從而創建着色的直線
LineDashedMaterial:類似與基礎材質,但可以創建虛線效果
(1)MeshBasicMaterial:不考慮光照的影響。
屬性:
color
wireframe
wireframeLinewidth
wireframeLinecap:線段端點如何顯示。可選值有:butt(平)、round、square。默認是round。WebGLRenderer對象不支持該屬性。
wireframeLinejoin:線段連接點如何顯示。可選值有:round、bevel(斜角)、miter(尖角)。默認是round。WebGLRenderer對象不支持屬性。
shading:着色模式。可選值:THREE.SmoothShading、THREE.FlatShading。
vertexColors:為每個頂點定義不同的顏色。在CanvasRenderer對象中不起作用。
fog:指示當前是否會受全局霧化效果設置的影響。
兩種設置屬性的方式:
//1.構造函數 var meshMaterial = new THREE.MeshBasicMaterial({color:0xffccff}); //2屬性 meshMaterial.visible = false;
(2)MeshDepthMaterial
使用這種材質的物體,其外觀不是由光照或某個材質屬性決定的;而是由物體到相機的距離決定的。可以將這種材質與其他材質相結合,從而很容易創建逐漸消失的效果。
只有兩個控制線框的屬性:
wireframe
wireframeLinewidth
可以通過設置相機的near和far的值,來控制創建中使用這種材質的物體的消失速度。如果near和fat之間的差值越大,那么物體遠離相機時,只會稍微消失一點;反之,物體消失的效果非常明顯。
var cubeMaterial = new THREE.MeshDepthMaterial(); var colorMaterial = new THREE.MeshBasicMaterial({color:0x00ff00,transparent:true,blending:THREE.MultiplyBlending}); var cube = new THREE.SceneUtils.createMultiMaterialObject(cubeGeometry,[colorMaterial,cubeMaterial]); cube.children[1].scale.set(0.99,0.99,0.99);//避免渲染遮擋而造成的閃爍
(3)MeshNormalMaterial
法向量的作用: 決定光的發射方向、在計算光照、陰影時提供信息、為物體表面上色。法向量所指的方向決定每個面從MeshNormalMaterial材質獲取的顏色。
屬性:
wireframe
wireframeLinewidth
shading
for(var f = 0 , f1 = sphere.geometry.faces.length; f < f1 ; f++){ var face = spere.geometry.faces[f]; var arrow = new THREE.ArrowHelper(face.normal,face.centroid,2,0x3333ff); spere.add(arrow); }
在球體的每個面上添加了一個長度為2,顏色為0x3333ff的箭頭
(4)MeshFaceMaterial
可以為幾何體的每一個面指定不同的材質。
假設有一個正方體,可以為每個面指定不同的顏色。
var matArray = []; matArray.push(new THREE.MeshBasicMaterial({color:0x00ff00})); matArray.push(new THREE.MeshBasicMaterial({color:0x00ff00})); matArray.push(new THREE.MeshBasicMaterial({color:0x00ff00})); matArray.push(new THREE.MeshBasicMaterial({color:0x00ff00})); matArray.push(new THREE.MeshBasicMaterial({color:0x00ff00})); matArray.push(new THREE.MeshBasicMaterial({color:0x00ff00})); var faceMaterial = new THREE.MeshFaceMaterial(matArray); var cubeGeometry = new THREE.CubeGeometry(3,3,3); var cube = new THREE.Mesh(cubeGeometry,faceMaterial);
(5)MeshLambertMaterial
對光源有反應。
基本屬性:
color、opacity、shading、blending、depthTest、depthWrite、wireframe、wireframeLineWith、wirefLinecap、wireframeLinejoin、vertexColors、fog。
獨特屬性:
ambient:和AmbientLight光源一起使用。該顏色會與AmbientLight光源的顏色相乘。默認是白色。
emissive:該材質發射的屬性。不像是光源,只是一種純粹的、不受其他光照影響的顏色。默認是黑色。
(6)MeshPhongMaterial
基本屬性:
color、opacity、shading、blending、depthTest、depthWrite、wireframe、wireframeLineWith、wirefLinecap、wireframeLinejoin、vertexColors、fog。
獨特屬性:
ambient
emissive
specular:指定該材質的光亮程度及其高光部分的顏色。如果將他設置成跟color屬性相同的顏色,將會得到一種更加類似金屬的材質。如果設置為灰色,材質將變得更像塑料。
shininess:指定高光部分的亮度。默認是30.
(7)ShaderMaterial
屬性:
wireframe
wireframeLinewidth
shading
vertexColor
fog:指示當前是否會受全局霧化效果設置的影響。
獨特屬性:
fragmentShader:定義每個傳入的像素的顏色。
vertexShader:允許你修改每一個傳入的頂點的位置
uniforms:該屬性可以向你的着色器發送消息。將同樣的信息發送到每一個頂點和片段。
defines:該屬性可以轉換為vertexShader和fragmentShader里的#define代碼。該屬性可以用來設置着色器程序里的一些全局變量。
attributes:該屬性可以修改每個頂點和片段。常用來傳遞位置數據和法向量相關的數據。如果要用這個屬性,辣么你要為幾何體中的所有頂點提供信息。
lights:定義光照數據是否傳遞給着色器。默認是false。
獨特屬性:
fragmentShader:定義每個傳入的像素的顏色。
vertexShader:允許你修改每一個傳入的頂點的位置
uniforms:該屬性可以向你的着色器發送消息。將同樣的信息發送到每一個頂點和片段。
defines:該屬性可以轉換為vertexShader和fragmentShader里的#define代碼。該屬性可以用來設置着色器程序里的一些全局變量。
attributes:該屬性可以修改每個頂點和片段。常用來傳遞位置數據和法向量相關的數據。如果要用這個屬性,辣么你要為幾何體中的所有頂點提供信息。
lights:定義光照數據是否傳遞給着色器。默認是false。
(8)LineBasicMaterial
基本屬性:
color
lineWidth
LineCap:butt、round、square。默認是round。WebGLRenderer不支持該屬性。
LineJoin:round、bevel(斜切)、miter(尖角)。默認是round。WebGLRenderer不支持該屬性。
vertexColors:該屬性值設置為 THREE.VertexColors值時,就可以為每個頂點指定一種顏色。
fog:指定當前物體是否受全局霧化效果的影響。
(9)LineDashedMaterial
和LineBasicMaterial有着一樣的屬性,但是有幾個額外的屬性,可以用來定義短划線長度和短划線中間空格長度的屬性。
獨特屬性:
scale:縮放dashSize和gapSize。如果scale<1,則dashSize和gapSize就會增大。
dashSize:短線划的長度
gapSize:間隔的長度