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变量想象成"全局广播",一次设置,全场生效。


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

相关推荐
brzhang2 分钟前
我们复盘了100个失败的AI Agent项目,总结出这3个“必踩的坑”
前端·后端·架构
万能的小裴同学9 分钟前
让没有小窗播放的视频网站的视频小窗播放
前端·javascript
小小琪_Bmob后端云42 分钟前
引流之评论区截流实验
前端·后端·产品
我科绝伦(Huanhuan Zhou)1 小时前
MOP数据库备份脚本生成工具
前端·css·数据库
海的诗篇_1 小时前
前端开发面试题总结-vue2框架篇(三)
前端·javascript·css·面试·vue·html
Danny_FD1 小时前
在 React 函数组件中实现 `<textarea>` 平滑自动滚动到底部
前端
掘金一周1 小时前
数据脱敏的这6种方案,真香!| 掘金一周 5.29
前端·人工智能·后端
小小愿望1 小时前
彻底禁用移动端H5页面默认下拉刷新功能:原理与实战
前端
Antioper1 小时前
盘点前端经典手写代码题
前端
断竿散人1 小时前
⚡CSS动画性能优化:60fps丝滑体验的终极秘籍
前端·css·性能优化