WebGL中三角形顶点间varying变量值的计算方式
WebGL中顶点间Varying变量的插值计算逻辑
我在WebGL代码中给顶点着色器传入了对应2个三角形6个顶点的attribute属性,通过varying变量将值传递到片元着色器,想了解顶点之间的varying变量值是如何计算的?以下是我的代码和通过readPixels获取的像素输出结果:
代码实现
顶点着色器
attribute vec2 a_position; attribute float a_number; varying float v_number; void main() { v_number = a_number; gl_Position = vec4(a_position, 0, 1); }
片元着色器
precision highp float; varying float v_number; void main() { gl_FragColor=vec4(v_number,0,0,0); }
JavaScript代码
"use strict"; var canvas = document.querySelector("#canvas"); canvas.width = 5; canvas.height = 5; var gl = canvas.getContext("webgl"); gl.getExtension('OES_texture_float'); var texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,5,5,0,gl.RGBA,gl.FLOAT,null); let program = gl.createProgram(); const vertexShader=gl.createShader(gl.VERTEX_SHADER); const fragmentShader=gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(vertexShader, vertex); gl.shaderSource(fragmentShader, fragment); gl.compileShader(vertexShader); gl.compileShader(fragmentShader); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); let numberBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,numberBuffer); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([1,2,3,4,5,6]),gl.STATIC_DRAW); let positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([ -1, -1, -1, +1, +1, -1, +1, -1, -1, +1, +1, +1, ]),gl.STATIC_DRAW); gl.useProgram(program); let numberLoc = gl.getAttribLocation(program,"a_number"); gl.enableVertexAttribArray(numberLoc); gl.bindBuffer(gl.ARRAY_BUFFER,numberBuffer); gl.vertexAttribPointer(numberLoc,1,gl.FLOAT,false,0,0); let positionLoc = gl.getAttribLocation(program,"a_position"); gl.enableVertexAttribArray(positionLoc); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.vertexAttribPointer(positionLoc,2,gl.FLOAT,false,0,0); gl.viewport(0, 0, 5, 5); var fb = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fb); gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D, texture, 0); gl.drawArrays(gl.TRIANGLES, 0, 6); let pixels = new Float32Array(5 * 5 * 4); gl.readPixels(0, 0, 5, 5, gl.RGBA, gl.FLOAT, pixels); console.log(pixels);
HTML代码
<canvas id="canvas"></canvas>
像素输出结果
4.9 5.1 5.3 5.5 5.7 1.9 4.7 4.9 5.1 5.3 1.7 2.1 4.5 4.7 4.9 1.5 1.9 2.3 4.3 4.5 1.3 1.7 2.1 2.5 4.1
问题解答
核心逻辑:重心坐标插值
WebGL对三角形覆盖的每个片元(像素),会根据片元在三角形内的重心坐标计算三个顶点的权重,再用权重对顶点的varying值做加权求和,得到该片元的varying值:
- 顶点位置的片元:对应顶点的权重为1,其余两个顶点权重为0,所以varying值等于该顶点的attribute值。
- 三角形边上的片元:权重由片元到两个顶点的距离决定,呈现线性插值效果。
- 三角形内部的片元:三个顶点的权重之和为1,通过加权计算得到最终的varying值。
结合示例分析
你绘制的两个三角形对应顶点数据:
- 三角形1:顶点
(-1,-1)(a_number=1)、(-1,+1)(a_number=2)、(+1,-1)(a_number=3),覆盖画布左下区域。 - 三角形2:顶点
(+1,-1)(a_number=4)、(-1,+1)(a_number=5)、(+1,+1)(a_number=6),覆盖画布右上及中间区域。
从输出结果可以验证:
- 右上角像素(4,4)对应三角形2的顶点
(+1,+1),varying值接近6(输出5.7,属于浮点精度和viewport映射的正常误差)。 - 左下角像素(0,0)对应三角形1的顶点
(-1,-1),varying值接近1(输出1.3)。 - 中间像素(2,2)处于三角形2内部,通过重心坐标加权后得到4.5,和输出结果完全匹配。
补充说明
- 正交投影场景下,重心插值等价于线性插值;如果是透视投影,WebGL会自动执行透视校正插值,保证纹理、颜色等在透视场景下的显示正确性。
- 输出中的小数偏差,是WebGL的viewport坐标映射、浮点运算精度导致的,属于正常现象。
内容的提问来源于stack exchange,提问作者oceanGermanique
相关产品推荐
相关产品推荐

