個人three.js學習記錄(二)

經過上一篇文章個人three.js學習記錄(一)基本上是入門了three.js,可是這不夠3D,此次我但願能把以前作的demo弄出來,而後經過例子來分析操做步驟。javascript

1. 示例

圖一

上圖是以前作的一個demo,有點醜,但願不要介意。
這個主要是外面一層包裹着天空盒, 而後裏面是一個由開頂的立方體作成的房子(暫且理解爲房子)以及裏面的傢俱構成,其中包括能夠播放視頻的電視,一個能夠照的鏡子,導入的沙發模型等html

2. 操做步驟

2.1 準備工做

首先,咱們須要上一篇文章的基礎,這裏再也不贅述,咱們直接進入主題,首先須要初始化咱們所須要的環境,代碼以下:java

2.1.1 加入html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>day0613</title>

    <style>
        body {
            background-color: #000;
            color: #fff;
            margin: 0;
            overflow: hidden;
        }
    </style>
    <script src="js/three.js"></script>
    <script src="js/stats.min.js"></script>
    <script src="js/dat.gui.js"></script>
    <script src="js/Detector.js"></script>
    <script src="js/DDSLoader.js"></script>
    <script src="js/OBJLoader.js"></script>
    <script src="js/MTLLoader.js"></script>
    <script src="js/OrbitControls.js"></script>
    <script src="js/day0613.js"></script>
    <script src="js/Mirror.js"></script>

</head>
<body>
<!--用於加載視頻資源-->
<video id="video" src="video/sintel.ogv" style="display: none; left: 15px; top: 75px;"></video>

<div id="webgl" style="position: absolute; left: 0; top: 0;"></div>

<script type="text/javascript">
    threeStart();
</script>
</body>
</html>

咱們須要加入<video>加載咱們的視頻,這是在咱們的模型中播放的紋理資源,而後div #webgl是用於嵌入WebGL的渲染器的,接下來調用threeStart()這個函數來進入咱們3D的世界git

2.1.2 初始化變量
var WALL_POSITION_Y = 150;
var WALL_HEIGHT = 355;
var WALL_WIDTH = 10;
var WALL_LENGTH = 800;
var TV_WIDTH = WALL_LENGTH / 4;
var TV_HEIGHT = WALL_HEIGHT / 3;
var TV_THICKNESS = 5;
var SKYBOX_HEIGHT = 0;
var MIRROR_WIDTH = 70;
var MIRROR_HEIGHT = 150;


var camera, scene, renderer, container;
var material, controls, stats, video, texture, object;
var verticalMirrorMesh;
var mirror;

以上的變量我沒有註釋,若是不知道能夠查下單詞,記得我寫的時候是查字典寫的,翻譯應該沒錯:-)github

2.1.3 threeStart()

在這裏,由於涉及的東西不是可以一會兒就可以說出來,因此先來一個整體的概覽可使得本身在內心有必定的印象,知道本身應該作些什麼web

var threeStart = function () {

    //判斷瀏覽器是否支持webGL
    if (!Detector.webgl) Detector.addGetWebGLMessage();
    //窗口尺寸改變事件
    window.addEventListener('resize', onWindowResize, false);

    /**
     * 窗口監聽,用於改變窗口時可以實時保持窗口的比例
     */
    function onWindowResize() {
        //從新設置相機寬高比
        camera.aspect = window.innerWidth / window.innerHeight;
        //更新相機的投影矩陣,這裏沒有理解什麼意思,我把它理解爲更新相機裏面的各類參數
        camera.updateProjectionMatrix();
        //從新設置
        renderer.setSize(window.innerWidth, window.innerHeight);
    }

    initRenderer();
    initScene();
    initCamera();
    //加入燈光
    initLight();
    //這裏是繪製TV、牆壁、鏡子、地板
    paint();
    createSky();
    //導入模型
    initObj();
    //回調重複渲染
    arimate();
};

這裏我前面的加入相機、渲染器、場景的部分先不說,咱們能夠看到的是須要在原有個人three.js學習記錄(一)基礎上加入燈光、導入模型、繪製包含紋理的形狀、渲染等數組

