和得物技術一塊兒走進Web3D的世界(1) 畫個立方體吧

須要瞭解一下的前置知識(下面是推薦閱讀的連接)html

GLSL github.com/wshxbqq/GLS…git

Shader thebookofshaders.com/01/github

矩陣 bk.tw.lvfukeji.com/wiki/%E7%9F…web

齊次座標 bk.tw.lvfukeji.com/wiki/%E9%BD…canvas

最終效果

step1:創建webgl渲染上下文

這個就是簡單的獲取dom而後獲取上下文 (注意下這裏由於是畫3d因此要開啓深度檢測)api

const canvasDom = document.getElementById('canvas')
gl = canvasDom.getContext('webgl')

//開啓深度檢測
gl.enable(gl.DEPTH_TEST)

step2:建立頂點着色器與片元着色器

關於着色器shader是一個超級大的話題(推薦看TheBooksOfShader,尷尬的是做者沒寫完)。dom

大體能夠這麼理解:webgl

• 頂點着色器肯定了畫布上點的位置spa

• 3d世界中基礎的幾何圖形是三角形,片元着色器表明了區域的表現形式3d

先看一下webgl的座標系,z+軸是面向咱們的視角:

截屏2021-02-10 上午11.07.24.png

下面這段是頂點着色器:

const Vertex = `
        attribute vec3 vPosition;
        void main() { 
            gl_PointSize = 1.0; 
            gl_Position =  mat4(1,0, 0, 0 ,0, 0.52, -0.85, 0, 0, 0.85, 0.52, 0, 0, 0, 0,1)*mat4(0.52,0, 0.85, 0 ,0, 1, 0, 0, -0.85, 0, 0.52, 0, 0, 0, 0,1)*vec4(vPosition, 1);
        }
    `

attribute : 只能存在於vertex shader中,通常用於保存頂點或法線數據,它能夠在數據緩衝區中讀取數據。

vec3 vPosition 定義了一個3維向量
由於3d空間一個點(x,y,z)

mat4(1,0, 0, 0 ,0, 0.52, -0.85, 0, 0, 0.85, 0.52, 0, 0, 0, 0,1)是一個齊次矩陣 表示繞y軸旋轉45度

mat4(0.52,0, 0.85, 0 ,0, 1, 0, 0, -0.85, 0, 0.52, 0, 0, 0, 0,1)表示繞z軸旋轉45度

這樣咱們才能看到3d的效果。

下面這個就是編譯shader,固定套路記住就行了 (開發中不大會用原生手寫webgl)

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, Vertex);
    gl.compileShader(vertexShader);

下面這段是片元着色器:

const Fragment = `
        #ifdef GL_ES
        precision highp float;
        #endif
        void main() {
            gl_FragColor = vec4(1.0,0,0,1.0);
        }
    `

表示的意思是畫布上的顏色是紅色 vec4(1.0,0,0,1.0) 而後也是固定套路:

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, Fragment);
    gl.compileShader(fragmentShader);

step3:建立一個程序

記住就好,就是調用api

const program = gl.createProgram();

step4:連接程序與着色器

記住就好,就是調用api

gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);

step5:創建緩衝數據

cube是用來獲取頂點座標

剩下的都是套路,api就不展開了,能夠去mdn上查閱

//cube是用來獲取頂點座標
function cube(size = 1.0) {
        const h = 0.5 * size;
        const vertices = [
            [-h, -h, -h],
            [-h, h, -h],
            [h, h, -h],
            [h, -h, -h],
            [-h, -h, h],
            [-h, h, h],
            [h, h, h],
            [h, -h, h],
        ];
        const positions = [];
        function quad(a, b, c, d, e, f, g, h) {
            [a, b, c, d, e, f, g, h].forEach((i) => {
                positions.push(vertices[i]);
            });
        }
        quad(0, 1, 1, 2, 2, 3, 3, 0);
        quad(4, 5, 5, 6, 6, 7, 7, 4);
        quad(1, 2, 2, 6, 6, 5, 5, 1);
        quad(0, 3, 3, 7, 7, 4, 4, 0);
        quad(0, 1, 1, 5, 5, 4, 4, 0);
        quad(3, 7, 7, 6, 6, 2, 2, 3);
        return { positions};
    }
    const geometry = cube(1.0);
    console.log(geometry)
    const points = new Float32Array(geometry.positions.flat());
    const bufferId = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
    gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

