Viblo CTF
-2

Tìm hiểu WebGL Phần 4: Các ví dụ.

Lời Nói Đầu

Ở phần trước ta tìm hiểu về các bước để lập trình một ứng dụng WebGL và một ví dụ về vẽ 3 điểm bằng WebGL. Ở phần này ta sẽ chỉ đi vào các ví dụ cụ thể như vẽ tam giác, vẽ hình đa giác, vẽ mô hình 3D, tạo màu cho các mô hình...

Các ví dụ từ đơn giản

Như chúng ta đa biết quá trình 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.

Vẽ tam giác

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 và các chỉ số đỉ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, các chỉ số đỉnh và lưu vào vertex buffer.
        var vertices = [
            -0.5,0.5,0.0,
            0.0,0.5,0.0,
            -0.25,0.25,0.0, 
         ];
         
         var indices = [0, 1, 2];
         
         //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 index buffer để lưu trữ các chỉ số
         var Index_Buffer = gl.createBuffer();

         // Bind mảng buffer thích hợp cho buffer object
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);

         // Truyền dữ liệu cho index buffer object
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
         
         // Unbind the buffer
         gl.bindBuffer(gl.ELEMENT_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. Ngoài ra để các điểm có thể nhìn được rõ ta thiết lập kích thước cho điển thông qua biến gl_PointSize.
        // Mã nguồn được lưu dưới dạng một chuỗi vào một biến javascript
         var vertCode =
            'attribute vec3 coordinates;' +
				
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
               'gl_PointSize = 10.0;'+
            '}';
        // 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);
  • 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 drawElements() để vẽ tam giác do ta sử dụng các đỉnh cùng các chỉ số đỉ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.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

Ở trên từ 3 đỉnh và các chỉ số của đỉnh với dữ liệu vị trí được xử lý bởi vertex shader program và màu sắc được xử lý bởi fragment shader program, kết quả sẽ là:

Vẽ hình tứ giác

  • 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 tứ giác và các chỉ số đỉnh của tứ giác sau đó lưu trữ vào các buffer object. Do tứ giác được hình thành từ hai tam giác ghép lại nên về indices là mảng gồm 6 phần tử là chỉ số của đỉnh của hai tam giác ghép thành tứ giác. 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, các chỉ số đỉnh 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,
               0.5,0.5,0.0 
            ];

         indices = [3,2,1,3,1,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 index buffer để lưu trữ các chỉ số
         var Index_Buffer = gl.createBuffer();

         // Bind mảng buffer thích hợp cho buffer object
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);

         // Truyền dữ liệu cho index buffer object
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
         
         // Unbind the buffer
         gl.bindBuffer(gl.ELEMENT_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. Ngoài ra để các điểm có thể nhìn được rõ ta thiết lập kích thước cho điển thông qua biến gl_PointSize.
        // Mã nguồn được lưu dưới dạng một chuỗi vào một biến javascript
         var vertCode =
            'attribute vec3 coordinates;' +
				
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
               'gl_PointSize = 10.0;'+
            '}';
        // 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);
  • 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 drawElements() để vẽ tứ giác do ta vẽ tứ giác bằng việc ghép hai 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.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

Kết quả

Tạo màu sắc cho các mô hình đồ họa

Như ta đã biết shader program chịu trách nhiệm xử lý các tác vụ liên quan tới việc xứ lý dữ liệu hình học cho ra vị trị cần render của các mô hình đồ họa. Ngoài ra chúng còn liên quan tới các tác vụ về màu sắc, texture của các mô hình đồ họa. Do đó để tạo màu cho các mô hình đồ họa ta cần định nghĩa ở các shader program. Sau đây là ví dụ về tạo màu cho hình tứ giác ở ví dụ trên. Phần này chỉ override lại phần shader program còn các code về tạo khởi tạo canvas, khởi tạo webgl context, cùng dữ liệu mô hình và các bước còn lại tương tự code ở trên.

// mã nguồn vertex shader program, ta có khở tạo thêm môt biến vColor kiểu varying( sử dụng để share dữ liệu giữa các shader program).
         var vertCode = 'attribute vec3 coordinates;'+
            'attribute vec3 color;'+
            'varying vec3 vColor;'+
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
               'vColor = color;'+
            '}';
            
         // tạo đối tượng vertex shader, attach và biên dịch
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         
         gl.shaderSource(vertShader, vertCode);

         gl.compileShader(vertShader);
         
         // mã nguồn fragment shader
         var fragCode = 'precision mediump float;'+
            'varying vec3 vColor;'+
            'void main(void) {'+
               'gl_FragColor = vec4(vColor, 1.);'+
            '}';
         //   tạo đối tượng fragment shader, attach mã nguồn và biên dịch
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         gl.shaderSource(fragShader, fragCode);
         
         gl.compileShader(fragShader);

Kết quả khi thay shader program ở ví dụ vẽ tứ giác bằng shader program mới

Kết Luận

Bài viết vừa giới thiệu về các ví dụ đơn giản về vẽ hình tam giác, tứ giác và tạo màu sác, phần tới sẽ là các ví dụ phức tạo hơn liên quan tới chuyển động, phóng to thu nhỏ các mô hình...

Tài Liệu Tham Khảo

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


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.