当前位置: 首页 > news >正文

WebGL学习

1. 环境搭建

1.1 获取 Canvas 元素和 WebGL 上下文

在 WebGL 中,我们通常使用<canvas>元素作为绘图区域,通过getContext方法获取 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 上下文对象gl,后续的 WebGL 操作都将基于这个上下文对象进行。
1.2 初始化着色器

着色器是 WebGL 中用于处理图形渲染的程序,分为顶点着色器和片元着色器。我们需要将着色器源码编译并链接成一个可执行的程序。

import { initShader } from '../lib';
import fragShader from '../shader/xxx/fragShader.glsl';
import vertexShader from '../shader/xxx/vertexShader.glsl';

// fragShader和vertexShader引入之后是字符串,需要被编译为可执行的程序
const program = initShader(gl, vertexShader, fragShader);

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_SHADERgl.FRAGMENT_SHADER)的着色器对象。
  • gl.shaderSource(shader, source):将着色器源码source赋值给着色器对象shader
  • gl.compileShader(shader):编译着色器对象。
  • gl.createProgram():创建一个 WebGL 程序对象。
  • gl.attachShader(program, shader):将着色器对象附加到程序对象上。
  • gl.linkProgram(program):链接程序对象,将顶点着色器和片元着色器组合成一个可执行的程序。
  • gl.useProgram(program):使用指定的程序对象进行渲染。

2. 数据传递

2.1 Attribute 变量传值

Attribute 变量用于在顶点着色器中接收顶点数据,通常是每个顶点都有不同的值。

// 获取attribute变量的内存地址
const a_Position = gl.getAttribLocation(program, 'a_Position');
// 往内存地址中写入数据
const point = [0.5, 0.5];
gl.vertexAttrib2f(a_Position, ...point);

解释

  • gl.getAttribLocation(program, 'a_Position'):获取顶点着色器中a_Position变量的内存地址。
  • gl.vertexAttrib2f(a_Position, ...point):将二维坐标point的值传递给a_Position变量。
2.2 Uniform 变量传值

Uniform 变量用于在顶点着色器和片元着色器中传递全局常量,所有顶点都使用相同的值。

const u_ScreenSize = gl.getUniformLocation(program, 'u_ScreenSize');
gl.uniform2f(u_ScreenSize, canvas.width, canvas.height);

解释

  • gl.getUniformLocation(program, 'u_ScreenSize'):获取程序对象中u_ScreenSize变量的内存地址。
  • gl.uniform2f(u_ScreenSize, canvas.width, canvas.height):将画布的宽度和高度作为二维向量传递给u_ScreenSize变量。

3. 缓冲区对象

缓冲区对象用于存储大量的顶点数据,提高数据传输效率。

// 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):将缓冲区对象buffer绑定到指定的目标targetgl.ARRAY_BUFFER用于存储顶点数据)。
  • gl.bufferData(target, data, usage):将数据data写入到绑定的缓冲区对象中,usage指定数据的使用方式(gl.STATIC_DRAW表示数据不会频繁更改)。
  • gl.vertexAttribPointer(location, size, type, normalized, stride, offset):设置attribute变量对缓冲区的访问规则。
    • locationattribute变量的内存地址。
    • size:每个顶点的分量个数。
    • type:数据的格式。
    • normalized:是否需要归一化。
    • stride:连续顶点属性之间的间隔。
    • offset:顶点属性在数组中的偏移量。
  • gl.enableVertexAttribArray(location):启动对attribute变量的数据读取。

4. 绘制操作

4.1 gl.drawArrays

gl.drawArrays用于从缓冲区中读取顶点数据并进行绘制。

gl.drawArrays(gl.POINTS, 0, 3);

解释

  • gl.drawArrays(mode, first, count)
    • mode:指定绘制的模式,如gl.POINTS(点)、gl.LINES(线)、gl.TRIANGLES(三角形)等。
    • first:从第几个顶点开始绘制。
    • count:要绘制的顶点数量。
4.2 gl.drawElements

