WebGL详解Part3:WebGL API介绍

WebGL详解Part3:WebGL API介绍

前两篇文章,咱们已经把WebGL的"底层逻辑"和"魔法咒语"摸了个门清。这一篇,骚话王要带你正式走进WebGL API的江湖,亲手操控3D世界的每一根神经。毕竟,光有理论和纸上谈兵还不够,真高手还得会用API这把"趁手兵器",才能在前端3D的江湖里纵横捭阖、指点江山。

WebGL的API,说白了就是一套和GPU打交道的"指挥棒"。它不像jQuery那样"傻瓜式"操作DOM,也不像Canvas 2D那样一笔一划画图,而是让你用JavaScript直接和显卡"谈判",把顶点、纹理、着色器、缓冲区这些底层细节玩得明明白白。

本篇就来揭开WebGL API的神秘面纱,从最基础的上下文获取,到各种常用对象的创建与管理,再到渲染流程的点点滴滴,手把手教你如何用API"指点江山"。无论你是刚入门的小白,还是想进阶的前端老炮,只要跟着本文走下去,保证你能把WebGL API玩得炉火纯青。

如果觉得这篇文章对你有帮助,记得点赞、收藏、关注,后续还有更多WebGL实战干货等你来拿!


创建着色器对象的API

说到WebGL的"魔法世界",着色器(Shader)绝对是灵魂角色。要让着色器在GPU上"现身",第一步就是用API创建着色器对象。WebGL为我们准备了专门的"召唤咒"------gl.createShader()

API详解:gl.createShader(type)

  • 作用:创建一个空的着色器对象(Shader Object),后续可以为它"注入"GLSL源码。
  • 参数
    • type:指定要创建的着色器类型,常用的有:
      • gl.VERTEX_SHADER ------ 顶点着色器
      • gl.FRAGMENT_SHADER ------ 片元(像素)着色器
  • 返回值:着色器对象(WebGLShader),如果失败则返回null。
举个例子

假如你要创建一个顶点着色器对象,可以这样写:

js 复制代码
const gl = canvas.getContext('webgl');
const vertexShader = gl.createShader(gl.VERTEX_SHADER);

同理,创建片元着色器对象:

js 复制代码
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

是不是很像在RPG游戏里"招募英雄"?不过,这时候的着色器对象还只是个"空壳",还没有任何实际功能。后续还需要用gl.shaderSource()给它"注入灵魂"(GLSL源码),再用gl.compileShader()进行编译,才能真正上阵杀敌。

常见用法套路

完整的着色器创建流程一般是:

  1. gl.createShader(type)创建对象
  2. gl.shaderSource(shader, source)注入GLSL源码
  3. gl.compileShader(shader)编译
  4. 检查编译状态,确认无误

下面给出一个完整的顶点着色器创建示例:

js 复制代码
// 1. 创建着色器对象
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
// 2. 注入GLSL源码
const vertexSource = `
  attribute vec3 position;
  void main() {
    gl_Position = vec4(position, 1.0);
  }
`;
gl.shaderSource(vertexShader, vertexSource);
// 3. 编译着色器
gl.compileShader(vertexShader);
// 4. 检查编译状态
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  const info = gl.getShaderInfoLog(vertexShader);
  console.error('着色器编译失败:', info);
}

这样,一个"有灵魂"的顶点着色器对象就诞生了!

骚话王小贴士:

  • 创建着色器对象只是第一步,后续还要和片元着色器一起"组队"成程序对象(Program),才能真正参与渲染。
  • API记不住?多敲几遍代码,熟能生巧,API自然就成了你的"肌肉记忆"。

设置GLSL源码的API:gl.shaderSource

有了"空壳"着色器对象,接下来就要给它"注入灵魂"------也就是把GLSL源码塞进去。这一步,WebGL为我们准备了专属API:gl.shaderSource()

API详解:gl.shaderSource(shader, source)

  • 作用:为指定的着色器对象设置GLSL源码。
  • 参数
    • shader:要设置源码的着色器对象(由gl.createShader创建)。
    • source:一段GLSL源码字符串。
  • 返回值:无(void)。
举个例子

假如你已经有了一个顶点着色器对象vertexShader,可以这样给它"注入灵魂"------

js 复制代码
const vertexSource = `
  attribute vec3 position;
  void main() {
    gl_Position = vec4(position, 1.0);
  }
`;
gl.shaderSource(vertexShader, vertexSource);

