threejs 基礎概要

threejs 基礎概要

點擊查看官方文檔javascript

下面是翻譯的內容(稍做修改)html

先了解一下Three.js應用程序的結構。Three.js應用程序須要建立一堆對象並將它們鏈接在一塊兒。下圖表示一個小three.js應用程序的圖。
java

關於上圖的注意事項canvas

  • Renderer 渲染器,將一個場景和一個攝像機傳遞給渲染器,它就會將攝像機截錐內的3D場景部分做爲2D圖像呈現給畫布。數組

  • 有一個樹狀結構的場景圖,其中包括各類物體,如Scene對象,多個 Mesh對象,Light對象,Group,Object3D,和Camera對象。一個Scene對象定義了場景圖的根,而且包含諸如背景色和霧化度之類的屬性。這些對象定義了分層的父/子樹狀結構,並表示對象出如今何處,以及如何定向。瀏覽器

  • 注意,上圖中的Camera相機,佔場景圖中的一半。這是爲了表示在three.js中,與其餘對象不一樣。攝像機沒必要在場景圖中才能正常工做。與其餘對象同樣,Camera 做爲其餘對象的子對象,將相對於其父對象移動和定向。less

  • Mesh 網格對象,表示用特定的材質繪製特定的幾何圖形。Material對象和Geometry對象均可以被多個網格對象使用。例如,要在不一樣的位置繪製2個藍色立方體,咱們可能須要2個網格對象來表示每一個立方體的位置和方向。咱們只須要一個Geometry來保存一個立方體的頂點數據,一個Material來指定藍色。兩個網格對象能夠引用相同的Geometry對象和相同的Material對象函數

  • Geometry 幾何對象,表示某些幾何形狀的頂點數據,例如球體,立方體,平面,狗,貓,人,樹,建築物等。Three.js提供了許多內置的幾何圖元。同時也能夠建立自定義幾何圖形以及,或者從文件加載幾何圖形(如***.obj文件)。動畫

  • Material 表面材質對象,標識繪製幾何圖形的表面屬性,包括要使用的顏色及其光澤程度等。1個Material還能夠引用一個或多個Texture對象,這些對象能夠用於例如將圖像包裝到幾何圖形的表面上。ui

  • Texture 紋理對象,一般表示從圖像文件加載,從畫布生成或從另外一個場景渲染的圖像。

  • Light 燈光對象,表明不一樣類型的燈光。

下面製做一個最小的「Cube」場景,以下所示

首先加載three.js

<script type="module">
    // 1.直接使用線上資源
    import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';

    // 2.直接使用本地 three.js
    import *as THREE from "./three.js/build/three.js"

    // 3.使用本地 three.module.js
    // 本地啓服務(http-server等), import 才能訪問本地 three.module.js
    import *as THREE from "./three.js/build/three.module.js"
    
</script>

script標籤中 type="module" 很重要。這使咱們可以使用import關鍵字來加載three.js。還有其餘方法能夠加載three.js,可是從r106版本開始,建議使用模塊。模塊的優勢是能夠輕鬆導入所需的其餘模塊。這使咱們沒必要手動加載它們依賴的額外腳本。

建立canvas標籤

<body>
    <canvas id="c"></canvas>
</body>

建立render

< script type = "module" >
    import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';

    function main() {
        const canvas = document.querySelector('#c');
        const renderer = new THREE.WebGLRenderer({
            canvas
        });
        ...
    }
    main();
</script>

在查找畫布以後,咱們建立了一個WebGLRenderer。渲染器負責獲取你提供的全部數據並將其呈現到畫布上。過去有其餘渲染器,好比CSSRenderer, CanvasRenderer,未來可能會有WebGL2Renderer或WebGPURenderer。目前使用WebGLRenderer使用WebGL渲染3D到畫布。

