Tìm hiểu WebGL Phần 5: Các ví dụ về chuyển đổi vị trí, tương tác, phóng to thu nhỏ

Lời Nói Đầu

Tiếp nối các ví dụ về WebGL ở phần này ta sẽ đi vào các ví dụ về chuyển động, tương tác với các mô hình đồ họa của WebGL.

Các ví dụ nâng cao

Nhắc lại các bước để tạo một ứng dụng WebGL bao gồm:

  • Chuẩn bị canvas và WebGL context.
  • Tạo dữ liệu hình học cho mô hình cần vẽ., tạo các buffer object để bind với các dữ liệu hình học.
  • Tạo các shader program để xử lý dữ liệu hình học về các mô hình và liên kết chúng lại với nhau.
  • Liên kết các shader program với các buffer object.
  • Sử dụng hàm để vẽ và render các mô hình từ dữ liệu hình học.

Dịch chuyển vị trí cho tam giác

Dịch chuyển (translation) là một trong những thao tác biến đổi mô hình đồ họa được cung cấp bởi WebGL. Sử dụng dịch chuyển cho phép chúng ta có thể di chuyển một đối tượng đồ họa trong không gian 3 chiều xyz. Ví dụ chúng ta có một tam giác với 3 đỉnh [a, b, c] và muốn di chuyển nó n đơn vị theo chuyền dương trục x và m đơn vị theo chiều dương truc y thì các đỉnh mới của tam giác đó sẽ là [a+n, b+m, c+0]. có nghĩa để dịch chuyển tam giác ta cần thêm một lượng khoảng cách cho mỗi đỉnh của tam giác dx, dy, dz. Và vertex shader program được sử dụng để xử lý tác vụ này. Ở vertex shader program ngoài các biến attributes, corrdinates (lưu trữ vị trí các đỉnh) ta định nghĩa thêm biến uniform để lưu trữ khoảng cách biến đổi (x,y,z). Sau đó chúng ta cộng biến uniform này vào các biến corrdinates sau đó gán cho biến gl_Position