同理,片元着色器也一样:

js 复制代码
const fragmentSource = `
  precision mediump float;
  void main() {
    gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0);
  }
`;
gl.shaderSource(fragmentShader, fragmentSource);

这一步就像"给英雄装备技能书",只有把GLSL源码塞进去,着色器对象才有了真正的"战斗力"。

常见用法套路

一般来说,设置GLSL源码的步骤紧跟在创建着色器对象之后:

js 复制代码
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, '...GLSL源码...');

源码可以直接写在字符串里,也可以通过Ajax等方式动态加载,灵活性很高。

骚话王小贴士:

  • GLSL源码建议用反引号(`)包裹,这样可以多行书写,结构更清晰。
  • 如果源码有语法错误,后续编译时会报错,记得及时检查。

如果你觉得讲得明白,骚话鬼才的WebGL专栏还会持续更新,记得点赞收藏,后续还有更多API实战技巧等你来解锁!


编译着色器源码的API:gl.compileShader

有了"空壳"着色器对象,也注入了GLSL源码,接下来就要让它"开窍"------也就是把源码编译成GPU能理解的指令。这一步,WebGL为我们准备了"炼丹炉"------gl.compileShader()

API详解:gl.compileShader(shader)

  • 作用:编译指定的着色器对象,把GLSL源码变成GPU能执行的"机器语言"。
  • 参数
    • shader:要编译的着色器对象(由gl.createShader创建,并已通过gl.shaderSource设置源码)。
  • 返回值:无(void)。
举个例子

假如你已经有了一个顶点着色器对象,并且已经设置好了GLSL源码:

js 复制代码
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);

同理,片元着色器也一样:

js 复制代码
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);

这一步就像"炼丹",只有编译成功,着色器对象才能真正"活"过来。

检查编译结果

编译着色器时,最常见的坑就是GLSL源码有语法错误或者不被当前GPU支持。WebGL提供了gl.getShaderParameter(shader, gl.COMPILE_STATUS)来检查编译是否成功。

如果编译失败,可以用gl.getShaderInfoLog(shader)获取详细的错误信息,方便排查问题。

检查示例:
js 复制代码
// 编译着色器
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, vertexSource);
gl.compileShader(shader);

// 检查编译状态
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  const info = gl.getShaderInfoLog(shader);
  console.error('着色器编译失败:', info);
}

骚话王小贴士:

  • 编译失败时,错误信息通常会告诉你是哪一行、哪个变量、什么语法有问题,调试时一定要多看infoLog。
  • 有些浏览器对GLSL的支持略有差异,建议尽量写"标准"GLSL代码,避免踩坑。

创建程序对象的API:gl.createProgram

着色器对象单打独斗还不行,只有把顶点着色器和片元着色器"组队"成一个完整的程序对象(Program),才能真正让GPU执行你的渲染逻辑。WebGL为我们准备了"组队神器"------gl.createProgram()

API详解:gl.createProgram()

  • 作用:创建一个空的程序对象(Program Object),用于后续链接顶点着色器和片元着色器。
  • 参数:无。
  • 返回值:程序对象(WebGLProgram),如果失败则返回null。
举个例子

创建一个程序对象非常简单:

js 复制代码
const program = gl.createProgram();

此时的program还是个"空壳",需要把已经编译好的顶点着色器和片元着色器"装配"进去,才能发挥威力。

这一步就像"组建战队",只有把各路英雄(着色器)都招募进来,才能出征沙场。

常见用法套路

一般来说,创建程序对象的步骤如下:

  1. gl.createProgram()创建程序对象
  2. gl.attachShader(program, shader)把顶点着色器和片元着色器"装配"进来
  3. gl.linkProgram(program)链接程序对象
  4. gl.useProgram(program)激活程序对象,准备渲染

下面给出一个完整的流程示例:

js 复制代码
// 1. 创建程序对象
const program = gl.createProgram();
// 2. 装配着色器
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 3. 链接程序对象
gl.linkProgram(program);
// 4. 检查链接状态
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  const info = gl.getProgramInfoLog(program);
  console.error('程序对象链接失败:', info);
}
// 5. 激活程序对象
gl.useProgram(program);

骚话王小贴士:

  • 一个程序对象只能链接一个顶点着色器和一个片元着色器。
  • 着色器对象在链接后可以删除(gl.deleteShader),不会影响程序对象的正常运行。

附加着色器对象的API:gl.attachShader

有了程序对象(Program),还需要把编译好的顶点着色器和片元着色器"装配"进去,这一步就靠gl.attachShader()来完成。你可以把它理解为"把英雄加入战队"。

API详解:gl.attachShader(program, shader)

  • 作用:将一个已经编译好的着色器对象附加到指定的程序对象上。
  • 参数
    • program:目标程序对象(由gl.createProgram创建)。
    • shader:要附加的着色器对象(顶点或片元着色器,已编译)。
  • 返回值:无(void)。
举个例子

假如你已经有了一个程序对象program,和编译好的顶点着色器vertexShader、片元着色器fragmentShader,可以这样"组队"------

js 复制代码
// 把顶点着色器装进程序对象
gl.attachShader(program, vertexShader);
// 把片元着色器也装进去
gl.attachShader(program, fragmentShader);

注意:一个程序对象只能各自附加一个顶点着色器和一个片元着色器,不能重复附加同类型。

常见用法套路

通常,attachShader的步骤紧跟在createProgram之后,linkProgram之前:

js 复制代码
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

骚话王小贴士:

  • 着色器对象必须先编译成功再attach,否则后续link会失败。
  • attachShader只是"组队",真正让程序对象生效还需要linkProgram这一步。

链接程序对象的API:gl.linkProgram

着色器"组队"完毕,最后还需要"磨合训练"才能真正上场,这一步就靠gl.linkProgram()来完成。它会把顶点着色器和片元着色器"捏合"成一个完整的渲染程序。

API详解:gl.linkProgram(program)

  • 作用:链接指定的程序对象,把附加的顶点着色器和片元着色器合成为一个可用的WebGL程序。
  • 参数
    • program:要链接的程序对象(由gl.createProgram创建,并已attachShader)。
  • 返回值:无(void)。
举个例子

假如你已经有了一个程序对象program,并且已经attach了顶点着色器和片元着色器:

js 复制代码
// 链接程序对象
gl.linkProgram(program);

这一步就像"战队磨合",只有磨合成功,才能真正上场比赛。

检查链接结果

链接程序对象时,最常见的坑是着色器类型不匹配、变量名不一致等。WebGL提供了gl.getProgramParameter(program, gl.LINK_STATUS)来检查链接是否成功。

如果链接失败,可以用gl.getProgramInfoLog(program)获取详细的错误信息,方便排查问题。

检查示例:
js 复制代码
// 链接程序对象
gl.linkProgram(program);
// 检查链接状态
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  const info = gl.getProgramInfoLog(program);
  console.error('程序对象链接失败:', info);
}

骚话王小贴士:

  • 链接成功后,着色器对象可以用gl.deleteShader安全删除,节省内存。
  • 如果顶点着色器和片元着色器的varying变量名、类型不一致,链接一定会失败。

激活程序对象的API:gl.useProgram

程序对象(Program)链接成功后,还需要"点将出征",让它成为当前WebGL上下文的"主力军"。这一步就靠gl.useProgram()来完成。

API详解:gl.useProgram(program)

  • 作用:激活指定的程序对象,让后续的绘制命令都使用它。
  • 参数
    • program:要激活的程序对象(由gl.createProgram创建并已成功链接)。
  • 返回值:无(void)。
举个例子

假如你已经有了一个链接成功的程序对象program

js 复制代码
// 激活程序对象
gl.useProgram(program);

这一步就像"点将出征",只有被激活的程序对象,才能真正参与后续的渲染大业。

常见用法套路

通常,useProgram的步骤紧跟在linkProgram之后,且每次切换不同的渲染逻辑时都要重新useProgram:

js 复制代码
// 切换到指定的程序对象
gl.useProgram(program);
// 后续的绘制命令都会用这个program

骚话王小贴士:

  • 如果传入null,表示不使用任何程序对象,WebGL会"罢工"不渲染。
  • 多个program可以切换使用,但同一时刻只能有一个处于激活状态。

什么是渲染管线?WebGL的渲染管线长啥样

说到3D图形开发,绕不开的一个核心概念就是"渲染管线"(Rendering Pipeline)。你可以把它想象成一条流水线,每一站都负责把3D世界的原材料一步步加工成屏幕上的像素大餐。

渲染管线的本质

渲染管线其实就是一套"加工流程",把你的3D模型、材质、光照等数据,经过一系列处理,最终变成屏幕上的2D图像。每一步都像工厂里的工序,环环相扣,缺一不可。

WebGL中的渲染管线

在WebGL(以及OpenGL ES 2.0)中,渲染管线主要包括以下几个核心阶段,每个阶段都肩负着不可替代的职责:

  1. 顶点着色器(Vertex Shader)
    • 负责对每个顶点进行变换(如模型、视图、投影矩阵),输出裁剪空间坐标,并可传递属性(如法线、纹理坐标)给后续阶段。
  2. 图元装配(Primitive Assembly)
    • 将顶点数据按照指定的方式(如三角形、线段)组装成基本图元。
  3. 裁剪与透视除法(Clipping & Perspective Division)
    • 对超出视锥体的图元进行裁剪,并将坐标从裁剪空间转换到屏幕空间(NDC -> 屏幕坐标)。
  4. 光栅化(Rasterization)
    • 将组装好的图元转换为一系列片元(Fragment),也就是待处理的像素点。此阶段决定了哪些像素会被"点亮"。
  5. 片元着色器(Fragment Shader)
    • 对每个片元进行颜色、光照、纹理等计算,输出最终颜色值。这里可以实现各种炫酷的像素级特效。
  6. 逐片元操作(Per-fragment Operations)
    • 包括深度测试(Depth Test)、模板测试(Stencil Test)、混合(Blending)等,决定片元是否写入帧缓冲,以及如何与已有像素融合。
  7. 帧缓冲(Framebuffer)写入
    • 通过所有测试的片元最终写入帧缓冲(Frame Buffer),成为屏幕上可见的像素。

整个流程就像"流水线造车",每个环节都至关重要。只有所有阶段协同作战,才能把3D世界的复杂数据精准无误地呈现在屏幕上。

你可以把渲染管线想象成一条"3D数据变美图"的高速公路:顶点着色器是起点,图元装配和裁剪是中途的分流站,光栅化是把数据"切片"成像素,片元着色器是像素的化妆师,最后一站帧缓冲才是"上镜"的终极舞台。

说了这么多渲染管线的"幕后流程",那怎么才能让这些流程真正"跑起来",在屏幕上看到3D世界的精彩画面呢?这就需要用到WebGL的绘制命令------最常用的就是gl.drawArrays()

绘制图元的API:gl.drawArrays

渲染管线搭建好之后,最后一步就是"发号施令",让GPU开始批量绘制图元。gl.drawArrays()就是WebGL里最基础、最常用的绘制API。

图元绘制API详解:gl.drawArrays(mode, first, count)

  • 作用:按照指定的方式,从顶点数组中顺序取出顶点,组装成图元并提交给渲染管线。
  • 参数
    • mode:指定要绘制的图元类型,常用的有:
      • gl.POINTS(点)
      • gl.LINES(线段)
      • gl.LINE_STRIP(线带)
      • gl.LINE_LOOP(线环)
      • gl.TRIANGLES(三角形)
      • gl.TRIANGLE_STRIP(三角带)
      • gl.TRIANGLE_FAN(三角扇)
    • first:从顶点数组的哪个位置开始取顶点(第一个顶点的索引,通常是0)。
    • count:一共要取多少个顶点参与绘制。
  • 返回值:无(void)。
举个例子

假如你已经准备好了顶点数据,并且所有着色器、程序对象都已激活:

js 复制代码
// 假设有3个顶点,绘制一个三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

这行代码的意思是:从顶点数组的第0个顶点开始,连续取3个顶点,按照"三角形"方式组装并提交给渲染管线。

你可以把drawArrays想象成"按下启动键",让渲染管线全流程正式开工,把数据变成屏幕上的图像。

常见用法套路

通常,drawArrays的调用顺序是在所有数据、着色器、程序对象都准备好,并且相关属性(attribute)都已绑定后:

js 复制代码
// 1. 绑定缓冲区、设置attribute等
// 2. 激活程序对象 gl.useProgram(program)
// 3. 调用drawArrays发起绘制
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);

骚话王小贴士:

  • 如果顶点数量不够,或者attribute没绑定好,drawArrays会"哑火"啥都不画。
  • 想绘制更复杂的模型,可以多次调用drawArrays,或者结合索引绘制(drawElements)。

索引绘制API:gl.drawElements

如果说gl.drawArrays是"顺序点兵",那gl.drawElements就是"点名上场"。它允许你通过索引数组灵活指定顶点的组合方式,极大提升了顶点复用率和绘制效率,是绘制复杂模型的利器。

图元绘制API详解:gl.drawElements(mode, count, type, offset)

  • 作用:按照索引数组指定的顺序,从顶点数组中取出顶点,组装成图元并提交给渲染管线。
  • 参数
    • mode:指定要绘制的图元类型,和drawArrays一致(如gl.TRIANGLESgl.LINES等)。
    • count:要绘制的索引数量(即一共用多少个索引参与组装图元)。
    • type:索引数组的数据类型,常用的有:
      • gl.UNSIGNED_BYTE(8位无符号整数)
      • gl.UNSIGNED_SHORT(16位无符号整数)
      • WebGL2还支持gl.UNSIGNED_INT(32位无符号整数)
    • offset:索引缓冲区起始偏移量(以字节为单位,通常为0)。
  • 返回值:无(void)。
举个例子

假如你有4个顶点,想用索引绘制两个三角形组成一个矩形:

js 复制代码
// 顶点数据(4个顶点)
const vertices = new Float32Array([
  -1, -1, 0,  // 0
   1, -1, 0,  // 1
   1,  1, 0,  // 2
  -1,  1, 0   // 3
]);
// 索引数据(6个索引,两个三角形)
const indices = new Uint16Array([
  0, 1, 2,  // 第一个三角形
  2, 3, 0   // 第二个三角形
]);
// ...绑定顶点缓冲区和索引缓冲区
// 绘制
// 参数依次为:图元类型、索引数量、索引类型、偏移量
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

这行代码的意思是:按照indices数组指定的顺序,从vertices数组中取顶点,组装成两个三角形。

drawArrays vs drawElements

  • drawArrays:顶点顺序固定,适合简单、重复度低的模型。
  • drawElements:通过索引灵活复用顶点,适合复杂、重复度高的模型(如网格、立方体等)。

你可以把drawElements想象成"点名上场",只让指定的顶点参与战斗,既高效又灵活。

常见用法套路

通常,drawElements的调用顺序是在绑定好顶点缓冲区、索引缓冲区,并设置好attribute后:

js 复制代码
// 1. 绑定顶点缓冲区、索引缓冲区
// 2. 设置attribute
// 3. 激活程序对象 gl.useProgram(program)
// 4. 调用drawElements发起绘制
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_SHORT, 0);

骚话王小贴士:

  • 索引缓冲区必须用ELEMENT_ARRAY_BUFFER绑定。
  • count参数是索引数量,不是顶点数量。
  • type和索引数组类型要对应,否则会报错。

创建缓冲区对象的API:gl.createBuffer

在WebGL中,所有顶点数据、索引数据等都需要先存放到GPU的"缓冲区对象"里,才能被渲染管线高效读取。你可以把缓冲区想象成显卡的"数据仓库",而gl.createBuffer()就是开仓建库的第一步。

缓冲区API详解:gl.createBuffer()

  • 作用:创建一个空的缓冲区对象(Buffer Object),用于后续存储顶点、索引等数据。
  • 参数:无。
  • 返回值:缓冲区对象(WebGLBuffer),如果失败则返回null。
举个例子

创建一个缓冲区对象非常简单:

js 复制代码
const vertexBuffer = gl.createBuffer();

此时的vertexBuffer还是个"空壳",需要后续绑定(bindBuffer)和填充数据(bufferData)才能真正用起来。

这一步就像"建仓库",后续还要"指定用途"和"搬运货物"进来。

常见用法套路

通常,缓冲区的使用流程如下:

  1. gl.createBuffer()创建缓冲区对象
  2. gl.bindBuffer(target, buffer)绑定到指定用途(如gl.ARRAY_BUFFERgl.ELEMENT_ARRAY_BUFFER
  3. gl.bufferData(target, data, usage)填充数据
典型流程示例:
js 复制代码
// 1. 创建缓冲区对象
const vertexBuffer = gl.createBuffer();
// 2. 绑定到顶点缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 3. 填充顶点数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

骚话王小贴士:

  • 一个WebGL上下文可以创建多个缓冲区对象,分别用于顶点、索引、实例等不同用途。
  • 缓冲区对象的"类型"由bindBuffer时的target参数决定。

获取attribute变量位置的API:gl.getAttribLocation

在WebGL中,顶点着色器里的attribute变量需要和缓冲区数据一一对应。要想把数据"对号入座",首先得知道每个attribute在程序对象里的"座位号"。这就要用到gl.getAttribLocation()

API详解:gl.getAttribLocation(program, name)

  • 作用:查询指定attribute变量在程序对象中的位置索引(location),后续用来绑定缓冲区数据。
  • 参数
    • program:已链接成功的程序对象(WebGLProgram)。
    • name:attribute变量名(字符串,需与着色器源码一致)。
  • 返回值:location索引(整数,>=0),如果未找到返回-1。
举个例子

假如你的顶点着色器里有如下声明:

glsl 复制代码
attribute vec3 position;

在JS里获取它的位置:

js 复制代码
const positionLoc = gl.getAttribLocation(program, 'position');

这样,positionLoc就是position变量在当前program里的"座位号",后续设置attribute指针时就要用到它。

你可以把getAttribLocation想象成"查座位表",只有知道每个变量的编号,数据才能精准送达。

常见用法套路

通常,getAttribLocation的调用顺序是在程序对象链接成功后,绘制前:

js 复制代码
const positionLoc = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);

骚话王小贴士:

  • attribute变量名必须和着色器源码完全一致,大小写敏感。
  • 如果返回-1,说明变量未被用到或写错名字,需检查着色器源码。

设置attribute常量值的API:gl.vertexAttrib[1234]f

在WebGL中,除了用缓冲区批量传递attribute数据,有时候也需要为某个attribute变量直接指定一个常量值(比如所有顶点颜色都一样)。这时就要用到gl.vertexAttrib1fgl.vertexAttrib2fgl.vertexAttrib3fgl.vertexAttrib4f这组API。

API详解:gl.vertexAttrib[1234]f(location, v0, v1, v2, v3)

  • 作用:为指定的attribute变量设置一个常量值,后续所有顶点都会用这个值,直到再次设置或启用缓冲区数据。
  • 参数
    • location:attribute变量的位置索引(由getAttribLocation获得)。
    • v0, v1, v2, v3:要设置的常量值,数量取决于API后缀数字(1~4)。
  • 返回值:无(void)。
举个例子

假如你的顶点着色器里有如下声明:

glsl 复制代码
attribute vec3 color;

如果你想让所有顶点都用同一个颜色(比如红色):

js 复制代码
const colorLoc = gl.getAttribLocation(program, 'color');
gl.disableVertexAttribArray(colorLoc); // 禁用缓冲区输入,启用常量模式
gl.vertexAttrib3f(colorLoc, 1.0, 0.0, 0.0); // 所有顶点都用(1,0,0)

你可以把这组API想象成"全员统一着装",一条命令让所有顶点都穿上同样的"制服"。

常见用法套路

  • 先用getAttribLocation获取位置
  • 用disableVertexAttribArray禁用缓冲区输入
  • 用vertexAttrib[1234]f设置常量值
js 复制代码
const loc = gl.getAttribLocation(program, 'foo');
gl.disableVertexAttribArray(loc);
gl.vertexAttrib1f(loc, 0.5); // 所有顶点foo都为0.5

骚话王小贴士:

  • 如果attribute变量启用了缓冲区(enableVertexAttribArray),则vertexAttrib[1234]f设置的值会被忽略。
  • 适合用在所有顶点都用同一个属性值的场景,比如纯色渲染、统一透明度等。

设置attribute指针的API:gl.vertexAttribPointer

在WebGL中,顶点数据通常是批量存储在缓冲区里的。要让GPU知道如何从缓冲区"按字段"读取数据,就需要用gl.vertexAttribPointer()来设置attribute指针。你可以把它理解为"数据分拣员",负责把每一份数据送到正确的变量手里。

API详解:gl.vertexAttribPointer(index, size, type, normalized, stride, offset)

  • 作用:指定当前绑定的缓冲区对象中的数据如何对应到attribute变量上。
  • 参数
    • index:attribute变量的位置索引(由getAttribLocation获得)。
    • size:每个顶点属性的数据个数(1~4),如vec3就填3。
    • type:数据类型,常用gl.FLOAT
    • normalized:是否归一化(true/false),通常顶点坐标用false,颜色用true。
    • stride:每个顶点数据的字节跨度(0表示紧密排列)。
    • offset:当前属性在每个顶点数据中的起始偏移(字节)。
  • 返回值:无(void)。
举个例子

假如你的顶点数据是这样:

js 复制代码
// 每个顶点有3个坐标和3个颜色分量
const vertices = new Float32Array([
  // x, y, z,   r, g, b
   0, 0, 0,   1, 0, 0,
   1, 0, 0,   0, 1, 0,
   0, 1, 0,   0, 0, 1
]);

设置position和color的attribute指针:

js 复制代码
const FSIZE = vertices.BYTES_PER_ELEMENT;
// 绑定缓冲区并填充数据
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 获取位置索引
const posLoc = gl.getAttribLocation(program, 'position');
const colorLoc = gl.getAttribLocation(program, 'color');
// 启用attribute
gl.enableVertexAttribArray(posLoc);
gl.enableVertexAttribArray(colorLoc);
// 设置指针
// 位置:每3个float为一组,起始偏移0
// 步长6个float(24字节)
gl.vertexAttribPointer(posLoc, 3, gl.FLOAT, false, FSIZE * 6, 0);
// 颜色:每3个float为一组,起始偏移3个float(12字节)
gl.vertexAttribPointer(colorLoc, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);

你可以把vertexAttribPointer想象成"快递分拣员",每次都能把数据包裹准确送到对应的收件人手里。

常见用法套路

  • 先用getAttribLocation获取位置
  • 用enableVertexAttribArray启用attribute
  • 用vertexAttribPointer设置指针

骚话王小贴士:

  • stride和offset单位都是字节,别和元素个数搞混。
  • 每次切换缓冲区或attribute变量,都要重新设置指针。
  • 如果只用常量值(vertexAttrib[1234]f),无需设置指针。

uniform变量相关API简明介绍

在WebGL中,uniform变量用于向着色器传递全局参数,比如变换矩阵、光照参数、颜色等。它们在所有顶点/片元间保持一致,是"全场通用的指令"。

获取uniform位置:gl.getUniformLocation

  • 作用:获取指定uniform变量在程序对象中的位置。
  • 参数
    • program:已链接成功的程序对象。
    • name:uniform变量名(需与着色器源码一致)。
  • 返回值:location对象,未找到返回null。
示例
js 复制代码
const uColorLoc = gl.getUniformLocation(program, 'uColor');

设置uniform值:gl.uniform[1234]f、gl.uniformMatrix4fv等

  • 作用:为指定的uniform变量传递数据。
  • 常用API
    • gl.uniform1f(location, v):设置float类型
    • gl.uniform2f(location, v0, v1):设置vec2
    • gl.uniform3f(location, v0, v1, v2):设置vec3
    • gl.uniform4f(location, v0, v1, v2, v3):设置vec4
    • gl.uniform1i(location, v):设置int/bool/sampler2D等
    • gl.uniformMatrix4fv(location, false, mat):设置4x4矩阵
示例
js 复制代码
// 设置颜色
const uColorLoc = gl.getUniformLocation(program, 'uColor');
gl.uniform4f(uColorLoc, 1.0, 0.0, 0.0, 1.0); // 红色
// 设置变换矩阵
const uMVP = gl.getUniformLocation(program, 'uMVP');
gl.uniformMatrix4fv(uMVP, false, mvpMatrix);

你可以把uniform变量想象成"全局广播",一次设置,全场生效。


骚话鬼才又来分享知识了,如果觉得有用就点赞收藏,咱们下篇再见!

相关推荐
wordbaby9 分钟前
搞不懂 px、dpi 和 dp?看这一篇就够了:图解 RN 屏幕适配逻辑
前端
程序员爱钓鱼11 分钟前
使用 Node.js 批量导入多语言标签到 Strapi
前端·node.js·trae
鱼樱前端12 分钟前
uni-app开发app之前提须知(IOS/安卓)
前端·uni-app
V***u45313 分钟前
【学术会议论文投稿】Spring Boot实战:零基础打造你的Web应用新纪元
前端·spring boot·后端
i听风逝夜1 小时前
Web 3D地球实时统计访问来源
前端·后端
iMonster1 小时前
React 组件的组合模式之道 (Composition Pattern)
前端
呐呐呐呐呢1 小时前
antd渐变色边框按钮
前端
元直数字电路验证1 小时前
Jakarta EE Web 聊天室技术梳理
前端
wadesir1 小时前
Nginx配置文件CPU优化(从零开始提升Web服务器性能)
服务器·前端·nginx
牧码岛1 小时前
Web前端之canvas实现图片融合与清晰度介绍、合并
前端·javascript·css·html·web·canvas·web前端