You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,-1)(a_number=1)、(-1,+1)(a_number=2)、(+1,-1)(a_number=3),覆盖画布左下区域。
  2. 三角形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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 11:44:56