3D世界

前言
最近在學(xué)習(xí)three.js,以下是我的筆記。

相關(guān)概念了解

1.WebGL與Three.js

1.1什么是WebGL

WebGL是基于OpenGL ES 2.0的Web標(biāo)準(zhǔn),可以通過(guò)HTML5 Canvas元素作為DOM接口訪問(wèn)。
聽起來(lái)挺像回事兒的,但是這是什么意思呢?
如果你了解OpenGL,那么我解釋起來(lái)就比較輕松了。WebGL可以看做是將OpenGL ES(OpenGL for Embedded Systems,OpenGL嵌入式版本,針對(duì)手機(jī)、游戲機(jī)等設(shè)備相對(duì)較輕量級(jí)的版本)移植到了網(wǎng)頁(yè)平臺(tái),像Chrome、Firefox這些現(xiàn)代瀏覽器都實(shí)現(xiàn)了WebGL標(biāo)準(zhǔn),使用JavaScript就可以用你熟悉的、類似OpenGL的代碼編寫了。
如果你不了解OpenGL,那也沒(méi)關(guān)系,因?yàn)檎鏣hree.js不需要你了解OpenGL或WebGL一樣,本書也不需要你預(yù)先知道這些知識(shí)。你可以把WebGL簡(jiǎn)單地認(rèn)為是一種網(wǎng)絡(luò)標(biāo)準(zhǔn),定義了一些較底層的圖形接口,至于究竟多底層,稍后我們和Three.js代碼對(duì)比來(lái)看。本書不會(huì)過(guò)多涉及WebGL的相關(guān)知識(shí),如果讀者想學(xué)習(xí)的話,市場(chǎng)上有不少相關(guān)書籍可供參考。
現(xiàn)在,我們知道了WebGL是一個(gè)底層的標(biāo)準(zhǔn),在這些標(biāo)準(zhǔn)被定義之后,Chrome、Firefox之類的瀏覽器實(shí)現(xiàn)了這些標(biāo)準(zhǔn)。然后,程序員就能通過(guò)JavaScript代碼,在網(wǎng)頁(yè)上實(shí)現(xiàn)三維圖形的渲染了。如果這對(duì)你來(lái)說(shuō)還是太抽象,別著急,稍后我們會(huì)用具體的例子來(lái)說(shuō)明。

1.2什么是WebGL

Three.js究竟能用來(lái)干什么呢?
Three.js封裝了底層的圖形接口,使得程序員能夠在無(wú)需掌握繁冗的圖形學(xué)知識(shí)的情況下,也能用簡(jiǎn)單的代碼實(shí)現(xiàn)三維場(chǎng)景的渲染。我們都知道,更高的封裝程度往往意味著靈活性的犧牲,但是Three.js在這方面做得很好。幾乎不會(huì)有WebGL支持而Three.js實(shí)現(xiàn)不了的情況,而且就算真的遇到這種情況,你還是能同時(shí)使用WebGL去實(shí)現(xiàn),而不會(huì)有沖突。當(dāng)然,除了WebGL之外,Three.js還提供了基于Canvas、SVG標(biāo)簽的渲染器,但由于通常WebGL能夠?qū)崿F(xiàn)更靈活的渲染效果,所以本書主要針對(duì)基于WebGL渲染器進(jìn)行說(shuō)明。

1.1.3 WebGL vs. Three.js

畫一個(gè)這個(gè)圖

image

three 寫法

var renderer = new THREE.WebGLRenderer({
    canvas: document.getElementById('mainCanvas')
});
renderer.setClearColor(0x000000); // black

var scene = new THREE.Scene();

var camera = new THREE.PerspectiveCamera(45, 4 / 3, 1, 1000);
camera.position.set(0, 0, 5);
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);

var material = new THREE.MeshBasicMaterial({
        color: 0xffffff // white
});
// plane
var planeGeo = new THREE.PlaneGeometry(1.5, 1.5);
var plane = new THREE.Mesh(planeGeo, material);
plane.position.x = 1;
scene.add(plane);

// triangle
var triGeo = new THREE.Geometry();
triGeo.vertices = [new THREE.Vector3(0, -0.8, 0),
        new THREE.Vector3(-2, -0.8, 0), new THREE.Vector3(-1, 0.8, 0)];
triGeo.faces.push(new THREE.Face3(0, 2, 1));
var triangle = new THREE.Mesh(triGeo, material);
scene.add(triangle);

renderer.render(scene, camera);

WebGL代碼

var gl;
function initGL(canvas) {
    try {
        gl = canvas.getContext("experimental-webgl");
        gl.viewportWidth = canvas.width;
        gl.viewportHeight = canvas.height;
    } catch (e) {
    }
    if (!gl) {
        alert("Could not initialise WebGL, sorry :-(");
    }
}

function getShader(gl, id) {
    var shaderScript = document.getElementById(id);
    if (!shaderScript) {
        return null;
    }

    var str = "";
    var k = shaderScript.firstChild;
    while (k) {
        if (k.nodeType == 3) {
            str += k.textContent;
        }
        k = k.nextSibling;
    }

    var shader;
    if (shaderScript.type == "x-shader/x-fragment") {
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
        shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
        return null;
    }

    gl.shaderSource(shader, str);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert(gl.getShaderInfoLog(shader));
        return null;
    }

    return shader;
}

var shaderProgram;

function initShaders() {
    var fragmentShader = getShader(gl, "shader-fs");
    var vertexShader = getShader(gl, "shader-vs");

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert("Could not initialise shaders");
    }

    gl.useProgram(shaderProgram);

    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
}

var mvMatrix = mat4.create();
var pMatrix = mat4.create();

function setMatrixUniforms() {
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
    gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}

var triangleVertexPositionBuffer;
var squareVertexPositionBuffer;

function initBuffers() {
    triangleVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
    var vertices = [
         0.0,  1.0,  0.0,
        -1.0, -1.0,  0.0,
         1.0, -1.0,  0.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    triangleVertexPositionBuffer.itemSize = 3;
    triangleVertexPositionBuffer.numItems = 3;

    squareVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    vertices = [
         1.0,  1.0,  0.0,
        -1.0,  1.0,  0.0,
         1.0, -1.0,  0.0,
        -1.0, -1.0,  0.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    squareVertexPositionBuffer.itemSize = 3;
    squareVertexPositionBuffer.numItems = 4;
}

function drawScene() {
    gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);

    mat4.identity(mvMatrix);

    mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]);
    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);

    mat4.translate(mvMatrix, [3.0, 0.0, 0.0]);
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
}

function webGLStart() {
    var canvas = document.getElementById("lesson01-canvas");
    initGL(canvas);
    initShaders();
    initBuffers();

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.enable(gl.DEPTH_TEST);

    drawScene();
}

總結(jié):從上面的代碼我們不難發(fā)現(xiàn),使用原生WebGL接口實(shí)現(xiàn)同樣功能需要5倍多的代碼量,而且很多代碼對(duì)于沒(méi)有圖形學(xué)基礎(chǔ)的程序員是很難看懂的。由這個(gè)例子我們可以看出,使用Three.js開發(fā)要比WebGL更快更高效。尤其對(duì)圖形學(xué)知識(shí)不熟悉的程序員而言,使用Three.js能夠降低學(xué)習(xí)成本,提高三維圖形程序開發(fā)的效率

參考:
three.js入門指南

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容