Ta sẽ thực hiện theo các bước đã được giới thiệu để làm một ứng dụng WebGL:

  • Chuẩn bị canvas và WebGL context.
        <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

        <script>
             var canvas = document.getElementById('my_Canvas');
             gl = canvas.getContext('experimental-webgl'); 
         </script>
  • Tạo dữ liệu hình học cho đỉnh của tam giác sau đó lưu trữ vào các buffer object. Các điểm như là các vertex ta có thể định nghĩa nó trong tọa độ 3D hoặc 2D. Dưới đây là định nghĩa các đỉnh trong tọa độ 3D và lưu vào vertex buffer.
        var vertices = [
            -0.5,0.5,0.0, 	
            -0.5,-0.5,0.0, 	
            0.5,-0.5,0.0,   
         ];
         
         //Tạo vertex buffer object để lưu trữ các đỉnh
         var vertex_buffer = gl.createBuffer();

         //Bind mảng buffer thích hợp cho buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
      
         // Truyền dữ liệu cho vertex buffer object
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, null);
  • Tạo và biên dịch các shader program.
    • Để xử lý dữ liệu về vị trí của các đỉnh (vertex) ta định nghĩa vertex shader program. Trong vertex shader program ta định một vector attribute để lưu trữ tạo độ 3D và gán nó cho biến gl_Position.
        // Mã nguồn được lưu dưới dạng một chuỗi vào một biến javascript
         var vertCode =
           'attribute vec4 coordinates;' + 
            'uniform vec4 translation;'+
            'void main(void) {' +
               '  gl_Position = coordinates + translation;' +
            '}';
        // Tạo vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);

         // Gán mã nguồn cho đối tượng vertex shader mới được tạo
         gl.shaderSource(vertShader, vertCode);

         // Biên dịch vertex shader object
         gl.compileShader(vertShader);
  • Để xử lý màu sắc cho các điểm ta định nghĩa fragment shader program. Trong fragment shader program ta thiết lập màu bằng việc gán giá trị cho biến gl_FragColor.
     // Mã nguồn fragment shader được lưu dưới dạng một chuỗi.
         var fragCode =
            'void main(void) {' +
               ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';
         
         // Tạo fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Gán mã nguồn cho fragment shader object
         gl.shaderSource(fragShader, fragCode);
      
         // Biên dịch fragment shader object
         gl.compileShader(fragShader);
  • Sau khi tạo và biên dịch các shader program cần thiết ta sẽ liên kết chúng lại.
         // Tạo shader program object để phối hợp 2 shader đã tạo ở trên
         var shaderProgram = gl.createProgram();

         // Gán vertex shader cho shader program
         gl.attachShader(shaderProgram, vertShader); 
 
         // Gán fragment shader cho shader program
         gl.attachShader(shaderProgram, fragShader);

         // Liên kết hai program với nhau
         gl.linkProgram(shaderProgram);

         // Sử dụng phối hợp vertex và shader program
         gl.useProgram(shaderProgram);
  • Thực hiện chuyển đổi vị trí của tam giác
        // Chuẩn bị khoảng cần dịch chuyển tam giác
        var Tx = 0.5, Ty = 0.5, Tz = 0.0;
        // lấy biến traslation của vertext shader program 
        var translation = gl.getUniformLocation(shaderProgram, 'translation');
        // thực hiện gán giá trị cho biến translation 
        gl.uniform4f(translation, Tx, Ty, Tz, 0.0);
  • Liên kết các shader program với các buffer object.
      // Bind vertex buffer object với mảng buffer thích hợp
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Lấy vị trí của attribute của vertex shader
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         // Trỏ attribute tới vertex buffer objet ở trên.
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);

         // Sử dụng attribute
         gl.enableVertexAttribArray(coord);
  • Vẽ hình tam giác Ở đây ta sẽ sử dụng drawArrays() để vẽ tam giác do ta sử dụng các đỉnh để định nghĩa tam giác.
         gl.clearColor(0.5, 0.5, 0.5, 0.9);
         gl.enable(gl.DEPTH_TEST);
         gl.clear(gl.COLOR_BUFFER_BIT);
         gl.viewport(0,0,canvas.width,canvas.height);

         // vẽ tam giác
         gl.drawArrays(gl.TRIANGLES, 0, 3);

Ở trên từ 3 đỉnh của tam giác ban đầu cùng với lượng cộng thêm để dich chuyển tam giác, kết quả sẽ là tam giác được dịch chuyển lên góc của canvas:

Phóng to thu nhỏ tam giác (scaling)