gl.drawElements用于通过索引缓冲区来绘制图形,适用于多个顶点共享的情况。

const indices = new Uint16Array([0, 1, 2]);
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);

解释

  • gl.createBuffer():创建一个索引缓冲区对象。
  • gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer):将缓冲区对象绑定到索引缓冲区目标。
  • gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW):将索引数据写入到索引缓冲区中。
  • gl.drawElements(mode, count, type, offset)
    • mode:绘制模式。
    • count:要绘制的索引数量。
    • type:索引数据的类型。
    • offset:索引数据在缓冲区中的偏移量。

5. 矩阵变换

在 WebGL 中,我们通常使用矩阵来实现视图和投影变换。

import { mat4 } from 'gl-matrix';

// 创建视图矩阵
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.orthoNO(projMatrix, ...Object.values(vModelProj));
    gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix);
};

解释

  • mat4.create():创建一个 4x4 的单位矩阵。
  • mat4.lookAt(out, eye, center, up):创建一个视图矩阵,eye表示相机位置,center表示注视的坐标,up表示上方向。
  • mat4.orthoNO(out, left, right, bottom, top, near, far):创建一个正交投影矩阵。
  • gl.uniformMatrix4fv(location, transpose, value):将 4x4 矩阵value传递给uniform变量locationtranspose表示是否需要转置矩阵。

6. 深度检测

深度检测用于判断物体的正确遮挡关系,确保离相机近的物体遮挡离相机远的物体。

// 深度检测,用来判断物体的正确遮挡关系
gl.enable(gl.DEPTH_TEST);

解释

  • gl.enable(capability):启用指定的功能,gl.DEPTH_TEST表示启用深度检测。

7. 动画循环

使用requestAnimationFrame实现动画循环,不断更新和重绘场景。

let angleX = 0;
let angleY = 0;
const render = () => {
    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    angleX += 1;
    angleY += 1;
    cube.rotation = [angleX, angleY, 0];
    gl.drawElements(gl.TRIANGLES, cube.count, gl.UNSIGNED_SHORT, 0);
    requestAnimationFrame(render);
};

render();

解释

  • requestAnimationFrame(callback):请求浏览器在下次重绘之前调用callback函数,实现动画循环。

相关文章:

  • 时尚复古新艺术风品牌海报徽标设计衬线英文字体安装包 Blessing – Art Nouveau Font
  • 【软考-架构】5.3、IPv6-网络规划-网络存储-补充考点
  • log4j2漏洞:反弹shell
  • maven导入本地jar包
  • C++抽象与类的核心概念解析
  • 粗解JQHttpServer
  • 如何优化 TCP/IP 的 NCCL 通信
  • 地图(六)利用python绘制连接地图
  • 【QT:窗口】
  • 小程序API —— 51小程序界面交互 - loading 提示框
  • 旅游类小程序界面设计
  • 基于HetEmotionNet框架的多模态情绪识别系统
  • 实战2. 利用Pytorch解决 CIFAR 数据集中的图像分类为 10 类的问题——提高精度
  • 施磊老师c++(八)
  • 唤起“栈”的回忆
  • 【数据结构】栈与队列:基础 + 竞赛高频算法实操(含代码实现)
  • Web测试
  • 神聖的綫性代數速成例題7. 逆矩陣的性質、逆矩陣的求法
  • 深度学习-yolo实战项目【分类、目标检测、实例分割】?如何创建自己的数据集?如何对数据进行标注?没有GPU怎么办呢?
  • 计算机网络基础:网络配置与管理
  • 河南一女子被医院强制带走治疗,官方通报:当值医生停职
  • 101岁陕西省军区原司令员冀廷璧逝世,曾参加百团大战
  • 当智慧农场遇见绿色工厂:百事如何用科技留住春天的味道?
  • 商务部召开全国离境退税工作推进会:提高退税商店覆盖面,扩大入境消费
  • 特朗普中东行:“能源换科技”背后的权力博弈|907编辑部
  • 普京批准俄方与乌克兰谈判代表团人员名单