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函数,实现动画循环。
### React WebGL 学习教程和资源 #### 了解基础概念 为了更好地理解如何在React中使用WebGL,建议先掌握一些基础知识。这包括熟悉JavaScript、HTML以及CSS的基础语法;对于图形渲染有一定认识,特别是WebGL的工作原理及其API调用方式。 #### 使用现有库简化开发过程 直接操作WebGL API可能较为复杂,因此可以考虑利用现有的第三方库来加速开发流程并降低难度。例如`react-particles-webgl`就是一个基于React、Three.js和WebGL构建的2D/3D粒子效果库[^1]。另一个例子是`React Unity WebGL`,这是一个让开发者可以在React应用程序里轻松嵌入Unity WebGL游戏或应用的强大工具[^2]。 #### 实践案例研究 通过实际的例子学习往往更加直观有效。比如查看`@14islands/r3f-scroll-rig`这个项目,它展示了怎样把WebGL与React结合起来创造滚动控制场景的效果[^4]。还有关于如何分离SVG并通过React-3Fiber创建可重复使用的WebGL组件的文章也值得一看[^3]。 #### 步骤指南(注意:这里不使用步骤词汇) 全局安装Create React App用于快速搭建新的React工程项目,并按照官方文档指导完成环境配置[^5]。接着可以根据具体需求引入相应的WebGL库文件到项目当中去。如果打算集成像Cesium这样的大型GIS平台,则需参照其官方说明进行设置。 #### 示例代码片段展示基本结构 ```jsx import React from 'react'; // 假设我们正在导入一个名为MyWebGLComponent的自定义WebGL组件 import MyWebGLComponent from './components/MyWebGLComponent'; function App() { return ( <div className="App"> {/* 渲染我们的WebGL组件 */} <MyWebGLComponent /> </div> ); } export default App; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super毛毛穗

今天晚饭加什么?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值