注意這裏有一些注意的細節。若是你沒有傳遞一個畫布到three.js,它會爲你建立一個,但你必須把它添加到你的文檔。在哪裏添加它取決於你的用例,你必須改變你的代碼,因此咱們發現傳遞一個畫布給three.js感受更靈活。

我能夠將畫布放在任何地方,代碼會找到它,就好像我有代碼能夠將畫布插入到文檔中,若是個人用例發生變化,我可能須要更改代碼。

建立Camera

接下來,咱們須要一臺照相機。下面代碼將建立一個PerspectiveCamera(透視相機)。

const fov = 75; // 視場角
const aspect = 2; // canvas 默認的寬高比例 默認 300/150 = 2
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

fovfield of view 的縮寫,表示視場角。在這種狀況下,垂直尺寸爲75度。注意,Three.js中的大多數角度都以 弧度 表示,但因爲某些緣由,透視相機取 。下圖(網上截的圖)中的ω,就表示視場角。

aspect 是畫布的顯示方面。咱們將在另外一篇文章中詳細介紹 ,但默認狀況下,畫布尺寸爲300x150像素,因此寬高比爲300/150=2。

nearfar 表示將要渲染在相機前面的空間範圍。該範圍以前或以後的任何內容都會被裁剪(不繪製)。

fov、aspect、near、far四個值,定義了一個「視錐」。視錐是3d形狀的名稱,它的形狀像一個金字塔,頂端被切掉。換句話說,將「視錐」這個詞視爲另外一種3D形狀,例如球體,立方體,棱鏡,視錐。

近平面和遠平面的高度由視場肯定。兩個平面的寬度由視場和外觀肯定。

定義的視錐內部的全部內容都將被繪製。視錐外面的都不繪製。

相機默認是向下看-Z軸,向上看+Y軸。咱們把立方體放在原點,因此咱們須要把攝像機從原點日後移一點,這樣才能看到任何東西。

camera.position.z = 2;

在上圖中,咱們能夠看到咱們的攝像機在z = 2處。它沿着-Z軸向下看。咱們的截錐從攝像機前面0.1個單位開始,到攝像機前面5個單位。由於在這個圖中,咱們是向下看的,視野是受角度影響的。咱們的畫布的寬度是它的高度的兩倍,因此整個視野將比咱們指定的75度的垂直視野寬得多。

建立Scene

接下來建立一個scene。three.js中的一個場景,能夠當作是一種場景圖形式的根。任何你想要讓three.js繪製的東西,都須要添加到場景中。

const scene = new THREE.Scene();

建立Geometry

接下來,咱們建立一個包含盒子數據的BoxGeometry。
幾乎任何咱們想要在Three.js中顯示的東西,都須要定義構成3D對象的頂點的幾何圖形。

const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

建立表面Material

而後咱們建立一個基本材質並設置它的顏色。顏色可使用標準的CSS樣式6位十六進制顏色值來指定。

const material = new THREE.MeshBasicMaterial({
    color: 0x44aa88
})

建立網格Mesh

而後建立一個網格。在three.js中,網格表明了一個幾何體(物體的形狀)和一個材質(如何繪製物體,閃亮的仍是平坦的,什麼顏色,應用什麼紋理)的組合等。以及該對象在場景中的位置、方向和比例。

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

將網格添加到場景

scene.add(cube);

渲染場景

而後,咱們能夠經過調用渲染器的render函數,並將場景和攝像機傳遞給它,來渲染場景

renderer.render(scene, camera);

開啓動畫渲染

沿着-Z軸觀察的而立方體自己是與軸對齊的,因此咱們只能看到一個面。用動畫讓它旋轉,就能夠看清3D繪製的效果。
使用瀏覽器的window.requestAnimationFrame進行渲染。

function render(time) {
    time *= 0.001; // 轉化一下時間
    cube.rotation.x = time; // 繞x軸旋轉網格
    cube.rotation.y = time; // 繞y軸旋轉網格
    renderer.render(scene, camera); // renderer渲染
    requestAnimationFrame(render);
}
requestAnimationFrame(render);