Phóng to thu nhỏ các đối tượng mô hình đồ họa được thực hiện bằng cách nhân chỉ số các đỉnh của mô hình với một tỷ lệ nào đó. Ví dụ nếu một tam giác có các đỉnh là [a, b, c] thì tam giác có đỉnh là [2a, 2b, 2c] sẽ có kích thước gấp đôi tam giác đầu. Để thực hiện công việc trên ta tạo ra một ma trận uniform và nhân ma trận đó với tọa độ của các đỉnh.

  • Chuẩn bị canvas và WebGL context.
        <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

        <script>
             var canvas = document.getElementById('my_Canvas');
             gl = canvas.getContext('experimental-webgl'); 
         </script>
  • Tạo dữ liệu hình học cho đỉnh của tam giác sau đó lưu trữ vào các buffer object. Dưới đây là định nghĩa các đỉnh trong tọa độ 3D và lưu vào vertex buffer.
            var vertices =  [
            -0.5,0.5,0.0, 	
            -0.5,-0.5,0.0, 	
            0.5,-0.5,0.0,   
         ];
         
         //Tạo vertex buffer object để lưu trữ các đỉnh
         var vertex_buffer = gl.createBuffer();

         //Bind mảng buffer thích hợp cho buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
      
         // Truyền dữ liệu cho vertex buffer object
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, null);
         
  • Tạo và biên dịch các shader program.
    • Để xử lý dữ liệu về vị trí của các đỉnh (vertex) ta định nghĩa vertex shader program. Trong vertex shader program ta định một vector attribute để lưu trữ tạo độ 3D và gán nó cho biến gl_Position.
        // Mã nguồn được lưu dưới dạng một chuỗi vào một biến javascript
         var vertCode =
            'attribute vec4 coordinates;' + 
            'uniform mat4 u_xformMatrix;' +
            'void main(void) {' +
               '  gl_Position = u_xformMatrix * coordinates;' +
            '}';

        // Tạo vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);

         // Gán mã nguồn cho đối tượng vertex shader mới được tạo
         gl.shaderSource(vertShader, vertCode);

         // Biên dịch vertex shader object
         gl.compileShader(vertShader);
  • Để xử lý màu sắc cho các điểm ta định nghĩa fragment shader program. Trong fragment shader program ta thiết lập màu bằng việc gán giá trị cho biến glFragColor.
     // Mã nguồn fragment shader được lưu dưới dạng một chuỗi.
         var fragCode =
            'void main(void) {' +
               ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';
         
         // Tạo fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Gán mã nguồn cho fragment shader object
         gl.shaderSource(fragShader, fragCode);
      
         // Biên dịch fragment shader object
         gl.compileShader(fragShader);
  • Sau khi tạo và biên dịch các shader program cần thiết ta sẽ liên kết chúng lại.
         // Tạo shader program object để phối hợp 2 shader đã tạo ở trên
         var shaderProgram = gl.createProgram();

         // Gán vertex shader cho shader program
         gl.attachShader(shaderProgram, vertShader); 
 
         // Gán fragment shader cho shader program
         gl.attachShader(shaderProgram, fragShader);

         // Liên kết hai program với nhau
         gl.linkProgram(shaderProgram);

         // Sử dụng phối hợp vertex và shader program
         gl.useProgram(shaderProgram);
  • Chuẩn bị tỉ lệ và thực hiện scaling tam giác.
    // ở đây ta chuẩn bị tỷ lệ là các dx, dy, dz.
    var dx = 1.0, dy = 1.5, dz = 1.0;
    // Tạo ma trận đường chéo gồm các tỷ lệ và phần tử cuối cùng của đường chéo là 1.
    var xformMatrix = new Float32Array([
            dx,   0.0,  0.0,  0.0,
            0.0,  dy,   0.0,  0.0,
            0.0,  0.0,  dz,   0.0,
            0.0,  0.0,  0.0,  1.0  
         ]);
         
    // lấy biến u_xformMatrix của vertex shader program
    var u_xformMatrix = gl.getUniformLocation(shaderProgram, 'u_xformMatrix');
    
    // gán giá trị của ma trận đường chéo tạo ở trên cho biến u_xformMatrix.
    gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);
  • Liên kết các shader program với các buffer object.
      // Bind vertex buffer object với mảng buffer thích hợp
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Lấy vị trí của attribute của vertex shader
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         // Trỏ attribute tới vertex buffer objet ở trên.
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);

         // Sử dụng attribute
         gl.enableVertexAttribArray(coord);
  • Vẽ hình tứ giác bằng cách vẽ hai tam giác có chỉ số đỉnh được chỉ trong mảng indices Ở đây ta sẽ sử dụng drawArrays() để vẽ tam giác.
         gl.clearColor(0.5, 0.5, 0.5, 0.9);
         gl.enable(gl.DEPTH_TEST);
         gl.clear(gl.COLOR_BUFFER_BIT);
         gl.viewport(0,0,canvas.width,canvas.height);

         // vẽ tứ giác
         gl.drawArrays(gl.TRIANGLES, 0, 3);

Kết quả từ tam giác đều, nó sẽ được kéo theo trục y dài ra 1.5 lần:

Kết Luận

Bài viết vừa giới thiệu về các ví dụ về biến đổi mô hình đồ họa như dịch chuyển nó và scaling kích thước của mô hình.

Tài Liệu Tham Khảo

https://www.tutorialspoint.com/webgl/index.htm.


All Rights Reserved