2.1.4 加入燈光
/**
 * 初始化燈光
 */
function initLight() {
    //環境光,沒有方向,任意方向的發射光線
    var ambient = new THREE.AmbientLight();
    //環境光強度
    ambient.intensity = .8;
    //場景加入環境光
    scene.add(ambient);
    //方向光,顏色十六進制表示 0xffeedd
    var directionalLight = new THREE.DirectionalLight(0xffeedd);
    //設置方向光的來源點
    directionalLight.position.set(0, 10, 10).normalize();
    scene.add(directionalLight);
}
2.1.5 回調渲染

這裏就是當咱們改變了視角以後這是一個連貫的動畫,須要咱們的計算機重複的渲染場景才行,因此須要一個函數去重複的調用才行瀏覽器

/**
 * 回調函數,重畫整個場景
 */
function arimate() {
   if (video.readyState === video.HAVE_ENOUGH_DATA) {
        //設置咱們的紋理須要更新 這裏的紋理就是視頻紋理
        //其實更新的就是將視頻播放的畫面從新截取到紋理,一幀一幀咱們看起來就是動畫
        if (texture) texture.needsUpdate = true;
       video.play();
    }
    //鏡子也須要渲染,它至關於攝像機從這個3d世界看到的渲染到平面(鏡子)
    mirror.renderWithMirror(new THREE.Mirror(renderer, camera));
    //渲染
    renderer.render(scene, camera);
    //fps狀態更新
    stats.update();
    //從新調用arimate
    requestAnimationFrame(arimate);
}

2.2 天空盒

我先不按照threeStart()函數的調用順序展開說明,這裏我先來一個簡單的建立天空盒createSky()app

/**
 * 建立天空盒
 */
function createSky() {
    //這部分是給出圖片的位置及圖片名
    var imagePrefix = "img/sky/dawnmountain-";
    var directions  = ["xpos", "xneg", "ypos", "yneg", "zpos", "zneg"];
    var imageSuffix = ".png";

    //建立一個立方體而且設置大小
    var skyGeometry = new THREE.CubeGeometry( 5000, 5000, 5000 );
    //這裏是用於天空盒六個面儲存多個材質的數組
    var materialArray = [];
    //循環將圖片加載出來變成紋理以後將這個物體加入數組中
    for (var i = 0; i < 6; i++)
        materialArray.push( new THREE.MeshBasicMaterial({
            //這裏imagePrefix + directions[i] + imageSuffix 就是將圖片路徑弄出來
            map: THREE.ImageUtils.loadTexture( imagePrefix + directions[i] + imageSuffix ),
            side: THREE.BackSide  //由於這裏咱們的場景是在天空盒的裏面,因此這裏設置爲背面應用該材質
        }));

    //MultiMaterial能夠將MeshBasicMaterial多個加載而後直接經過Mesh生成物體
    var skyMaterial = new THREE.MultiMaterial( materialArray );
    //加入形狀skyGeometry和材質MultiMaterial
    var sky = new THREE.Mesh( skyGeometry, skyMaterial );
    //設置天空盒的高度
    sky.position.y = SKYBOX_HEIGHT;
    //場景當中加入天空盒
    scene.add( sky );
}

雖然上面實現的東西不復雜,可是以上短短的代碼須要理解確實不是很是容易,這裏有涉及到了圖片紋理的問題(有圖形學的基礎會好點吧,可是我上課的忘了差很少),可能我並不能真正意義的去理解,我如今只是須要快速的入門,可以簡單的使用three.js就好了less

2.3 繪製圖形

接下來咱們來繪製咱們的‘房子’,整體的代碼以下:

/**
 * 繪製場景
 */
