[WebAR和WebVR學習之路]組成Three.js場景的基本組件

組成Three.js場景的基本組件

《創(chuàng)建場景》

場景中的基本組件:相機猜憎、光源和幾何體

THREE.Scene()對象就是以上基本組件的一個容器朴皆。

習題2-1:

創(chuàng)建一個平面帕识、相機和適當光源。在dat.GUI中創(chuàng)建Add Cube和Remove Cube按鈕遂铡,并顯示目前場景中物體的個數(shù)肮疗。Add Cube和Remove Cube會分別在平面的上方創(chuàng)建或移除一個Cube,Cube保持第一章中的自旋動畫并使用UI控制扒接。

<!DOCTYPE html>

<html>

<head>
    <title>Example 02.01 - Basic Scene</title>
    <script type="text/javascript" src="../libs/three.js"></script>

    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            /* set margin to 0 and overflow to hidden, to go fullscreen */
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>

<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">

    // once everything is loaded, we run our Three.js stuff.
    function init() {

        var stats = initStats();

        // create a scene, that will hold all our elements such as objects, cameras and lights.
        var scene = new THREE.Scene();

        // create a camera, which defines where we're looking at.
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        scene.add(camera);

        // create a render and set the size
        var renderer = new THREE.WebGLRenderer();

        renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMapEnabled = true;

        // create the ground plane
        var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;

        // rotate and position the plane
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 0;
        plane.position.y = 0;
        plane.position.z = 0;

        // add the plane to the scene
        scene.add(plane);

        // position and point the camera to the center of the scene
        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 30;
        camera.lookAt(scene.position);

        // add subtle ambient lighting
        var ambientLight = new THREE.AmbientLight(0x0c0c0c);
        scene.add(ambientLight);

        // add spotlight for the shadows
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40, 60, -10);
        spotLight.castShadow = true;
        spotLight.shadowMapHeight=4096;
        spotLight.shadowMapWidth=4096;
        scene.add(spotLight);

        // add the output of the renderer to the html element
        document.getElementById("WebGL-output").appendChild(renderer.domElement);

        // call the render function
        var step = 0;

        var controls = new function () {
            this.rotationSpeed = 0.02;
            this.numberOfObjects = scene.children.length;

            this.removeCube = function () {
                var allChildren = scene.children;
                var lastObject = allChildren[allChildren.length - 1];
                if (lastObject instanceof THREE.Mesh) {
                    scene.remove(lastObject);
                    this.numberOfObjects = scene.children.length;
                }
            };

            this.addCube = function () {

                var cubeSize = Math.ceil((Math.random() * 3));
                var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
                var cubeMaterial = new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff});
                var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
                cube.castShadow = true;
                cube.name = "cube-" + scene.children.length;


                // position the cube randomly in the scene

                cube.position.x = -30 + Math.round((Math.random() * planeGeometry.parameters.width));
                cube.position.y = Math.round((Math.random() * 5));
                cube.position.z = -20 + Math.round((Math.random() * planeGeometry.parameters.height));

                // add the cube to the scene
                scene.add(cube);
                this.numberOfObjects = scene.children.length;
            };

            this.outputObjects = function () {
                console.log(scene.children);
            }
        };

        var gui = new dat.GUI();
        gui.add(controls, 'rotationSpeed', 0, 0.5);
        gui.add(controls, 'addCube');
        gui.add(controls, 'removeCube');
        gui.add(controls, 'outputObjects');
        gui.add(controls, 'numberOfObjects').listen();

        render();

        function render() {
            stats.update();

            // rotate the cubes around its axes
            scene.traverse(function (e) {
                if (e instanceof THREE.Mesh && e != plane) {

                    e.rotation.x += controls.rotationSpeed;
                    e.rotation.y += controls.rotationSpeed;
                    e.rotation.z += controls.rotationSpeed;
                }
            });

            // render using requestAnimationFrame
            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

        function initStats() {

            var stats = new Stats();

            stats.setMode(0); // 0: fps, 1: ms

            // Align top-left
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            document.getElementById("Stats-output").appendChild(stats.domElement);

            return stats;
        }
    }
    window.onload = init


</script>
</body>
</html>

場景的相關函數(shù):

Scene.Add():向場景中添加物體
Scene.Remove():在場景中移除物體
Scene.children():獲取場景中的所有子對象的列表
Scene.getChildByName():獲取場景中某個特定名字的子物體

在render函數(shù)中伪货,我們的處理方式和上一章差不讀,只不過在這里對使用scene.traverse()來進行一個對場景中的對象進行一個遍歷钾怔,及對每個子對象都執(zhí)行一次相應函數(shù)碱呼。
我們也可以使用for循環(huán)來遍歷場景的children這個屬性數(shù)組來達到相同的結果。

《場景的特殊效果》

Scene的霧

scene.fog=new THREE.FogExp2(0xffffff,0.015);

注意宗侦,使用霧會增加資源使用愚臀,降低幀率。

材質(zhì)覆蓋

scene.overrideMaterial=new THREE.MeshLambertMaterial({color:0xffffff});

使用上述函數(shù)將會覆蓋場景中所有材質(zhì)為指定材質(zhì)

《幾何體和網(wǎng)格對象》

在第一章我們用到了如何在場景中添加一個幾何體和網(wǎng)格對象矾利,如添加一個立方體的代碼如下:

var cubeGeometry = new THREE.CubeGeometry(4,4,4);
var cubeMaterial = new THREE.MeshPhongMaterial({color:0xff0000});
var cube=new THREE.Mesh(cubeGeometry,cubeMaterial);
scene.add(cube);

在上述代碼中懊悯,我們定義了該網(wǎng)格對象的形狀、幾何結構梦皮、外觀炭分、材質(zhì)等屬性,并把這些屬性和一個名為cube的網(wǎng)格對象結合在一起剑肯。

