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()
进行编译,才能真正上阵杀敌。
常见用法套路
完整的着色器创建流程一般是:
- 用
gl.createShader(type)
创建对象 - 用
gl.shaderSource(shader, source)
注入GLSL源码 - 用
gl.compileShader(shader)
编译 - 检查编译状态,确认无误
下面给出一个完整的顶点着色器创建示例:
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
还是个"空壳",需要把已经编译好的顶点着色器和片元着色器"装配"进去,才能发挥威力。
这一步就像"组建战队",只有把各路英雄(着色器)都招募进来,才能出征沙场。
常见用法套路
一般来说,创建程序对象的步骤如下:
- 用
gl.createProgram()
创建程序对象 - 用
gl.attachShader(program, shader)
把顶点着色器和片元着色器"装配"进来 - 用
gl.linkProgram(program)
链接程序对象 - 用
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)中,渲染管线主要包括以下几个核心阶段,每个阶段都肩负着不可替代的职责:
- 顶点着色器(Vertex Shader)
- 负责对每个顶点进行变换(如模型、视图、投影矩阵),输出裁剪空间坐标,并可传递属性(如法线、纹理坐标)给后续阶段。
- 图元装配(Primitive Assembly)
- 将顶点数据按照指定的方式(如三角形、线段)组装成基本图元。
- 裁剪与透视除法(Clipping & Perspective Division)
- 对超出视锥体的图元进行裁剪,并将坐标从裁剪空间转换到屏幕空间(NDC -> 屏幕坐标)。
- 光栅化(Rasterization)
- 将组装好的图元转换为一系列片元(Fragment),也就是待处理的像素点。此阶段决定了哪些像素会被"点亮"。
- 片元着色器(Fragment Shader)
- 对每个片元进行颜色、光照、纹理等计算,输出最终颜色值。这里可以实现各种炫酷的像素级特效。
- 逐片元操作(Per-fragment Operations)
- 包括深度测试(Depth Test)、模板测试(Stencil Test)、混合(Blending)等,决定片元是否写入帧缓冲,以及如何与已有像素融合。
- 帧缓冲(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.TRIANGLES
、gl.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)才能真正用起来。
这一步就像"建仓库",后续还要"指定用途"和"搬运货物"进来。
常见用法套路
通常,缓冲区的使用流程如下:
- 用
gl.createBuffer()
创建缓冲区对象 - 用
gl.bindBuffer(target, buffer)
绑定到指定用途(如gl.ARRAY_BUFFER
或gl.ELEMENT_ARRAY_BUFFER
) - 用
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.vertexAttrib1f
、gl.vertexAttrib2f
、gl.vertexAttrib3f
、gl.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)
:设置vec2gl.uniform3f(location, v0, v1, v2)
:设置vec3gl.uniform4f(location, v0, v1, v2, v3)
:设置vec4gl.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变量想象成"全局广播",一次设置,全场生效。
骚话鬼才又来分享知识了,如果觉得有用就点赞收藏,咱们下篇再见!