function paint() {
    paintFloor();
    paintMirror();
    paintWall();
    paintTV();
}
2.3.1 繪製地板
function paintFloor() {
        var loader = new THREE.TextureLoader;
        loader.load('img/floor.jpg', function (texture) {
            texture.wrapS = texture.wrapT = THREE.RepeatWrapping; //這裏設置x和y超過了圖片的像素以後進行的是重複繪製圖片操做
            texture.repeat = new THREE.Vector2(5, 5); //設置圖片重複繪製的密度這裏是5*5
            //設置材質是雙面應用該圖片材質
            var floorMaterial = new THREE.MeshBasicMaterial({map: texture, side: THREE.DoubleSide});
            //地板使用PlaneGeometry生成平面
            var floorGeometry = new THREE.PlaneGeometry(WALL_LENGTH, WALL_LENGTH);
            //生成地板的模型
            var floor = new THREE.Mesh(floorGeometry, floorMaterial);
            //設置地板的位置
            floor.position.y = -27;
            floor.rotation.x = Math.PI / 2;
            scene.add(floor);//場景加載該地板
        });
    }
2.3.2 繪製鏡子

咱們這個鏡子是由兩個部分組成的,一個是後面的一個盒子,一個就是鏡面,盒子是爲了看起來凸顯一點,若是沒有鏡面鏡子將會是這樣的

加入鏡片以後效果就不同了

如下是代碼的實現

function paintMirror() {
    //這裏是背面的盒子
        var Geometry = new THREE.BoxGeometry(MIRROR_WIDTH + 3, MIRROR_HEIGHT + 3, 5);
        var Material = new THREE.MeshBasicMaterial({color:0x000, side: THREE.DoubleSide});
        var Mesh = new THREE.Mesh(Geometry, Material);
        //肯定位置
        Mesh.position.y = 50;
        Mesh.position.z = -220;
        Mesh.position.x = -395;
        Mesh.rotateY(Math.PI / 2);
        scene.add(Mesh);

        //three.js有一個Mirror.js用於生成鏡子的,這是我在官方的示例看的
        //這裏有些代碼不是很理解,都是直接使用官方給出的,不過按照個人理解
        //主要是給它一個渲染器和照相機,它將3d世界看到的從新渲染一遍,可是須要不斷渲染,因此在回調函數中須要更新
        mirror = new THREE.Mirror( renderer, camera);
        verticalMirrorMesh = new THREE.Mesh( new THREE.PlaneBufferGeometry(MIRROR_WIDTH, MIRROR_HEIGHT), mirror.material );
        verticalMirrorMesh.add( mirror );
       
}
2.3.3 繪製牆壁
function paintWall() {

        //圖片加載器,加載成紋理
        var loader = new THREE.TextureLoader;
        var wallOutside = loader.load('img/wall-outside.jpg');
        var wallInside = loader.load('img/wall-inside.jpg');
        //設置紋理的過濾方式
        wallInside.wrapT = wallInside.wrapS = THREE.RepeatWrapping;
        wallInside.repeat = new THREE.Vector2(5, 5);
        
        //材質
        var materials = [];
        materials.push(new THREE.MeshBasicMaterial()); //默認的材質,沒有紋理
        materials.push(new THREE.MeshBasicMaterial());
        materials.push(new THREE.MeshBasicMaterial());
        materials.push(new THREE.MeshBasicMaterial());
        //這裏設置了兩種不一樣圖片生成的紋理,牆外的和牆內的
        materials.push(new THREE.MeshBasicMaterial({map: wallOutside, side: THREE.DoubleSide}));
        materials.push(new THREE.MeshBasicMaterial({map: wallInside, side: THREE.DoubleSide}));
        //這6個基礎材質的數組做爲參數傳遞給MeshFaceMaterial
        var faceMaterial = new THREE.MultiMaterial(materials);

        //建立其中一面牆,而後其餘的由此面牆生成
        var Geometry = new THREE.BoxGeometry(WALL_LENGTH, WALL_HEIGHT, WALL_WIDTH);
        //設置牆的位置參數
        var wallFront = new THREE.Mesh(Geometry, faceMaterial);
        wallFront.position.y = WALL_POSITION_Y;

        //經過clone函數能夠獲得一個全新的面,而後經過位置的改變做爲其餘牆面
        var wallLeft = wallFront.clone();
        wallLeft.rotation.y = 3 * Math.PI / 2;
        wallLeft.position.x = -WALL_LENGTH / 2;
        wallLeft.width = WALL_LENGTH + 100;

        var wallRight = wallFront.clone();
        wallRight.rotation.y = Math.PI / 2;
        wallRight.position.x = WALL_LENGTH / 2;

        var wallBack = wallFront.clone();
        wallBack.rotation.y = Math.PI;
        wallBack.position.z = -WALL_LENGTH / 2;

        wallFront.position.z = WALL_LENGTH / 2;
        scene.add(wallFront);
        scene.add(wallLeft);
        scene.add(wallRight);
        scene.add(wallBack);
    }
