1. 程式人生 > >原生webgl學習(六) WebGL寫簡單的漢字

原生webgl學習(六) WebGL寫簡單的漢字

本專欄所有文章示例程式碼均可在我的gitee碼雲上獲取,讀者可自行下載:https://gitee.com/babyogl/learnWebGL;本文demo程式碼在chapter-03下的draw-word-01.html,讀者可以自行下載檢視;

如果讀者還對本專欄的文章內容和程式設計方式還不是很瞭解,建議先對前面的文章進行學習:部落格專欄:webgl基礎學習。前面的幾節課,筆者為大家演示了WebGL如何繪製三角形和矩形,也說過繪製這些圖案當然不是無聊繪製著玩的,這節課我們就利用繪製多個三角形和矩形,然後來組成我們的一些簡單的漢字。下面我們先來看一張圖

這個“王”字(ps:會不會令你想起隔壁老王?哈哈) ,總共有四筆,我們可以用四個矩形來組成它,分別用16個頂點,以第一橫為例,要畫兩個三角形,畫圖時頂點的順序組合為:(v0,v1, v2),(v2,v1,v3),其實不只這種順序組合,讀者可以自行探索其他的組合。其他的筆畫也按照這個順序來,最終經過繪製後,就會有一個王字清晰地呈現在我們面前,像前面的文章一樣,給其加一個互動選單欄工具,用於控制其位置的平移、旋轉縮放。不同的是,這一次的旋轉,我們利用更加有逼格的形式實現。

著色器

來看一下著色器:

<script id="v-shader" type="x-shader/x-vertex">
        attribute vec2 a_position;
        uniform vec2 u_resolution;//解析度
        uniform vec2 u_translate;//平移
        uniform vec2 u_rotate;//旋轉
        uniform vec2 u_scale;//縮放
        varying vec4 v_color;
        void main()
        {
          vec2 sPosition = a_position * u_scale;
          vec2 rotatePosition = vec2(
              sPosition.x * u_rotate.y + sPosition.y * u_rotate.x,
              sPosition.y * u_rotate.y - sPosition.x * u_rotate.x);
          vec2 position = rotatePosition + u_translate;
          vec2 zeroToOne = position / u_resolution;
          vec2 zeroToTwo = zeroToOne * 2.0;
          vec2 clipSpace = zeroToTwo - 1.0;
          gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);//(1,-1)將座標原點設定為左上角
          v_color = vec4(gl_Position.xyz * 0.5, 0.6);
        }
</script>

<script id="f-shader" type="x-shader/x-vertex">
        #ifdef GL_ES
        precision mediump float;
        #endif
        varying vec4 v_color;
        void main()
        {
          gl_FragColor = v_color.rbga;
        }
</script>

初始化資料

初始化相關的資料,包括平移、旋轉、縮放、頂點位置、以及選單欄相關命令初始化:

 let translate = [100, 150];
 let rotate = [0, 1];
 let scale = [1, 1];
 //王
 let wang = [
     //第一橫
     0, 0,
     150, 0,
     0, 30,
     0, 30,
     150, 0,
     150, 30,
     //第二橫
     0, 80,
     150, 80,
     0, 110,
     0, 110,
     150, 80,
     150, 110,
     //第三橫
     0, 160,
     150, 160,
     0, 190,
     0, 190,
     150, 160,
     150, 190,
     //豎
     60, 30,
     60, 160,
     90, 30,
     90, 30,
     60, 160,
     90, 160
    ];
 let guiField = {
     '平移X': translate[0],
     '平移Y': translate[1],
     '縮放X': scale[0],
     '縮放Y': scale[1]
 };

 建立選單欄和建立繪製函式

