threejs構建web三維視圖入門教程

本文是一篇簡單的webGL+threejs構建web三維視圖的入門教程,你能夠了解到利用threejs建立簡單的三維圖形,而且控制圖形運動。如有不足,歡迎指出。
本文使用的框架是three.js
github地址:https://github.com/mrdoob/three.js
官網:http://threejs.org/
文檔:http://threejs.org/docs/javascript

本文中的示例已上傳github,地址:https://github.com/RizzleCi/three.js-democss

1、建立場景

咱們所見的視圖由兩個部分共同建立,scene和camera。
首先定義一個場景:html

var scene = new THREE.Scene();

而後定義一個相機:java

var camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );

等等,定義相機須要視窗的長寬。如今我要讓個人繪圖顯示在頁面的一個區域(<div>)標籤中。咱們來選中這個元素,獲取它的長寬。git

var container = document.getElementById('canvasdiv');
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;

這樣再加上前面的一行代碼,咱們就完成了對相機的定義。而後把相機位置設定在z軸上方便觀察。
camera.position.set(0,0,10)github

如今咱們須要一個渲染器把定義好的場景渲染出來。web

var renderer = new THREE.WebGLRenderer();

給這個渲染器合適的大小。canvas

renderer.setSize( width, height );

而後將其加入到dom中。跨域

canvasdiv.appendChild( renderer.domElement );

(運行之後發現這其實就是一個canvas元素。其實咱們也能夠在html中建立canvas元素再將renderer綁定到它上面。var renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('mainCanvas') });
最後進行渲染。瀏覽器

renderer.render(scene,camera);

這樣,就創建了一個簡單的3d場景。

2、繪製圖形

我將threejs中的物體理解爲模型+材料。以一個長方體爲例。
建立模型:

var geometry = new THREE.BoxGeometry( 1,2,1 );

定義材料:

var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );

有了這二者,咱們就能夠構建一個長方體方塊了。

var cube = new THREE.Mesh( geometry, material );

咱們將其添加到場景中顯示。

scene.add( cube );

這樣,一個三維的長方體就繪製完成了
關於其餘形狀的繪製,張雯莉的threejs入門指南中介紹的很詳細,在此很少贅述。
這部分的完整代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
#canvasdiv{
    width: 600px;
    height: 600px;
}

    </style>
    <script src="js/three.min.js"></script>
</head>
<body>


    <div class="main-content" id="canvasdiv">
        
    </div>


<script type="text/javascript" >
var container = document.getElementById('canvasdiv')
var scene = new THREE.Scene();
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;
var camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
camera.position.set(0,0,10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
canvasdiv.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 2,1,1 );
var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
renderer.render(scene,camera);
</script>
</body>
</html>

圖片描述
看上去它只是一個長方形而已,可是它確實是一個立體圖形,你能夠改變一下camera的位置來觀察一下。

camera.position.set( 5,3,10 );

圖片描述
好了,這樣看起來是一個立體的長方體了吧。

3、建立3d對象

大多數時候,咱們須要講繪製的圖形整合到一塊兒進行控制。此時,咱們便須要一個3d對象。

建立一個本身的對象

在上面繪製的那個長方體上面再放一個球。

var geometry = new THREE.SphereGeometry( 0.5,100,100 );
var material = new THREE.MeshBasicMaterial( { color: 0xb9c16c } );
var ball = new THREE.Mesh( geometry,material );
ball.position.set( 0,0,1 );
scene.add(ball);

另說一句,默認放置mesh的位置是( 0,0,0 ),和改變相機位置同樣,咱們能夠用ball.position.set方法來改變圖形或對象的位置。所以動畫也利用這個方法來實現。
而後要把它們整合成一個對象。
首先咱們建立一個對象。

var myobj = new THREE.Object3D();

而後把咱們畫的圖形添加到對象裏就ok啦。

myobj.add( cube );
myobj.add( ball );

這時候咱們已經有了一個3d對象,它包含咱們剛剛繪製的長方形和球。因而就沒有必要像原來那樣把圖形一個一個地放置到場景裏,只須要把剛剛建立的對象放置到場景裏。

scene.add( myobj );

效果圖

完整代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
#canvasdiv{
    width: 600px;
    height: 600px;
}

    </style>
    <script src="js/three.min.js"></script>

</head>
<body>


    <div class="main-content" id="canvasdiv">
        
    </div>


<script type="text/javascript" >
var container = document.getElementById('canvasdiv')
var scene = new THREE.Scene();
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;
var camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
camera.position.set( 0,0,10 )
var renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
canvasdiv.appendChild( renderer.domElement );

var geometry = new THREE.BoxGeometry( 2,1,1 );
var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );
var cube = new THREE.Mesh( geometry, material );
//scene.add( cube );

