GPU
一幅图像是由成千上万的像素点组成,我们每次处理一个像素都是一个任务。如果这些处理都让 CPU 来处理就有点浪费,所以 GPU 诞生~
GPU 是由大量的小型处理单元构成的,数量众多,能够保证同时处理所有的像素点。
-
顶点缓冲区( VBO ) :存储顶点的位置、颜色、法向量等数据(
createBuffer()
创建) -
顶点数组对象( VAO ) :存储顶点数据的格式以及顶点数据所需的VBO对象的引用,保存了所有顶点数据属性的状态结合
-
纹理缓冲区:存储纹理贴图像素数据
-
深度缓冲区
- 存储每个像素点的深度,即z坐标轴值(又称z缓冲区)
- 用来判断像素颜色能否存入颜色缓冲区,同一xy坐标舍弃z值大的颜色
-
颜色缓冲区
- 存储像素数据,即片元的颜色值
- 16位:RGB565,红色5位,绿色6位,蓝色5位,不保存透明通道(之所以绿色多1位是因为人眼对绿色更加敏感)
- 24位:RGB888,红色8位,绿色8位,蓝色8位,不保存透明通道
- 32位:RGBA8888,红色8位,绿色8位,蓝色8位,透明通道8位
- 如果使用了多渲染目标(Multiple Render Targets)技术,那么颜色附着的数量可能会大于一
-
模板缓冲区
- 控制颜色缓存某个位置的写入操作(阴影绘制需要使用)
WebGL 工作原理
WebGL 的发展历程
OpenGL 就是个开放图形库,是用于渲染 2D,3D 矢量图形的跨平台,跨语言的应用程序编程接口。
WebGL API 基于 OpenGL ES,而 OpenGL ES 又是 OpenGL 的针对嵌入式设备图形开发的子集。 因此,它的 API 可以让 Web 开发者通过 js 代码来操作本地 OpenGL(OpenGL ES---移动设备) 的部分接口,和显卡进行通信实现页面的图形的渲染。
GLSL
GLSL,全称 OpenGL Shading Language,也就是 OpenGL 着色器语言。主要用于开发着色器程序,可以通过编程控制 GPU 渲染过程中的某些部分。它是 C 语言的一个超集,增加了一些数据类型和数学函数。
WebGL、OpenGL 与 GLSL
渲染管线
绿色:可编程状态
紫色:不可编程状态
黄色:CPU,其余 GPU
以下内容均是基于 webgl 1
-
顶点着色器(Vertex Shader)
功能:操作顶点缓冲区中的顶点数据(顶点的坐标转换,转化为相机坐标系),此阶段为可编程状态。
顶点着色器处理的是逐顶点处理顶点数据
-
Attribute 属性
- 从缓冲中获取
- 常见的信息有uv、position、normal、indices、color
-
Uniform 全局变量
- 顶点变换矩阵、光方向、光源位置、光颜色
-
Textures 纹理:从像素或纹理元素中获取的数据
// attribute 从缓冲区获取的数据
attribute vec4 position;
uniform mat4 matrix;
void main() {
// position 即顶点坐标,3D图形,顶点坐标需转换成屏幕坐标
gl_Position = position * matrix;
}
-
图元装配
目的:通过顶点着色器的处理,我们得到了我们想要的顶点,由顶点生成一个个图元(三角形)。
- 点:gl.POINTS
- 线:gl.LINES
- 三角形:gl.TRIANGLES
- 顶点的像素尺寸:顶点着色器中的内置变量gl_PointSize
-
光栅化
光栅化就是把图元转化为片元,canvas 画布上图像的每一个像素都对应一个片元。(
- 判断像素是否在三角网格内,计算每条边上的像素坐标
- 对非顶点数据插值处理(赋予其他信息,因为“图元”信息不止有颜色)
gl_FragCoord:相对canvas 画布这个窗口坐标系统的值 内置变量
- 片元位置(canvas 画布):gl_FragCoord.xy(vec2类型)
- 片元的深度值:gl_FragCoord.z (标识片元距离人眼睛的距离)
gl_PointCoord:点域图元光栅化后的图元 内置变量
- gl_PointSize 定义的区域内的片元坐标
- 是个相对值,点域图元与其对应片元在canvas画布上的相对位置,区域[0, 1]
varying
- 顶点着色器中的 gl_Position 顶点数据
- 光栅化阶段进行插值
-
片元着色器(Fragment Shader)
片元着色器是逐片元处理片元数据,完成模型颜色,质地,光照效果,阴影
-
gl_FragColor:给其赋值即是给片元上色 内置变量
- 可以是一个确定的RGBA值
- 可以是一个和片元位置相关的值
- 可以是插值后的顶点颜色
-
dicard:将片元丢弃,并移除帧缓冲区
/* 确定颜色值 */
//红色
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
/* 颜色值关联片元位置 */
//canvasWidth、canvasHeight表示画布的宽高尺寸
gl_FragColor = vec4(gl_FragCoord.x/canvasWidth,gl_FragCoord.y/canvasHeight,0.0,1.0);
/* 顶点颜色插值 */
varying vec4 v_Color;//插值后顶点颜色数据
gl_FragColor = v_Color;
/* 纹理缓冲区 */
varying vec2 v_TexCoord;//插值后纹理坐标数据
uniform sampler2D u_Sampler;//插值处理后纹理贴图像素值数据
//texture2D方法拾取纹理坐标对应的像素值
gl_FragColor = texture2D(u_Sampler,v_TexCoord);
-
裁切测试
对用户指定的矩形区域进行测试,剔除窗口之外的像素
- 主要解决视口比屏幕窗口小造成的渲染浪费
- 开启:glEnable(GL_SCISSOR_TEST),指定区域:glScissor()
-
模板测试
根据Stencil或 Depth Buffer Test结果分条件更新 Stencil Buffer
- 开启:glEnable(GL_STENCIL_TEST)
- 利用模板测试:实现平面镜效果、平面阴影、物体轮廓等
-
深度测试
比较(x,y)坐标相同片元的深度值Z,默认的情况下深度值是gl_FragCoord.z(大的在后,小的在前)
- 抛弃掉位置靠后的像素值
- 开启:glEnable(GL_DEPTH_TEST),指定比较运算符:glDepthFunc()
- 渲染半透明物体,需要开启深度测试(关闭深度写入)
- alpha为0时,会进行深度测试
-
alpha混合
- 主要用于产生半透明效果,可根据片元的 alpha 值混合
- 开启:glEnable(GL_BLEND)
- 通过 glBlendFuncSeparate()、glBlendFunc() 和 glBlendEquation() 来设置各种混合效果(GL_ZERO、GL_ONE、GL_SRC_ALPHA、GL_FUNC_ADD等)
C¯result=C¯source∗Fsource+C¯destination∗Fdestination /* C¯source:源颜色向量。这是来自纹理的本来的颜色向量。 C¯destination:目标颜色向量。这是储存在颜色缓冲中当前位置的颜色向量。 Fsource:源因子。设置了对源颜色的alpha值影响。 Fdestination:目标因子。设置了对目标颜色的alpha影响。 */
-
抖动处理
-
开启:glEnable(GL_DITHER),默认开启
-
针对颜色较少的系统,牺牲分辨率通过颜色值的抖动增加可用颜色数量
-
WebGL 的一些限制
-
Line:一些机器的一些浏览器上线框永远是1,fix:可以用三角形模拟线条
-
应用于摄像机的任何后期处理效果都会禁用内置的抗锯齿功能 (webgl 1)
-
HDR 与抗锯齿功能不兼容 (webgl 1)
-
不同的多重采样级别(2x、4x 等)在 WebGL 中没有任何影响(webgl 1)
WebGL 2 与 WebGL 1 差异
兼容性
- WebGL2 几乎 100% 兼容 WebGL1
- WebGL2并非所有的浏览器都支持,所以判断不存在其上下文,则回退使用WebGL1
差异点
- 扩展功能:在WebGL1需要先加载扩展再调用,WebGL2中很多可以直接使用(比如VAO,OES_vertex_array_object扩展)
- WebGL 2 需要 显示指定着色器语言版本
// version 必须在第一行且不能有任何空格
var vsSource = `#version 300 es
`;
-
GLSL 300 es vs GLSL 100 es
- 使用 in 代替 attribute
-
// 顶点数据的变量,使用 in 代替 attribute // 在GLSL 100中,使用attribute关键词 attribute vec4 aPosition; attribute vec2 aTexcoord; attribute vec3 aNormal; // 在GLSL 300 es中,使用in关键词 in vec4 aPosition; in vec2 aTexcoord; in vec3 aNormal;
- varying 被in/out替代
-
// 在GLSL 100,varying关键词声明变量 varying vec2 vTexcoord; varying vec3 vNormal; // 在GLSL 300 es中,varying改为out声明 out vec2 vTexcoord; out vec3 vNormal;
- 取消内置变量 gl_FragColor
-
// GLSL 100 中,给内置变量gl_FragColor赋值来设置片元的输出颜色 gl_FragColor = vec4(1,1,1, 1); // white // GLSL 300 es中,需要自己定义一个输出颜色的变量 out vec4 myOutputColor;...... void main() { myOutputColor = vec4(1, 1, 1, 1); // white }
- texture代替 texture2D、textureCube
Three.js 工作原理
工作流程
黄色:JS 部分 绿色:opengl es 部分
顶点处理
坐标变换
将一个立方体模型绕 y 轴顺时针旋转 Math.PI/3,相机位置 y 为 60
顶点变换流程如下:
-
模型矩阵:modelMatrix,记录模型旋转、平移以及缩放等(使用 JS 计算位置性能较低,使用矩阵记录)
-
视图矩阵:viewMatrix,设置相机对象的位置属性和lookAt方法本质就是改变其视图矩阵属性
-
投影矩阵:projectMatrix 根据不同的相机其投影不一,且参数变化其也需要改变
- 通过 perspectiveCamera.xxx 修改属性,调用updateProjectionMatrix(不然不生效)
- 在更新渲染区域相关参数时,也要手动更新矩阵
-
编写顶点着色器
顶点着色器
CPU JS 矩阵变换流程
Three.js本地矩阵.materix和世界矩阵.matrixWorld
GPU opengl es
gl_Position = position * modelMatrix * viewMatrix * projectionMatrix;
片元着色器
CPU JS 创建一个 MeshBasicMaterial 材质,增加灯光,以及场景雾化效果
GPU opengl es
vec3 vLighting = LightColor * directional;
gl_FragColor = vec4(color.rgb * vLighting, color.a);
绘制流程
- 整体流程
- 画一个立方体
绘图页面中必备且唯一的要素:camera、scene、renderer
-
Scene:场景(三维空间),是物体、光源等元素的容器
-
Camera:相机(观察者),控制视角位置、范围和焦点位置
-
Object3D:物体(三维模型),包括二维的点线面、三维的粒子等
-
Light:光源,包括全局光、平行光、点光源等
-
Renderer:渲染器(画布),用于渲染场景及其物体
-
Control:控制器(相机控件),可通过键盘、鼠标控制相机
Three.js vs WebGL
-
WebGL
- 学习和开发成本较高,需要数学以及图形学知识基础
- JS 中可以直接使用其API绘制3D图形
-
Three.js
-
辅助生成材质,配置灯光,导出模型数据
-
自动生成了各种矩阵,用来达到旋转、平移以及缩放效果
-
生成了顶点着色器、根据材质生成片元着色器
-
将webGL基于光栅化的2D API,封装成了 3D API
-