function initGui(gl, program) {
        let gui = new dat.GUI();
        gui.add(guiField, '平移X', 0, gl.canvas.width-200).onChange(function(e) {
            translate[0] = e;
            drawScene(gl, program);
        });
        gui.add(guiField, '平移Y', 0, gl.canvas.height-200).onChange(function(e) {
            translate[1] = e;
            drawScene(gl, program);
        });
        
        gui.add(guiField, '縮放X', -4, 4).onChange(function (e) {
            scale[0] = e;
            drawScene(gl, program)
        });
        gui.add(guiField, '縮放Y', -4, 4).onChange(function (e) {
            scale[1] = e;
            drawScene(gl, program)
        });

        let ui = document.getElementById('ui-translate');
        ui.appendChild(gui.domElement);
 }


    //獲取著色器中的attribute變數的位置
    function getAttribute(gl, program, name) {
        return gl.getAttribLocation(program, name);
    }

    //獲取著色器中的uniform變數的位置
    function getUniform(gl, program, name) {
        return gl.getUniformLocation(program, name)
    }

    function drawScene(gl, program) {

        webglUtils.resizeCanvasToDisplaySize(gl.canvas);
        //告訴WebGL如何從剪輯空間轉換為畫素
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        //清除canvas
        gl.clear(gl.COLOR_BUFFER_BIT);

        //應用著色器程式
        gl.useProgram(program);

        //開啟頂點屬性
        gl.enableVertexAttribArray(program.pLocation);
        //繫結緩衝區
        gl.bindBuffer(gl.ARRAY_BUFFER, program.positionBuffer);

        setGeometry(gl, wang);

        //獲取緩衝區資料
        gl.vertexAttribPointer(program.pLocation, 2, gl.FLOAT, false, 0, 0);

        //設定解析度
        gl.uniform2f(program.rLocation, gl.canvas.width, gl.canvas.height);

        //旋轉
        gl.uniform2fv(program.roLocation, rotate);

        //設定平移
        gl.uniform2fv(program.tLocation, translate);
        
        //設定縮放
        gl.uniform2fv(program.sLocation, scale);

        gl.drawArrays(gl.TRIANGLES, 0, wang.length / 2);

    }

主函式

 function main() {
        let canvas = document.getElementById('translate');
        let gl = canvas.getContext('webgl', {antialias: true, depth: false})
        if (!gl) {
            alert("您的瀏覽器不支援WebGL!")
        }
        //獲取頂點和片段著色器文字
        let vShaderSource = document.getElementById('v-shader').text;
        let fShaderSource = document.getElementById('f-shader').text;

        //建立著色器程式
        let program = initShader(gl, vShaderSource, fShaderSource);
        program.pLocation = getAttribute(gl, program, 'a_position');
        program.rLocation = getUniform(gl, program, 'u_resolution');
        program.tLocation = getUniform(gl, program, 'u_translate');
        program.roLocation = getUniform(gl, program, 'u_rotate');
        program.sLocation = getUniform(gl, program, 'u_scale');
        //建立緩衝區
        program.positionBuffer = gl.createBuffer();
        //繫結緩衝區
        gl.bindBuffer(gl.ARRAY_BUFFER, program.positionBuffer);
        drawScene(gl, program);
        initGui(gl, program);

        //利用jQuery繪製圓控制圖形的旋轉
        $("#ui-rotate").gmanUnitCircle({
            width: 200,
            height: 200,
            value: 0,
            slide: function(e,u) {
                rotate[0] = u.x;
                rotate[1] = u.y;
                drawScene(gl, program);
            }
        });
  }

當然,除了“王”字,筆者也將構成“華”的頂點陣列post出來,筆者可以自行嘗試:

let hua = [
        45, 60,
        75, 80,
        0, 140,

        35, 120,
        65, 120,
        35, 240,
        35, 240,
        65, 120,
        65, 240,

        125, 80,
        155, 80,
        125, 240,
        125, 240,
        155, 80,
        155, 240,

        200, 80,
        220, 100,
        155, 140,

        135, 210,
        205, 210,
        135, 240,
        135, 240,
        205, 210,
        205, 240,

        0, 250,
        210, 250,
        0, 280,
        0, 280,
        210, 250,
        210, 280,

        85, 180,
        115, 180,
        85, 400,
        85, 400,
        115, 180,
        115, 400
    ];

程式實現效果

 

這種單個字的效果看起來可能不是很炫酷,下一節筆者將帶領大家一起繪製多個字,並控制它們的平移,旋轉,縮放。