2.3.4 繪製電視

這裏跟鏡子的實現是同樣的,背面一個盒子(加了紋理),而後加一個面用於貼入視頻紋理的,代碼以下:

function paintTV() {

        //獲取咱們的視頻的元素
        video = document.getElementById('video');
        //將咱們的視頻加載爲紋理(播放時的每一幀均可以將它看做圖片)
        texture = new THREE.Texture(video);

        //這裏是屏幕
        var tvScreenGeometry = new THREE.PlaneGeometry(TV_WIDTH - 2, TV_HEIGHT - 4);
        //將視頻紋理加入
        var tvScreenMaterial = new THREE.MeshBasicMaterial({map: texture, side: THREE.DoubleSide});
        var tvScreen = new THREE.Mesh(tvScreenGeometry, tvScreenMaterial);
        tvScreen.position.y = WALL_HEIGHT / 3 + 1;
        tvScreen.position.z = -WALL_LENGTH / 2 + 8;

        //這裏是屏幕後面的盒子
        var loader = new THREE.TextureLoader();
        var tvGeometry = new THREE.BoxGeometry(TV_WIDTH, TV_HEIGHT, TV_THICKNESS);
        var tvMaterial = new THREE.MeshBasicMaterial({map: loader.load('img/tv.jpg')});

        var tv = new THREE.Mesh(tvGeometry, tvMaterial);
        tv.position.y = WALL_HEIGHT / 3;
        tv.position.z = -WALL_LENGTH / 2 + TV_THICKNESS;

        scene.add(tvScreen);
        scene.add(tv);
    }

2.4 加載模型

/**
 * 初始化模型
 */

function initObj() {
	//這裏咱們是用max導出的obj模型包含材質
	//這裏是直接官網的例子
    // THREE.Loader.Handlers.add(/\.dds$/i, new THREE.DDSLoader());
    //材質加載器
    var mtlLoader = new THREE.MTLLoader();
    //設置路徑
    mtlLoader.setPath('./model/');
    //導入材質
    mtlLoader.load('room.mtl', function (materials) {
	    //材質導入調用這個回調函數(鉤子函數)
        // materials.preload();
        //obj模型加載器
        var objLoader = new THREE.OBJLoader();
        //設置將傳入材質參數
        objLoader.setMaterials(materials);
		//設置路徑
        objLoader.setPath('./model/');
        //導入模型時能夠加入執行信息和錯誤信息的函數,這裏我沒有加入
        //onProgress, onError
        //導入模型
        objLoader.load('room.obj', function (object) {
            //將導入的模型旋轉到合適的位置
            object.rotation.y = Math.PI;
            scene.add(object);
        });

    });
}

2.5 其餘

這裏除了以上的東西還有其餘的小組件,如fps監聽器(我截屏時左下角的東西),還有咱們的控制器,控制器其實就是經過改變咱們的模型位置或者是照相機位置達到的移動效果等等這些裏面所用到的我沒有在這裏拿出來,若是須要的能夠將代碼拿回去研究

3. 總結

由於我以前是須要快速入門three.js,因此沒有好好的系統地學習,因此我以爲我並無掌握好這項技能,並且在上課(圖形學,OpenGL)也沒有利用好機會努力地學,圖形學基礎有點差,因此我若是要接觸這類相關的仍是須要努力將圖形學弄好,而且理解three.js的東西

基礎部分推薦
http://www.hewebgl.com/article/articledir/1
https://www.zhihu.com/question/36367846?from=profile_question_card
http://blog.csdn.net/doupi520/article/category/6645411
除了以上還有上篇個人three.js學習記錄(一)推薦的

以上的代碼已經上傳Github

相關文章
相關標籤/搜索