Skip to content

WebGL ​

重要通知

。

webGL基本概况 ​

WebGL(Web 图形库)是一个 JavaScript API,可在任何兼容的 Web 浏览器中渲染高性能的交互式 3D 和 2D 图形,而无需使用插件。

const canvas = document.createElement('canvas');
const width = document.body.clientWidth;
const height = document.body.clientHeight;
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
document.body.append(canvas);

const gl = canvas.getContext("webgl");

webGL实现原理 ​

材质 Material ​

层次模型 Model ​

动画 Animate ​

平移 ​

缩放 ​

旋转 ​

光照 Light ​

光源类型 ​

平行光、环境光、点光源、聚光灯、

反射类型 ​

决定因素:入射光和物体表面的类型。 类型:漫反射、环境反射。

缓冲区对象 ​

颜色缓冲区 ​

gl.clearColor(red, green, blue, alpha); // 默认值:(0.0, 0.0, 0.0, 0.0)

深度缓冲区 ​

gl.clearDepth(depth); // 默认值:1.0

模板缓冲区 ​

gl.clearStencil(s); // 默认值:0

gl.getParameter(gl.COLOR_CLEAR_VALUE); gl.getParameter(gl.DEPTH_CLEAR_VALUE); gl.getParameter(gl.STENCIL_CLEAR_VALUE);

语法结构 ​

// 
gl.createBuffer();

// 
gl.bindBuffer();

// 将数据写入缓冲区对象, 开辟存储空间,向绑定在target目标上的缓冲区对象写入数据
gl.bufferData(
  target, // gl.ARRAY_BUFFER | gl.ELEMENT_ARRAY_BUFFER
  data, // 写入缓冲区的数据(类型化数组)
  usage // 表示程序将如何使用存储在缓冲区对象中的数据。gl.STATIC_DRAW:只会向缓冲区对象中写入一次数据,但需要绘制很多次 | gl.STREAM_DRAW:只会向缓冲区对象中写入一次数据,然后绘制若干次 | gl.DYNAMIC_DRAW:会向缓冲区对象中多次写入数据,并绘制很多次 
);

// 
gl.deleteBuffer();

// 
gl.isBuffer();

​

attribute与uniform变量 ​

gl.getAttribLocation(program, name); ​

const vsSource = `
  attribute vec4 a_Position;
  void main() {
    gl_Position = a_Position;
    gl_PointSize = 10.0;
  }
`;

// 获取attribute变量的存储位置
const a_Position = gl.getAttribLocation(program, 'a_Position');
if (a_Position < 0) {
  console.log('Failed to get the storage location of a_Position');
  return;
}

// 向attribute变量赋值
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0); // gl.vertexAttrib3fv(a_Position, new Float32Array([0.0, 0.0, 0.0]));

// 告诉显卡从当前绑定的缓冲区(bindBuffer()指定的缓冲区)中读取顶点数据。
// 将当前绑定到的缓冲区绑定到当前顶点缓冲区对象的通用顶点属性并指定其布局。
gl.vertexAttribPointer(
  location, // 指定待分配attribute变量的存储位置
  size, // 指定每个顶点属性的组成数量,必须是1,2,3或4。
  type, // 指定数组中每个元素的数据类型,gl.BYTE | gl.SHORT | gl.UNSIGNED_BYTE | gl.UNSIGNED_SHORT | gl.FLOAT | gl.HALF_FLOAT 
  normalized, // 当转换为浮点数时是否应该将整数数值归一化到特定的范围
  stride, // 一个GLsizei,以字节为单位指定连续顶点属性开始之间的偏移量(即数组中一行长度) 
  offset // 指定顶点属性数组中第一部分的字节偏移量,必须是类型的字节长度的倍数
);

// 开启attribute变量,使顶点着色器能够访问缓冲区的数据
gl.enableVertexAttribArray(location);

// 隐藏attribute变量
gl.disableVertexAttribArray(location);

gl.vertexAttrib3f同族函数 ​

gl.vertexAttrib1f(location, v0):; gl.vertexAttrib2f(location, v0, v1):; gl.vertexAttrib4f(location, v0, v1, v2, v3):;

gl.vertexAttrib3f同族函数的矢量版本 ​

gl.vertexAttrib1fv(location, new Float32Array([v0])):; gl.vertexAttrib2fv(location, new Float32Array([v0, v1])):; gl.vertexAttrib3fv(location, new Float32Array([v0, v1, v2])):; gl.vertexAttrib4fv(location, new Float32Array([v0, v1, v2, v3])):;