《幾何對象的屬性和函數(shù)》

geometry變量:
geometry變量是三維空間中的點集以及將這些點連接起來的面捧毛。(頂點vertice和面face)
例如:一個立方體有8個頂點(8個角),每個頂點的空間坐標為一個x,y,z的組合让网。這些點稱為頂點
一個立方體有6個面呀忧,這6個面稱為face。
像在opengl里面定義幾何體一樣溃睹,我們可以使用定義頂點和面的方法來定義一個幾何體:

var vertices=[
    new THREE.Vector3(1,3,1),
    new THREE.Vector3(1,3,-1),
    ...
    new THREE.Vector3(-1,-1,1)
];
var faces=[
    new THREE.Face3(0,2,1),
    new THREE.Face3(2,3,1),
    ...
    new THREE.Face3(3,6,4)
];
var geom=new THREE.Geometry();
geom.vertices=vertices;
geom.faces=faces;
geom.computeCentroids();
geom.mergeVertices()();

關于如何動態(tài)修改幾何體的頂點和面的知識而账,會在后面講到。

《網(wǎng)格對象的函數(shù)和屬性》

網(wǎng)格對象的主要屬性:
position
Rotation
Scale
TranslateX
TranslateY
TranslateZ
上述屬性與Unity3D API中的Transform的屬性相類似
可以通過直接修改position的值來改變網(wǎng)格物體的位置因篇,網(wǎng)格物體的position其實質(zhì)上是一個THREE.Vector3類型的變量泞辐,所以其設置的方法有如下:

Obj.position.x=1;…
Obj.position=new THREE.Vector3(1,2,3);
Obj.position.set(1,2,3);

在設置對象位置的過程中笔横,設置的位置是相對于其父對象的位置而設置的。比如使用THREE.SceneUtils.createMultiMaterialObject()創(chuàng)建多個不同材質(zhì)的對象時咐吼,返回的不僅僅是一個對象吹缔,而是一個對象組。如果我們改變其中一個對象的位置的時候锯茄,另一個對象不會發(fā)生改變厢塘,如果改變這個對象組的位置的時候,其所有子物體都會發(fā)生改變肌幽。(父子階層)
對于rotation的操作對象是數(shù)學中的弧度(rad)晚碾,一個物體旋轉一周的弧度是2*PI。所以我們的旋轉操作代碼如下:

Obj.rotation.x=0.5*Math.PI;(旋轉0.5π個弧度喂急,即旋轉90度)
Obj.rotation.set(0.5*Math.PI,0,0);
Obj.rotation=new THREE.Vector3(0.5*Math.PI,0,0);

上述代碼表示繞著x軸旋轉90°的操作迄薄。
Scale基本同上。
對于translate函數(shù)煮岁,使用translate可以改變物體的位置讥蔽,translate是相對于物體所移動的位移,而非絕對位置(與Unity的Translate類似)画机。

《相機》

正交相機和透視相機是所有的3D引擎中都存在的2種相機冶伞。對于不同的需求我們選擇使用不同的相機。
對于透視相機THREE.PerspectiveCamera主要參數(shù)如下:
Fov(視角場):人類眼鏡的視野大概為180°步氏,一些鳥類有著360°的視野响禽,在游戲中我們通常使用60到90°的視角,而在普通的3D應用中我們推薦使用45°的視野荚醒。
Aspect(長寬比):長寬比指的是渲染結果輸出的橫向長度和縱向長度的比值芋类。推薦使用window.innerWidth/window.innerHeight
Near(近視平面)
Far(遠視平面)

對于正交相機THREE.OrthographicCamera的主要參數(shù)如下:
Left(左邊界)
Right(右邊界)
Top(上邊界)
Bottom(下邊界)
Near(近視平面)
Far(遠視平面)
以上一些屬性及其含義可以在OpenGL的官網(wǎng)文檔中找到。

如何讓相機看向指定位置界阁?
使用camera.lookAt(new THREE.Vector3(x,y,z));函數(shù)做到使相機看向x,y,z點侯繁。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市泡躯,隨后出現(xiàn)的幾起案子贮竟,更是在濱河造成了極大的恐慌,老刑警劉巖较剃,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咕别,死亡現(xiàn)場離奇詭異,居然都是意外死亡写穴,警方通過查閱死者的電腦和手機惰拱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啊送,“玉大人偿短,你說我怎么就攤上這事欣孤。” “怎么了翔冀?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵导街,是天一觀的道長披泪。 經(jīng)常有香客問我纤子,道長,這世上最難降的妖魔是什么款票? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任控硼,我火速辦了婚禮,結果婚禮上艾少,老公的妹妹穿的比我還像新娘卡乾。我一直安慰自己,他們只是感情好缚够,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布幔妨。 她就那樣靜靜地躺著,像睡著了一般谍椅。 火紅的嫁衣襯著肌膚如雪误堡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天雏吭,我揣著相機與錄音锁施,去河邊找鬼。 笑死杖们,一個胖子當著我的面吹牛悉抵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摘完,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼姥饰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了孝治?” 一聲冷哼從身側響起媳否,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎荆秦,沒想到半個月后篱竭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡步绸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年掺逼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓤介。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吕喘,死狀恐怖赘那,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情氯质,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布闻察,位于F島的核電站拱礁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏辕漂。R本人自食惡果不足惜呢灶,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钉嘹。 院中可真熱鬧鸯乃,春花似錦、人聲如沸跋涣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陈辱。三九已至,卻和暖如春性置,著一層夾襖步出監(jiān)牢的瞬間拾并,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工鹏浅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嗅义,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓隐砸,卻偏偏與公主長得像之碗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子季希,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容