var geometry = new THREE.SphereGeometry( 0.5,100,100 );
var material = new THREE.MeshBasicMaterial( { color: 0xb9c16c } );
var ball = new THREE.Mesh( geometry,material );
ball.position.set( 0,0,1 );
//scene.add(ball);

var myobj = new THREE.Object3D();
myobj.add( cube );
myobj.add( ball );
scene.add( myobj );

renderer.render(scene,camera);
</script>
</body>
</html>

外部導入.obj文件

threejs支持從外部導入.obj文件,據說這種文件是用3DsMax繪製的,用PS也能夠編輯。咱們須要引入OBJMTLLoader.js,MTLLoader.js文件;也有一個OBJLoader.js,但利用這個庫只能導入模型而不能導入繪製obj時添加的材質,我的感受不是很是實用,就不作介紹了。這時候,咱們須要把文件們放到一個服務器上,不然會出現跨域問題。
爲了讓圖像更明顯,咱們添加一些光線。

scene.add( new THREE.AmbientLight( 0xffffff ) );

這裏,咱們經過導入圖片來設置這個對象的紋理。

var texture = new THREE.Texture();
var loader = new THREE.ImageLoader( );
loader.load( 'tank.jpg', function ( image ) {
    texture.image = image;
    texture.needsUpdate = true;
} );

開始導入咱們的3D對象!

var loader = new THREE.OBJMTLLoader();  
loader.load('tank.obj','tank.mtl',function(object){ 
    tank = object;

    object.traverse(function(child){
        if (child instanceof THREE.Mesh){
        //將貼圖賦於材質
            child.material.map = texture;
            child.material.transparent = true;
        }
    });
    object.position.set(0,0,0);
    scene.add( object );  
    camera.lookAt( object.position ); 
    renderer.render( scene,camera );  
});

圖片描述
模型導入進去了,可是看起來仍是很奇怪,這就要咱們加一些其餘光線渲染一下。在這裏咱們添加平行光線。

var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.5 );
directionalLight.position.set( 1, 1, 1 )
scene.add( directionalLight );

圖片描述
變得光澤多了。
從外部導入obj的完整代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
#canvasdiv{
    width: 1200px;
    height: 800px;
}

    </style>
    <script src="js/three.min.js"></script>
    <script src="js/MTLLoader.js"></script>  
    <script src="js/OBJMTLLoader.js"></script> 
</head>
<body>


    <div class="main-content" id="canvasdiv">
        
    </div>


<script type="text/javascript" >
var container = document.getElementById('canvasdiv')
var scene = new THREE.Scene();
var width = canvasdiv.clientWidth;
var height = canvasdiv.clientHeight;
var camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
camera.position.set( -10,10,10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
canvasdiv.appendChild( renderer.domElement );


var texture = new THREE.Texture();
var loader = new THREE.ImageLoader(  );
loader.load( 'tank.jpg', function ( image ) {

    texture.image = image;
    texture.needsUpdate = true;

} );
var loader = new THREE.OBJMTLLoader();  
loader.load('tank.obj','tank.mtl',function(object){ 
    tank = object;
    object.traverse(function(child){
        if (child instanceof THREE.Mesh){
            //將貼圖賦於材質
            child.material.map = texture;
            //重點,沒有該句會致使PNG沒法正確顯示透明效果
            child.material.transparent = true;
        }
    });
    object.position.set(0,0,0);
    scene.add( object );  
    camera.lookAt( object.position );
    renderer.render( scene,camera ); 
});  
scene.add( new THREE.AmbientLight( 0xffffff ) );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.5 );
directionalLight.position.set( 1, 1, 1 )
scene.add( directionalLight );

renderer.render(scene,camera);
</script>
</body>
</html>

4、動畫

如今咱們想辦法讓這些圖形動起來。
在threejs中運用最多的動畫是用requestAnimationFrame()方法。也能夠利用傳統的setInterval()作,但用這個會掉幀。
這裏咱們作一個render函數,來進行渲染和動畫調用。這裏之前面添加了myobj對象的代碼爲基礎。

基本的動畫

如今來不斷地改變對象的角度方便對其進行觀察。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
#canvasdiv{
    width: 600px;
    height: 600px;
}

    </style>
    <script src="js/three.min.js"></script>

</head>
<body>


    <div class="main-content" id="canvasdiv">
        
    </div>


<script type="text/javascript" >
var container,camera,scene,renderer,myobj;