gl.getUniformLocation(program, name); ​

const fsSource = `
  uniform vec4 u_FragColor;

  void main() {
    gl_FragColor = u_FragColor;
  }
`;

// 获取uniform变量的存储位置
const u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
if (u_FragColor < 0) {
  console.log('Failed to get the storage location of u_FragColor');
  return;
}

// 向uniform变量赋值
gl.uniform4f(a_Position, 0.0, 0.0, 0.0);

gl.uniform4f同族函数 ​

gl.uniform1f(location, v0):; gl.uniform2f(location, v0, v1):; gl.uniform4f(location, v0, v1, v2, v3):;

gl.uniform3f同族函数的矢量版本 ​

gl.uniform1fv(location, new Float32Array([v0])):; gl.uniform2f(location, new Float32Array([v0, v1])):; gl.uniform3f(location, new Float32Array([v0, v1, v2])):; gl.uniform4fv(location, new Float32Array([v0, v1, v2, v3])):;

gl.uniformMatrix2fv(location, transpose, value); gl.uniformMatrix3fv(location, transpose, value); gl.uniformMatrix4fv(location, transpose, value); location:WebGLUniformLocation 对象包含了要修改的 uniform attribute位置. 位置使用 getUniformLocation() 获得. transpose:GLboolean指定是否转置矩阵。必须为 false. value:Float32Array 型或者是 GLfloat 序列值.假定值以列主要顺序提供.

gl.uniform[1234][fi][v]() 方法指定了uniform 变量的值 ​

void gl.uniform1f(location, v0); void gl.uniform1fv(location, value); void gl.uniform1i(location, v0); void gl.uniform1iv(location, value);

void gl.uniform2f(location, v0, v1); void gl.uniform2fv(location, value); void gl.uniform2i(location, v0, v1); void gl.uniform2iv(location, value);

void gl.uniform3f(location, v0, v1, v2); void gl.uniform3fv(location, value); void gl.uniform3i(location, v0, v1, v2); void gl.uniform3iv(location, value);

void gl.uniform4f(location, v0, v1, v2, v3); void gl.uniform4fv(location, value); void gl.uniform4i(location, v0, v1, v2, v3); void gl.uniform4iv(location, value);

字体 Font ​

音频 Audio ​

阴影 Shadow ​

相机 Camera ​

坐标转换 ​

模型矩阵(modelMatrix):将顶点从[局部坐标]系转换到[世界坐标]系中; 视图矩阵(viewMatrix):将顶点从[世界坐标]转化到[视图坐标]系下; 投影矩阵(projectionMatrix):将顶点从[视图坐标]系转换到规范立方体中(即[屏幕]中);

const VSHADER_SOURCE = `
  attribute vec4 a_Position;
  uniform mat4 u_ModelMatrix;
  uniform mat4 u_ViewMatrix;
  uniform mat4 u_ProjMatrix;

  void main() {
    gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
  }
`;

var modelMatrix = new Matrix4(); // 模型矩阵
var viewMatrix = new Matrix4();  // 视图矩阵
var projMatrix = new Matrix4();  // 投影矩阵

modelMatrix.setTranslate(0.75, 0, 0); 
viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);  

gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

视点(eyeX, eyeY, eyeZ) ​

观察者所在三维空间中的位置。

观察目标点(atX, atY, atZ) ​

被观察目标所在的点。

上方向(upX, upY, upZ):固定观察者视线的位置,否则观察者在保持视点与观察目标点不变的情况下,还是可以围绕视线旋转观察目标,此时观察者观察到的目标场景就会变化。 ​

最终绘制在屏幕上的影像中的向上的方向。如果不确定上方向,紧紧确定了视点与观察目标点,那么观察者以视线为轴旋转的(头部偏移,那么观察目标的场景也会偏移)

透视机制 ​

WebGL默认为按照缓冲区顺序绘制图形,后绘制的图形会覆盖先覆盖的图形。

正射投影 | Orthographic Projection ​

透视投影 | Perspective Projection ​

纹理 Texture ​

// 把 WebGLTexture 对象绑定到指定对象上。
gl.bindTexture();

// 创建 WebGLTexture 对象。
gl.createTexture();

// 删除 WebGLTexture 对象。
gl.deleteTexture();

// 返回 Boolean 值,表示给入的 WebGLTexture 是否有效。
gl.isTexture();

// 指定一个为压缩格式的2D纹理图片。
gl.compressedTexImage2D();