step6渲染

const Position = gl.getAttribLocation(program, 'vPosition');//獲取頂點着色器中的position變量的地址
gl.vertexAttribPointer(Position, 3, gl.FLOAT, false, 0, 0);//給變量設置長度和類型
gl.enableVertexAttribArray(Position);//激活這個變量

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.drawArrays(gl.LINES, 0, 48)

gl.drawArrays(gl.LINES, 0, 48)就是渲染的api

webgl中有7種圖元 (表示怎麼畫)

gl.POINTS 孤立點 繪製一系列點

gl.LINES 繪製一系列單獨線段。每兩個點做爲端點,線段之間不鏈接

gl.LINE_STRIP 連續線段 繪製一個線條。即,繪製一系列線段,上一點鏈接下一點

gl.LINE_LOOP 連續線圈 繪製一個線圈。即,繪製一系列線段,上一點鏈接下一點,而且最後一點與第一個點相連

gl.TRIANGLES 孤立三角形

gl.TRIANGLE_STRIP 三角帶

gl.TRIANGLE_FAN 三角扇

0,48表示從0取48個點繪製

總結

上述過程就是一個完整的webgl繪畫流程。

完整的代碼

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D立方體</title>
</head>

<body>
    <canvas id='canvas' width="800" height="800"></canvas>
</body>
<script>

    // 第一步 建立webgl上下文
    const canvasDom = document.getElementById('canvas')
    gl = canvasDom.getContext('webgl')

    //開啓深度檢測
    gl.enable(gl.DEPTH_TEST)
    console.log(gl)

    // 第二步 建立頂點着色器與片元着色器
    const Vertex = `
        attribute vec3 vPosition;
        void main() { 
            gl_PointSize = 1.0; 
            gl_Position =  mat4(1,0, 0, 0 ,0, 0.52, -0.85, 0, 0, 0.85, 0.52, 0, 0, 0, 0,1)*mat4(0.52,0, 0.85, 0 ,0, 1, 0, 0, -0.85, 0, 0.52, 0, 0, 0, 0,1)*vec4(vPosition, 1);
        }
    `

    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, Vertex);
    gl.compileShader(vertexShader);

    const Fragment = `
        #ifdef GL_ES
        precision highp float;
        #endif
        void main() {
            gl_FragColor = vec4(1.0,0,0,1.0);
        }
    `

    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, Fragment);
    gl.compileShader(fragmentShader);

    //第三步 建立程序對象
    const program = gl.createProgram();

    // 第四步 連接程序與着色器
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);

    gl.linkProgram(program);
    gl.useProgram(program);

    //第五步 創建緩衝數據
    function cube(size = 1.0) {
        const h = 0.5 * size;
        const vertices = [
            [-h, -h, -h],
            [-h, h, -h],
            [h, h, -h],
            [h, -h, -h],
            [-h, -h, h],
            [-h, h, h],
            [h, h, h],
            [h, -h, h],
        ];

        const positions = [];
        function quad(a, b, c, d, e, f, g, h) {
            [a, b, c, d, e, f, g, h].forEach((i) => {
                positions.push(vertices[i]);
            });
        }

        quad(0, 1, 1, 2, 2, 3, 3, 0);
        quad(4, 5, 5, 6, 6, 7, 7, 4);
        quad(1, 2, 2, 6, 6, 5, 5, 1);
        quad(0, 3, 3, 7, 7, 4, 4, 0);
        quad(0, 1, 1, 5, 5, 4, 4, 0);
        quad(3, 7, 7, 6, 6, 2, 2, 3);

        return { positions};
    }

    const geometry = cube(1.0);

    console.log(geometry)

    const points = new Float32Array(geometry.positions.flat());
    const bufferId = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
    gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

    // 第六步 渲染
    const Position = gl.getAttribLocation(program, 'vPosition');//獲取頂點着色器中的position變量的地址
    gl.vertexAttribPointer(Position, 3, gl.FLOAT, false, 0, 0);//給變量設置長度和類型
    gl.enableVertexAttribArray(Position);//激活這個變量

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
    gl.drawArrays(gl.LINES, 0, 48)
</script>

</html>

文|alex 關注得物技術,攜手走向技術的雲端

相關文章
相關標籤/搜索