雖然好了一點,但仍是很難看到3d效果。添加一些照明會有幫助,因此讓咱們添加一盞燈。three.js中有許多種類的light

建立燈光light

const color = 0xFFFFFF; // 光的顏色
  const intensity = 1; // 光照強度
  const light = new THREE.DirectionalLight(color, intensity);
  light.position.set(-1, 2, 4); // 燈的位置
  scene.add(light);

定向燈有一個位置和一個目標。二者都默認爲0, 0, 0。在本例中,咱們將燈光的位置設置爲-1, 2, 4,這樣它的位置好,就稍微在相機的左邊、上面和後面一些。定向燈的目標座標仍然是(0, 0, 0),因此它會照向原點。

咱們還須要換Materail。基本的Material不受光線的影響。讓咱們把它改爲MeshPhongMaterial,它會受到光線的影響。

再建立幾個網格Mesh

它如今應該是比較清晰的3D視圖了。爲了好玩,咱們再加兩個方塊。咱們讓每一個立方體使用相同的幾何圖形,但製做不一樣的材質,所以每一個立方體能夠是不一樣的顏色。
首先,將建立一個函數,用指定的顏色建立一個新的Material,而後加上指定的Geometry,建立一個Mesh,並將其添加到場景中,並設置其在x軸上的位置。

function makeInstance(geometry, color, x) {
    const material = new THREE.MeshPhongMaterial({
        color
    });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
    cube.position.x = x;
    return cube;
}

而後咱們將使用三種不一樣的顏色和X軸位置調用三次函數, 將生成的Mesh實例存在一個數組中。

const cubes = [
    makeInstance(geometry, 0x44aa88, 0),
    makeInstance(geometry, 0x8844aa, -2),
    makeInstance(geometry, 0xaa8844, 2),
];

最後在渲染函數中,旋轉三個立方體。給每一個立方體設置了稍微不一樣的旋轉角度。

function render(time) {
    time *= 0.001;
    cubes.forEach((cube, ndx) => {
        const speed = 1 + ndx * .1;
        const rot = time * speed;
        cube.rotation.x = rot;
        cube.rotation.y = rot;
    });
    ...
}

完整的demo代碼

<canvas id="c"></canvas>
<script type="module">
    import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';

    function main() {
        const canvas = document.querySelector('#c');

        const renderer = new THREE.WebGLRenderer({
            canvas
        });

        const scene = new THREE.Scene();

        {
            const fov = 75;
            const aspect = 2;
            const near = 0.1;
            const far = 5;
            const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
            camera.position.z = 2;
        }
        
        {
            const color = 0xFFFFFF;
            const intensity = 1;
            const light = new THREE.DirectionalLight(color, intensity);
            light.position.set(-1, 2, 4);
        }

        scene.add(light);

        {
            const boxWidth = 1;
            const boxHeight = 1;
            const boxDepth = 1;
            const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
        }

        function makeInstance(geometry, color, x) {
            const material = new THREE.MeshPhongMaterial({
                color
            });
            const cube = new THREE.Mesh(geometry, material);
            scene.add(cube);
            cube.position.x = x;
            return cube;
        }

        const cubes = [
            makeInstance(geometry, 0x44aa88, 0),
            makeInstance(geometry, 0x8844aa, -2),
            makeInstance(geometry, 0xaa8844, 2),
        ];

        function render(time) {
            time *= 0.001;
            cubes.forEach((cube, ndx) => {
                const speed = 1 + ndx * .1;
                const rot = time * speed;
                cube.rotation.x = rot;
                cube.rotation.y = rot;
            });
            renderer.render(scene, camera);
            requestAnimationFrame(render);
        }
        requestAnimationFrame(render);
    }
    main();
</script>
相關文章
相關標籤/搜索