// 指定一个为压缩格式的2D纹理子图片。
gl.compressedTexSubImage2D();

// 复制2D纹理图片。
gl.copyTexImage2D(); 

// 复制2D纹理子图片。
gl.copyTexSubImage2D(); 

// 为 WebGLTexture 对象生成一组纹理映射。
gl.generateMipmap(); 

// 返回纹理信息。
gl.getTexParameter();

// 指定2D纹理图片。
gl.texImage2D();

// 更新当前 WebGLTexture 的子矩形。
gl.texSubImage2D(); 

// 设置纹理参数。
gl.texParameterf(); 

// 设置纹理参数。
gl.texParameteri();

图形 Geometry ​

图形装配 ​

光栅化 ​

显示在屏幕上的三角形是由片元(像素)组成的,所以还需要将图形转化为片元。

绘制操作 | 绘制缓冲区 ​

构造函数

gl.drawArrays(
  mode, // 绘制方式,具体详见绘制方式的图形API
  first, // [整型数] 指定从哪个顶点开始绘制
  count // [整型数] 指定绘制需要用到多少个顶点
);

gl.clear(); // 把指定的缓冲区清空为预设的值。

gl.drawArrays(); // 渲染数组中的原始数据。

gl.drawElements(); // 渲染元素数组中的原始数据。

gl.finish(); // 阻断执行,直到之前所有的操作都完成。

gl.flush(); // 清空缓冲的命令,这会导致所有命令尽快执行完。

绘制方式的图形API ​

--------------------------------------------------------------------
  参数	              基本图形	呈现效果
--------------------------------------------------------------------
  gl.POINTS	          点	     一系列的点绘制在v0,v1,v2等处。

  gl.LINES	          线段	   一系列单独线段。
  gl.LINE_STRIP	      连线	   一个相连的线段。
  gl.LINE_LOOP	      回路	   一个相连接的回路。
  
  gl.TRIANGLES	      三角形	 一系列单独的三角形。
  gl.TRIANGLE_STRIP	  三角带	 一个相邻三角形组成的图形。
  gl.TRIANGLE_FAN     三角扇。
--------------------------------------------------------------------

视频 Video ​

扩展插件 ​

gl.getSupportedExtensions(); // 返回一个包含 DOMString 的 Array ,每个元素都为支持的WebGL扩展。
gl.getExtension(); // 返回一个扩展对象。

着色器 shader ​

着色器是一个绘制东西到屏幕上的函数。

  • 顶点着色器

用来描述顶点特性(位置、颜色等)的程序。

内置变量与函数 ​

# --------------------------------------- #
# 内置变量
# --------------------------------------- #
# gl_PointSize:设置顶点渲染出来的正方形面的相素大小(默认值是0)。
> float类型,在点渲染模式中,顶点由于是一个点,理论上我们并无法看到,所以他是以一个正对着相机的正方形面表现的。

# gl_Position:表示顶点位置,必须赋值,否则着色器无法工作。gl_Position = vec4(0.0, 0.0, 0.0);
> vec4类型,它表示最终传入片元着色器片元化要使用的顶点位置坐标。vec4(x,y,z,1.0),前三个参数表示顶点的xyz坐标值,第四个参数是浮点数1.0。
> 片元着色器是针对顶点着色器输出的顶点数据 (gl_Position)进行逐点绘制。

# gl_FragColor:gl_FragColor.rgb | gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
> vec4类型,主要用来设置片元像素的颜色,它的前三个参数表示片元像素颜色值RGB,第四个参数是片元像素透明度A,1.0表示不透明,0.0表示完全透明。

# gl_FragCoord:
> vec2类型,它表示WebGL在canvas画布上渲染的所有片元或者说像素的坐标,坐标原点是canvas画布的左上角,x轴水平向右,y竖直向下,gl_FragCoord坐标的单位是像素,gl_FragCoord的值是vec2(x,y),通过gl_FragCoord.x、gl_FragCoord.y方式可以分别访问片元坐标的纵横坐标。

# gl_PointCoord:
> vec2类型,同样表示像素的坐标,但是与gl_FragCoord不同的是,gl_FragCoord是按照整个canvas算的x值从[0,宽度],y值是从[0,高度]。而gl_PointCoord是在点渲染模式中生效的,而它的范围是对应小正方形面,同样是左上角[0,0]到右下角[1,1]。

