WebGL – How it works? – WebGL绘制图形



WebGL – How it works? – WebGL绘制图形

0 0


time33-webgl


On Github echoyoung46 / time33-webgl

WebGL

Created by @yangwt

What?

WebGL是一项用来在网页上绘制和渲染复杂三维图形(3D图形),并允许用户与之进行交互的技术。

WebGL 本质上是基于光栅化的 API ,而不是基于 3D 的 API。

光栅化

把物体的数学描述以及与物体相关的颜色信息转换为屏幕上用于对应位置的像素及用于填充像素的颜色,这个过程称为光栅化。

优势

  • 只需在HTML和JavaScript中添加三维图形学的代码
  • WebGL是内嵌在浏览器中,不必安装插件和库就可以使用
  • 基于浏览器,可以在多平台运行
  • 开发简单,不用搭建开发环境
  • 利用图形硬件来加速渲染

How it works?

着色器

WebGL只关注两件事——坐标和颜色。

使用WebGL程序的任务就是实现具有投影矩阵坐标和颜色的WebGL对象即可。

定义两个着色器———顶点着色器和片元着色器。顶点着色器提供坐标,片元着色器提供颜色

tips: 无论画板尺寸多大,投影矩阵坐标的范围只会在-1到1之间

<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

void main() {
gl_Position = vec4(a_position, 0, 1);
}
</script>

<script id="2d-fragment-shader" type="x-shader/x-fragment">
void main() {
gl_FragColor = vec4(0, 1, 0, 1); // green
}
</script>

着色器是用GLSL写的方法。处理每个顶点时都要引用一次,将定义的3D坐标点转化为屏幕可以显示出来的2D顶点,即投影矩阵的坐标。

用户可以将投影矩阵的值存储在特定的变量gl_Position中。GPU会处理这些值,并将它们存储在内部。

片元着色器中gl_fragColor设置颜色,描绘图形时,每个px都会通过片元着色器得到该px的颜色。

  • 产生三个顶点
  • GPU会使用它们来绘制三角形
  • 将三角形光栅化,或者说是使用像素点绘制出来
  • 对每一个像素点,GPU会调用片元着色器来确定该像素的颜色

WebGL API

WebGL context

var canvas = document.getElementById("canvas");
var gl = getWebGLContext(canvas);

变量类型

const 常量

attribute 顶点着色器定义的全局变量,存储可变的顶点坐标值

uniform 着色器定义的只读变量

varing 定义在顶点和片元着色器之间传递的变量,在顶点着色器定义,片元着色器中只读

创建program

webgl-utils.js

createProgramFromScript(gl, ["2d-vertex-shader", "2d-fragment-shader"]); 创建一个GLSL Program

查找数据

gl.getAttribLocation(program, "a_position");

gl.getUniformLocation(program, "a_position");

设置分辨率

gl.uniform2f(location, canvas.width, canvas.height); 设置分辨率

gl.uniform4f(colorLocation, Math.random(), Math.random(), Math.random(), 1); 设置随机颜色

缓冲区

gl.createBuffer(); 创建缓冲区

gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 绑定缓冲区为当前操作对象

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([]), gl.STATIC_DRAW); 将内存中的顶点数组复制到当前操作的数据缓冲区中

gl.enableVertexAttribArray(anyVertexAttributeLocation); 开启数组模式

gl.vertexAttribPointer(index, size, type, normalized, stride, offset); 把当前工作的数据缓冲区指定给数组

画图

gl.clear(gl.COLOR_BUFFER_BIT); 清空canvas

gl.uniformMatrix(matrixLocation, false, matrix); 创建矩阵

gl.drawArray(gl.TRIANGLE, 0, 3); 意味着有3个顶点要处理

WebGL绘制图形

2D

定义坐标点

new Float32Array([
    // left column
    0,  0,
    30, 0,
    0,  150,
    0,  150,
    30, 0,
    30, 150,

    // top rung
    30,  0,
    100, 0,
    30,  30,
    30,  30,
    100, 0,
    100, 30,

    // middle rung
    30, 60,
    67, 60,
    30, 90,
    30, 90,
    67, 60,
    67, 90])

绘制方法

// Set the matrix.
gl.uniformMatrix3fv(matrixLocation, false, matrix);

// Draw the geometry.
gl.drawArrays(gl.TRIANGLES, 0, 18);

定义着色器

<script id="2d-vertex-shader" type="x-shader/x-vertex">
    attribute vec2 a_position;

    uniform mat3 u_matrix;

    void main() {
    gl_Position = u_matrix * a_position;
    }
</script>

进入3D世界

计算机的3D世界由点组成,两个不重合的点组成一条直线,三个不在一条直线上的点就能组成一个三角形面,无数三角形面就能够组成各种形状的物体。

 

3D与2D最显著区别就是,3D具有深度,也就是Z轴(垂直屏幕方向)

3D

定义3D坐标

new Float32Array([
    // left column front
    0,  0,   0,
    30, 0,   0,
    0,  150, 0,
    0,  150, 0,
    30, 0,   0,
    30, 150, 0,
    
    ...(此处省略13个顶点坐标)
    
    // bottom
    0,   150,   0,
    0,   150,  30,
    30,  150,  30,
    0,   150,   0,
    30,  150,  30,
    30,  150,   0,

    // left side
    0,   0,   0,
    0,   0,  30,
    0, 150,  30,
    0,   0,   0,
    0, 150,  30,
    0, 150,   0]),

绘制3D图形

// Set the matrix.
gl.uniformMatrix4fv(matrixLocation, false, matrix);

// Draw the geometry.
gl.drawArrays(gl.TRIANGLES, 0, 16*6);

定义着色器

