WebGL学习2
WebGL(Web Graphics Library)是一种基于 OpenGL ES 2.0 的 JavaScript API,用于在网页上实现高性能的 3D 图形渲染。
1. 初始化 WebGL 上下文
在使用 WebGL 之前,需要获取<canvas>
元素并创建 WebGL 上下文。
// 获取canvas
const canvas = document.querySelector('canvas');
// 获取webgl上下文对象,注意选择webgl的1.0版本,2.0版本有一些语法不一样
const gl = canvas.getContext('webgl');
document.querySelector('canvas')
:通过选择器获取 HTML 文档中的<canvas>
元素。canvas.getContext('webgl')
:获取<canvas>
元素的 WebGL 上下文。如果浏览器不支持 WebGL,该方法将返回null
。
2. 着色器编程
WebGL 使用着色器(Shader)来处理图形渲染的各个阶段。着色器是运行在 GPU 上的小程序,主要分为顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)。
import fragShader from '../shader/2.webgl绘制点/fragShader.glsl';
import vertexShader from '../shader/2.webgl绘制点/vertexShader.glsl';
import { initShader } from '../lib';
// fragShader和vertexShader引入之后是字符串,需要被编译为可执行的程序
const program = initShader(gl, vertexShader, fragShader);
- 顶点着色器(Vertex Shader):负责处理每个顶点的位置、颜色等属性。
- 片元着色器(Fragment Shader):负责处理每个像素的颜色。
initShader
函数:用于初始化着色器,创建并配置程序对象。
export function initShader(gl, vshader_source, fshader_source) {
// 创建着色器
const vertexShader = createShaderFromString(
gl,
gl.VERTEX_SHADER,
vshader_source
);
const fragmentShader = createShaderFromString(
gl,
gl.FRAGMENT_SHADER,
fshader_source
);
// 创建一个程序对象 操作手,专门负责javaScript和shader着色器的通讯
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 将javascrpt和程序对象关联
gl.linkProgram(program);
// 使用程序对象
gl.useProgram(program);
// 返回程序对象
return program;
}
gl.createShader(type)
:创建一个指定类型的着色器对象(gl.VERTEX_SHADER
或gl.FRAGMENT_SHADER
)。gl.shaderSource(shader, source)
:将着色器源代码赋值给着色器对象。gl.compileShader(shader)
:编译着色器对象。gl.createProgram()
:创建一个程序对象,用于管理着色器。gl.attachShader(program, shader)
:将着色器对象附加到程序对象上。gl.linkProgram(program)
:链接程序对象,将顶点着色器和片元着色器关联起来。gl.useProgram(program)
:使用指定的程序对象进行渲染。
3. 缓冲区对象
缓冲区对象(Buffer Object)用于在 CPU 和 GPU 之间传递数据,如顶点坐标、颜色等。
// 1.创建顶点数据对象
const vertices = new Float32Array([
0, 0.5,
-0.5, -0.5,
0.5, -0.5,
]);
// 2.创建缓冲区对象
const buffer = gl.createBuffer();
// 3.绑定缓冲区对象的用途
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
// 4.向缓冲区中写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 5.设置attribute变量对缓冲区的访问规则
const a_Position = gl.getAttribLocation(program, 'a_Position');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 6.启动数据读取
gl.enableVertexAttribArray(a_Position);
gl.createBuffer()
:创建一个缓冲区对象。gl.bindBuffer(target, buffer)
:将缓冲区对象绑定到指定的目标(gl.ARRAY_BUFFER
用于存储顶点数据)。gl.bufferData(target, data, usage)
:向缓冲区对象中写入数据。usage
参数指定数据的使用方式,gl.STATIC_DRAW
表示数据不会频繁更改。gl.getAttribLocation(program, name)
:获取顶点着色器中attribute
变量的位置。gl.vertexAttribPointer(index, size, type, normalized, stride, offset)
:设置attribute
变量对缓冲区的访问规则。index
:attribute
变量的位置。size
:每个顶点的分量个数。type
:数据的类型,如gl.FLOAT
。normalized
:是否需要归一化。stride
:连续顶点属性之间的间隔。offset
:顶点属性在数组中的偏移量。
gl.enableVertexAttribArray(index)
:启用指定位置的attribute
变量。
4. 绘制图形
使用gl.drawArrays
或gl.drawElements
方法绘制图形。
// 最后调用drawArrays绘制,这里可以将count取3,一次绘制三个点
gl.drawArrays(gl.POINTS, 0, 3);
gl.drawArrays(mode, first, count)
:从指定位置开始,绘制指定数量的顶点。mode
:绘制模式,如gl.POINTS
(点)、gl.LINES
(线)、gl.TRIANGLES
(三角形)等。first
:从第几个顶点开始绘制。count
:要绘制的顶点数量。
5. 深度检测
深度检测用于判断物体的正确遮挡关系。
// 深度检测,用来判断物体的正确遮挡关系
gl.enable(gl.DEPTH_TEST);
gl.enable(capability)
:启用指定的功能,gl.DEPTH_TEST
表示启用深度检测。
6. 相机和投影矩阵
在 3D 场景中,需要使用相机和投影矩阵来定义视角和投影方式。
import { mat4 } from 'gl-matrix';
import PerspectiveCamera from '../lib/PerspectiveCamera';
// viewMatrix的参数
const vModelView = {
viewX: 0,
viewY: 0,
viewZ: 1,
lookAtX: 0,
lookAtY: 0,
lookAtZ: 0,
};
const vModelProj = {
fov: 45,
aspect: 1,
near: 0.1,
far: 4,
};
const camera = new PerspectiveCamera(gl, program, {
fov: vModelProj.fov,
aspect: vModelProj.aspect,
near: vModelProj.near,
far: vModelProj.far,
lookAt: [vModelView.lookAtX, vModelView.lookAtY, vModelView.lookAtZ],
position: [vModelView.viewX, vModelView.viewY, vModelView.viewZ],
up: [0, 1, 0],
});
// 创建视图矩阵
const createViewMatrix = () => {
const viewMatrix = mat4.create();
// 创建视图矩阵,三个参数,相机位置,注视的坐标,上方向
mat4.lookAt(
viewMatrix,
[vModelView.viewX, vModelView.viewY, vModelView.viewZ],
[vModelView.lookAtX, vModelView.lookAtY, vModelView.lookAtZ],
[0, 1, 0]
);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix);
};
// 创建投影矩阵
const createProjMatrix = () => {
const projMatrix = mat4.create();
// 创建正交投影矩阵
mat4.perspectiveNO(projMatrix, ...Object.values(vModelProj));
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix);
};
PerspectiveCamera
:自定义的透视相机类,用于管理相机的属性和矩阵。mat4.create()
:创建一个 4x4 的矩阵。mat4.lookAt(out, eye, center, up)
:创建视图矩阵,指定相机的位置、注视点和上方向。mat4.perspectiveNO(out, fovy, aspect, near, far)
:创建透视投影矩阵。gl.uniformMatrix4fv(location, transpose, value)
:将矩阵数据传递给着色器中的uniform
变量。
7. 光照效果
在 WebGL 中,可以实现不同类型的光照效果,如环境光、漫反射光和镜面高光。
import AmbientLight from '../lib/Light/AmbientLight';
import DiffuseLight from '../lib/Light/DiffuseLight';
import SpecularLight from '../lib/Light/SpecularLight';
const vModel = {
color: 0xffffff,
intensity: 1
};
const vModelDiffuse = {
color: 0xffffff,
intensity: 1
};
const light = new AmbientLight(gl, program, vModel);
const diffuseLight = new DiffuseLight(gl, program, vModelDiffuse);
const specularLight = new SpecularLight(gl, program);
AmbientLight
:环境光类,用于模拟全局光照效果。DiffuseLight
:漫反射光类,用于模拟物体表面的漫反射光照效果。SpecularLight
:镜面高光类,用于模拟物体表面的镜面反射光照效果。
8. GUI 交互
使用dat.gui
库可以创建简单的图形用户界面,方便调试和交互。
import * as dat from 'dat.gui';
const gui = new dat.GUI();
const vModel = {
scale: 1,
angle: 0,
tx: 0,
ty: 0
};
gui.add(vModel, 'scale', 0.1, 2).step(0.1).name('缩放').onChange(render);
gui.add(vModel, 'angle', 0, 360).step(1).name('旋转').onChange(render);
gui.add(vModel, 'tx', -1, 1).step(0.1).name('x轴平移').onChange(render);
gui.add(vModel, 'ty', -1, 1).step(0.1).name('y轴平移').onChange(render);
dat.GUI()
:创建一个 GUI 对象。gui.add(object, property, min, max, step)
:添加一个滑块控件,用于控制对象的属性。onChange(callback)
:当控件的值发生变化时,调用回调函数。