# 内建变量
> gl_VertexID:[只读] 正在绘制顶点的ID,一般是顶点的索引
> gl_FragCoord:[只读] 片段在窗口空间坐标,原点在左下角,x,y值介于glViewport设置的视口大小之间,z值为深度值
> gl_FrontFacing:[只读] 用于判断片段是属于正面还是反面
> gl_FragDepth:[可写] 可设置片段的深度值

gl_FrontFacing、、、、、、、、、、、、、、、、、、、、、、
<script>
  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
  gl_PointSize = 10.0;
</script>



# 常规变量
> uv:该顶点在UV映射时的横纵坐标
> normal:将attributes的normal
const VSHADER_SOURCE = ``;
  • 片元着色器

进行逐片元处理的程序,其中,片元指图像的单元。

const FSHADER_SOURCE = ``;
  • 初始化着色器
Eshen.initShaderProgram = function(vsSource, fsSource) {
  const vertexShader = Eshen.loadShader(gl.VERTEX_SHADER, vsSource);
  const fragmentShader = Eshen.loadShader(gl.FRAGMENT_SHADER, fsSource);

  if (!vertexShader || !fragmentShader) {
    throw new Error(`vertexShader or fragmentShader is null or undefined.`);
  }

  // 创建着色器程序
  const program = gl.createProgram();

  if (!program || !gl.isProgram(program)) {
    gl.deleteProgram(program);
    return null;
  }
  // 添加预先存在的着色器
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);

  gl.linkProgram(program);
  gl.validateProgram(program);

  // 是否编译成功
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.log('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
    gl.deleteProgram(program);
    gl.deleteShader(fragmentShader);
    gl.deleteShader(vertexShader);
    return null;
  }

  return program;
}
  • 加载着色器程序
Eshen.loadShader = function(type, source) {
  // 创建一个新的着色器
  const shader = gl.createShader(type); 

  if (!shader || !gl.isShader(shader)) {
    return null;
  }

  gl.shaderSource(shader, source); // 将源代码发送到着色器
  gl.compileShader(shader); // 编译一个GLSL着色器,使其成为为二进制数据

  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    // 异常时删除着色器代码,释放连接着色器程序的内存
    var error = gl.getShaderInfoLog(shader);
    console.log('Failed to compile shader: ' + error);
    gl.deleteShader(shader);
    return null;
  }
  return shader;
}
  • 初始化Buffer缓冲区
Eshen.initBuffer = function(vertices = new Float32Array([]), n, program) {
  const vertexBuffer = gl.createBuffer();
  if (!vertexBuffer) {
    console.log('Failed to create the buffer object');
    return false;
  }

  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); // 绑定缓冲区对象
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); // 将数据写入缓冲区对象

  // attribute变量
  const attributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
  const attrArray = new Array(attributes);

  for (let i = 0; i < attributes; ++i) {
    const info = gl.getActiveAttrib(program, i);
    attrArray[i] = gl.getAttribLocation(program, info.name);

    if (attrArray[i] < 0) {
      console.log(`Failed to get the storage location of ${info.name}`);
      continue;
    } 
    gl.vertexAttribPointer(attrArray[i], n, gl.FLOAT, false, 0, 0); 
    gl.enableVertexAttribArray(attrArray[i]);
  }

  // uniform变量
  const uniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
  const uniformArray = new Array(uniforms);

  for (let i = 0; i < uniforms; ++i) {
    const info = gl.getActiveUniform(program, i);
    uniformArray[i] = gl.getUniformLocation(program, info.name);

    if (uniformArray[i] < 0) {
      console.log(`Failed to get the storage location of ${info.name}`);
    }
  }

  return true;
}

顶点着色器 ​

用来描述顶点特性(位置、颜色等)的程序。

片元着色器 ​

进行逐片元处理的程序。 片元:图像的单元。

着色器对象一 ​

// 
gl.createShader();

// 
gl.compileShader();

// 
gl.isShader();

// 
gl.shaderSource();

// 
gl.deleteShader();

// 
gl.getShaderParameter();

// 
gl.getShaderInfoLog();

着色器对象二 ​

// 
gl.attachShader();

// 
gl.detachShader();

// 
gl.getAttachedShaders();

// 
gl.getShaderPrecisionFormat();

// 
gl.getShaderSource();

WebGLProgram程序对象 ​

// 
gl.createProgram();

// 
gl.deleteProgram();

// 
gl.isProgram();

// 
gl.useProgram();

// 
gl.linkProgram();

// 
gl.validateProgram();

// 
gl.getProgramParameter();

// 
gl.getProgramInfoLog();

Released under the MIT License.