<script id="3d-vertex-shader" type="x-shader/x-vertex">
    attribute vec4 a_position;
    
    uniform mat4 u_matrix;
    
    void main() {
    // Multiply the position by the matrix.
    gl_Position = u_matrix * a_position;
    }
</script>

顶点顺序

WebGL有前面和后面的概念 (看得见的为前面)。

前面的顶点坐标以顺时针的顺序定义

后面的顶点坐标以逆时针的顺序定义

Not

1,   2,   3,
 40,  50,  60,
700, 800, 900,

But

1,   2,   3,
700, 800, 900,
 40,  50,  60,

WebGL其他因素

  • 动画
  • 纹理
  • 透视
  • 镜头

动画

scale of 2, 1, rotation of 30 degrees, and translation of 100, 0

translation of 100,0, rotation of 30 degrees and scale of 2, 1

Matrix

2D 3x3 Matrix

Translate

Rotation

s = Math.sin(angleToRotateInRadians)

c = Math.cos(angleToRotateInRadians)

Scale

纹理

将一张图像映射(贴)到一个集合图形的表面,这张图片称为纹理图像或纹理。

纹理映射的作用,就是根据纹理图像,为之前光栅化后的每个片元涂上合适的颜色。组成纹理图像的像素又被称为纹素。

纹理映射步骤

准备好映射到几何图形上的纹理图像(不能跨域)

为几何图形配置纹理映射方式

加载纹理图像,对其进行一些配置,以在WebGL中使用它。

在片元着色器中将相应的纹素从纹理中抽取出来,并将纹素的颜色赋给片元。

顶点着色器定义纹理坐标

attribute vec4 a_position;
attribute vec2 a_texcoord;
 
uniform mat4 u_matrix;
 
varying vec2 v_texcoord;
 
void main() {
  // Multiply the position by the matrix.
  gl_Position = u_matrix * a_position;
 
  // Pass the texcoord to the fragment shader.
  v_texcoord = a_texcoord;
}

sampler2D引用纹理,texture2D得到颜色

precision mediump float;
 
// Passed in from the vertex shader.
varying vec2 v_texcoord;
 
// The texture.
uniform sampler2D u_texture;
 
void main() {
   gl_FragColor = texture2D(u_texture, v_texcoord);
}

设置纹理坐标

var positionLocation = gl.getAttribLocation(program, "a_position");
var texcoordLocation = gl.getAttribLocation(program, "a_texcoords");
 
...
 
// Create a buffer for texcoords.
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(texcoordLocation);
// We'll supply texcoords as floats.
gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);
// Set Texcoords.
setTexcoords(gl);
function setTexcoords(gl) {
  gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([
        // left column front
        0, 0,
        0, 1,
        1, 0,
        0, 1,
        1, 1,
        1, 0,
        // top rung front
        0, 0,
        0, 1,
        1, 0,
        0, 1,
        1, 1,
        1, 0,
 ...

引入图片

var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Fill the texture with a 1x1 blue pixel.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
              new Uint8Array([0, 0, 255, 255]));
// Asynchronously load an image
var image = new Image();
image.src = "resources/f-texture.png";
image.addEventLisenter('load', function() {
  // Now that the image has loaded make copy it to the texture.
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,gl.UNSIGNED_BYTE, image);
  gl.generateMips(gl.TEXTURE_2D);
});

透视

即近大远小的特征

实现:将坐标X和Y除以Z

<script id="2d-vertex-shader" type="x-shader/x-vertex">
...
uniform float u_fudgeFactor;
...
void main() {
  // Multiply the position by the matrix.
  vec4 position = u_matrix * a_position;

  // Adjust the z to divide by
  float zToDivideBy = 1.0 + position.z * u_fudgeFactor;

  // Divide x and y by z.
  gl_Position = vec4(position.xy / zToDivideBy, position.zw);
}
</script>

clipspace 中 Z 从 -1 到 +1,加 1 之后 zToDivideBy 从 0 到 +2 * fudgeFactor

镜头

移动镜头

移动物体

镜头移到原点

画一圈F

<script id="2d-vertex-shader" type="x-shader/x-vertex">
var numFs = 5;
  var radius = 200;

  // Compute the projection matrix
  var aspect = canvas.clientWidth / canvas.clientHeight;
  var projectionMatrix =
      makePerspective(fieldOfViewRadians, aspect, 1, 2000);

  // Draw 'F's in a circle
  for (var ii = 0; ii < numFs; ++ii) {
    var angle = ii * Math.PI * 2 / numFs;

    var x = Math.cos(angle) * radius;
    var z = Math.sin(angle) * radius;
    var translationMatrix = makeTranslation(x, 0, z);

    // Multiply the matrices.
    var matrix = translationMatrix;
    matrix = matrixMultiply(matrix, projectionMatrix);

    // Set the matrix.
    gl.uniformMatrix4fv(matrixLocation, false, matrix);

    // Draw the geometry.
    gl.drawArrays(gl.TRIANGLES, 0, 16 * 6);
  }
</script>

画镜头

var cameraMatrix = makeTranslation(0, 0, radius * 1.5);
  cameraMatrix = matrixMultiply(cameraMatrix, makeYRotation(cameraAngleRadians);

根据镜头矩阵计算视图矩阵

var viewMatrix = makeInverse(cameraMatrix);

应用视图矩阵来计算每个F的矩阵

var matrix = translationMatrix;
    matrix = matrixMultiply(matrix, viewMatrix);  // <=-- added
    matrix = matrixMultiply(matrix, projectionMatrix);

福利

miao~

Recommend Link

webglfundamentals.org

www.tutorailspoint.com/webgl/

learningwebgl.com/blog/

www.html5china.com/HTML5features/WebGL/

THE END

Thank you.

WebGL Created by @yangwt