var init = function () {
    container = document.getElementById('canvasdiv')
    scene = new THREE.Scene();
    var width = canvasdiv.clientWidth;
    var height = canvasdiv.clientHeight;
    camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
    camera.position.set( 0,0,10 )
    renderer = new THREE.WebGLRenderer();
    renderer.setSize( width, height );
    canvasdiv.appendChild( renderer.domElement );

    var geometry = new THREE.BoxGeometry( 2,1,1 );
    var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );
    var cube = new THREE.Mesh( geometry, material );
    //scene.add( cube );

    var geometry = new THREE.SphereGeometry( 0.5,100,100 );
    var material = new THREE.MeshBasicMaterial( { color: 0xb9c16c } );
    var ball = new THREE.Mesh( geometry,material );
    ball.position.set( 0,0,1 );
    //scene.add(ball);

    myobj = new THREE.Object3D();
    myobj.add( cube );
    myobj.add( ball );
    scene.add( myobj );    
}


var render = function () {
    requestAnimationFrame( render );
    myobj.rotation.x+=0.01;
    myobj.rotation.y+=0.01;
    renderer.render(scene,camera);
}
init()
render()
</script>
</body>
</html>

對動畫進行控制

接着讓咱們對動畫進行控制。
在我實現的項目中,是經過websocket鏈接後臺傳入參數來控制對象運動的,這裏就介紹一下使用參數控制吧。
這裏有一個問題,就是requestAnimationFrame回調的函數不能帶有參數,不然會出現奇怪的bug。因此我選擇用一個全局對象來進行控制。

var control={
    s:0,
    p:0,
    q:0,
    j:0,
}

這裏s是運動的速度,p,q,j分別是myobj將要運動到的位置的x,y,z座標。咱們先寫一個控制它在x軸上運動的函數:

var run = function () {
    if ( myobj.position.x<control.p ) {
        myobj.position.x += control.s;
        requestAnimationFrame( run );
        renderer.render( scene,camera )
    };
    if ( myobj.position.x>control.p ) {
        myobj.position.x -= control.s;
        requestAnimationFrame( run );
        renderer.render( scene,camera )
    };
}

再在render函數中添加對run的調用requestAnimationFrame( run )。這樣就能夠在命令行中改變對象control的值實現控制myobj的運動。可是在運動中止後個人瀏覽器爲何會變得很卡,並且運動速度回有變化。我還不知道緣由。不知道有沒有朋友和我有一樣的問題。因而我把函數拆成了兩個,這樣瀏覽器性能好些。

var run = function () {

    if ( myobj.position.x<control.p ) {
        myobj.position.x += control.s;            
        requestAnimationFrame( run );
    };
        renderer.render( scene,camera )
}
var runx = function () {

    if ( myobj.position.x>control.p ) {
    myobj.position.x -= control.s;            
        requestAnimationFrame( runx );
    };
        renderer.render( scene,camera )    
}

一樣的,也能夠寫出在y,z軸上運動的函數。
在x軸上運動的完整代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
#canvasdiv{
    width: 600px;
    height: 600px;
}

    </style>
    <script src="js/three.min.js"></script>

</head>
<body>


    <div class="main-content" id="canvasdiv">
        
    </div>


<script type="text/javascript" >
var container,camera,scene,renderer,myobj;

var init = function () {
    container = document.getElementById('canvasdiv')
    scene = new THREE.Scene();
    var width = canvasdiv.clientWidth;
    var height = canvasdiv.clientHeight;
    camera = new THREE.PerspectiveCamera( 90, width/height, 0.1, 1000 );
    camera.position.set( 0,0,10 )
    renderer = new THREE.WebGLRenderer();
    renderer.setSize( width, height );
    canvasdiv.appendChild( renderer.domElement );

    var geometry = new THREE.BoxGeometry( 2,1,1 );
    var material = new THREE.MeshBasicMaterial( { color: 0x645d50 } );
    var cube = new THREE.Mesh( geometry, material );
    //scene.add( cube );

    var geometry = new THREE.SphereGeometry( 0.5,100,100 );
    var material = new THREE.MeshBasicMaterial( { color: 0xb9c16c } );
    var ball = new THREE.Mesh( geometry,material );
    ball.position.set( 0,0,1 );
    //scene.add(ball);

    myobj = new THREE.Object3D();
    myobj.add( cube );
    myobj.add( ball );
    scene.add( myobj );    
}

var control={
    s:0,
    p:0,
    q:0,
    j:0,
}

var run = function () {

    if ( myobj.position.x<control.p ) {
        myobj.position.x += control.s;            
        requestAnimationFrame( run );
    };
        renderer.render( scene,camera )
}
var runx = function () {

    if ( myobj.position.x>control.p ) {
    myobj.position.x -= control.s;            
        requestAnimationFrame( runx );
    };
        renderer.render( scene,camera )    
}

var render = function () {
    requestAnimationFrame( run );
    requestAnimationFrame( runx );
    renderer.render(scene,camera);
}


init()
render()
</script>
</body>
</html>

這個入門教程就到這裏了,感謝閱讀。

相關文章
相關標籤/搜索