我的three.js學習記錄(二)

来源:http://www.cnblogs.com/lger/archive/2017/10/16/7678674.html
-Advertisement-
Play Games

通過上一篇文章 "我的three.js學習記錄(一)" 基本上是入門了three.js,但是這不夠3D,這次我希望能把之前做的demo弄出來,然後通過例子來分析操作步驟。 1. 示例 上圖是之前做的一個demo,有點醜,希望不要介意。 這個主要是外面一層包裹著 "天空盒" , 然後裡面是一個由開頂的 ...


通過上一篇文章我的three.js學習記錄(一)基本上是入門了three.js,但是這不夠3D,這次我希望能把之前做的demo弄出來,然後通過例子來分析操作步驟。

1. 示例

圖一

上圖是之前做的一個demo,有點醜,希望不要介意。
這個主要是外面一層包裹著天空盒, 然後裡面是一個由開頂的立方體做成的房子(暫且理解為房子)以及裡面的傢具構成,其中包括可以播放視頻的電視,一個可以照的鏡子,導入的沙發模型等

2. 操作步驟

2.1 準備工作

首先,我們需要上一篇文章的基礎,這裡不再贅述,我們直接進入主題,首先需要初始化我們所需要的環境,代碼如下:

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的世界

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;

以上的變數我沒有註釋,如果不知道可以查下單詞,記得我寫的時候是查字典寫的,翻譯應該沒錯:-)

2.1.3 threeStart()

在這裡,因為涉及的東西不是能夠一下子就能夠說出來,所以先來一個總體的概覽可以使得自己在心裡有一定的印象,知道自己應該做些什麼

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()

/**
 * 創建天空盒
 */
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就行了

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


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

-Advertisement-
Play Games
更多相關文章
  • 獲取驗證碼 var countdown = 60; var but = document.getElementById('time'); but.addEventListener('click', function (e) { setTime(this); }) function setTime (... ...
  • 假設有這樣一個數組: 我們想去掉數組中id重覆的對象,比如同樣id為2的兩個對象—— 我們該如何去做呢? 事實上,對於數組對象,傳統的去重方法無能為力,至於forEach()、filter()等迭代方法也不好使;真正能做到優雅去重的,是ES5新增加的一個方法——reduce() reduce()方法 ...
  • Bootstap這個框架本身已經包含了開髮網頁的眾多要素,包括了常用的工具以及擴展組件,如果你在開發頁面時覺得在某些方面還不夠的話,不妨看看最新收集的50個Bootstrap擴展插件,這些插件在我們平時開發頁面中經常會用到,從按鈕、麵包屑導航、日曆、樹形菜單、媒體播放器、以及可視化編輯器等等,如果認 ...
  • ...
  • 下麵舉例14道前端面試題選擇題(摘自妙味課堂) 1、 選項:A、object B、array C、arguments D、undefined 解析:arguments即是實參的集合,又稱類數組對象。他的類型是對象object。因此選A。 2、 選項:A、number B、undefined C、fu ...
  • <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"><!--編碼--> <title>我的第一個網頁(題目)</title></head><body><p>你們好!這是我的第一個網頁,歡迎你們的訪問。<br/>這裡用b r換行</p ...
  • 一、box-shadow的參數解析 此處參考http://blog.csdn.net/baidu_31345523/article/details/50264869 二、box-shadow應用 在說明我們這個應用之前,先給大家推薦一個網站http://www.css88.com/tool/css3 ...
  • 謝琳·伍德蕾(Shailene Woodley) 和 提奧·詹姆斯(Theo James) 謝琳·伍德蕾(Shailene Woodley),1991年11月15日出生於美國加州,美國影視演員。 五歲就被星探發掘,2002年開始,她在《尋人密探組》、《橘子郡男孩》等劇中頻繁的出鏡,2005年她出演了 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...