内容提要
本文介绍JavaScript向GLSL传递数据的方法。attribute仅用于顶点着色器,需通过createBuffer、bindBuffer激活、bufferData写入数据,再用getAttribLocation和vertexAttribPointer关联并启用。uniform用于传递固定数据,可通过uniformMatrix4fv等方法传入。sampler2D较特殊,需先激活纹理单元、绑定纹理对象,再用uniform1i传入纹理单元地址。
延伸解读
理解 bindBuffer 的“激活”本质
文章强调 bindBuffer 是一个“激活”动作,同一 target 仅能有一个 buffer 处于激活状态。这解释了为什么在 vertexAttribPointer 前需要重新 bindBuffer:因为运行过程中可能激活了其他 buffer。把 bindBuffer 理解为“切换当前操作对象”,就能理清 attribute 传递的整个流程,避免因状态混淆导致的错误。
attribute 与 uniform 的核心区别
attribute 只能由顶点着色器接收,每个顶点传入不同数据,适合位置、法线等逐顶点变化的量;uniform 是从外部传入的固定数据,所有顶点共享同一值,适合变换矩阵、光照参数等。理解这一区别有助于正确选择数据传递方式,避免在着色器中错误声明变量类型。
sampler2D 传递的特殊性
sampler2D 本质是 uniform,但传递方式特殊:需要先激活纹理单元(activeTexture),再绑定纹理对象(bindTexture),最后用 uniform1i 传入纹理单元地址。官方规范指出,加载 sampler 仅能用 glUniform1i 或 glUniform1iv,使用其他函数会导致 GL_INVALID_OPERATION 错误。
纹理单元与纹理对象的区分
文章指出 sampler2D 传值涉及两个概念:texture unit 用于处理纹理映射,GPU 至少支持 8 个,可通过 gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) 查询具体数量;texture object 则存储纹理数据,类似 buffer object 但更复杂。区分二者有助于理解为何需要先 activeTexture 再 bindTexture。
Q&A
WebGL 中 attribute 和 uniform 有什么区别?
attribute 是只能由顶点着色器接收的数据,每个顶点传入不同的值;uniform 是从外部传入的固定数据,每个顶点都相同。
如何将 attribute 数据从 JavaScript 传递到顶点着色器?
步骤:1. 创建 buffer:gl.createBuffer();2. 绑定 buffer 到 ARRAY_BUFFER:gl.bindBuffer(gl.ARRAY_BUFFER, buffer);3. 写入数据:gl.bufferData(gl.ARRAY_BUFFER, data, usage);4. 获取 attribute 地址:gl.getAttribLocation(program, name);5. 重新绑定 buffer 并设置指针:gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.vertexAttribPointer(location, size, type, normalize, stride, offset);6. 启用 attribute:gl.enableVertexAttribArray(location)。
为什么在 WebGL 中 bindBuffer 操作容易被误解?
因为 bindBuffer 实际上是一个“激活”操作,它将指定的 buffer 绑定到目标(如 ARRAY_BUFFER),使其成为当前激活的 buffer,后续操作如 bufferData 和 vertexAttribPointer 都会作用于这个激活的 buffer。同一个目标同时只能有一个激活的 buffer。
如何向 GLSL 传递 uniform 矩阵数据?
使用 gl.uniformMatrix4fv(location, transpose, value) 方法,其中 location 是通过 gl.getUniformLocation 获取的 uniform 地址,value 是 Float32Array 类型的矩阵数据。类似地,其他 uniform 类型可以使用 uniform[1234][fi][v] 系列方法。
WebGL 中 sampler2D 类型的 uniform 如何传递?
步骤:1. 创建纹理对象:gl.createTexture();2. 绑定纹理:gl.bindTexture(gl.TEXTURE_2D, texture);3. 上传图像数据:gl.texImage2D(...);4. 激活纹理单元:gl.activeTexture(gl.TEXTURE0);5. 将纹理对象绑定到当前纹理单元:gl.bindTexture(gl.TEXTURE_2D, texture);6. 通过 gl.uniform1i(location, textureUnit) 将纹理单元索引传递给 sampler2D。注意:加载 sampler 只能使用 glUniform1i 或 glUniform1iv。
WebGL 中 texture unit 和 texture object 有什么区别?
texture unit 是用于处理纹理映射的单元,GPU 至少支持 8 个,可通过 gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) 查询数量;texture object 是存储纹理数据的对象,类似于 buffer object,但更复杂。传递 sampler2D 时需要先激活 texture unit,再将 texture object 绑定到该单元。