You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WebGL技术问询:如何统计画布中渲染的顶点数量

好问题!要通过原生WebGL统计渲染的顶点、点和线数量,确实需要一些手动追踪的手段,先帮你理清思路,再解决你遇到的Three.js异常结果问题。

原生WebGL统计渲染顶点/图元的方法

WebGL本身并没有提供直接返回已渲染顶点数的API,但我们可以通过两种核心方式来追踪:

1. 手动拦截绘制调用(最可靠的顶点统计方式)

WebGL的所有渲染操作最终都会通过drawArraysdrawElementsdrawArraysInstanced等方法执行。我们可以封装这些方法,在每次调用时自动计算并累加顶点和图元的数量。

示例代码:

// 初始化统计容器,按需扩展
const renderStats = {
  totalVertices: 0,
  points: 0,
  lines: 0,
  triangles: 0
};

// 封装drawArrays
const originalDrawArrays = gl.drawArrays;
gl.drawArrays = function(mode, first, count) {
  // 累加处理的顶点数
  renderStats.totalVertices += count;

  // 根据绘制模式计算对应图元数
  switch(mode) {
    case gl.POINTS:
      renderStats.points += count;
      break;
    case gl.LINES:
      renderStats.lines += Math.floor(count / 2);
      break;
    case gl.LINE_STRIP:
      renderStats.lines += count - 1;
      break;
    case gl.LINE_LOOP:
      renderStats.lines += count;
      break;
    case gl.TRIANGLES:
      renderStats.triangles += Math.floor(count / 3);
      break;
    case gl.TRIANGLE_STRIP:
    case gl.TRIANGLE_FAN:
      renderStats.triangles += count - 2;
      break;
  }

  // 调用原始方法完成渲染
  originalDrawArrays.call(this, mode, first, count);
};

// 同理封装drawElements(处理索引绘制)
const originalDrawElements = gl.drawElements;
gl.drawElements = function(mode, count, type, offset) {
  renderStats.totalVertices += count; // 每个索引对应一个被处理的顶点

  // 图元计算逻辑和drawArrays一致
  switch(mode) {
    case gl.POINTS:
      renderStats.points += count;
      break;
    case gl.LINES:
      renderStats.lines += Math.floor(count / 2);
      break;
    case gl.LINE_STRIP:
      renderStats.lines += count - 1;
      break;
    case gl.LINE_LOOP:
      renderStats.lines += count;
      break;
    case gl.TRIANGLES:
      renderStats.triangles += Math.floor(count / 3);
      break;
    case gl.TRIANGLE_STRIP:
    case gl.TRIANGLE_FAN:
      renderStats.triangles += count - 2;
      break;
  }

  originalDrawElements.call(this, mode, count, type, offset);
};

// 封装实例化绘制方法(比如drawArraysInstanced)
if (gl.drawArraysInstanced) {
  const originalDrawArraysInstanced = gl.drawArraysInstanced;
  gl.drawArraysInstanced = function(mode, first, count, instanceCount) {
    const totalVertices = count * instanceCount;
    renderStats.totalVertices += totalVertices;

    let primitiveCount;
    switch(mode) {
      case gl.POINTS:
        primitiveCount = totalVertices;
        renderStats.points += primitiveCount;
        break;
      case gl.LINES:
        primitiveCount = Math.floor(count / 2) * instanceCount;
        renderStats.lines += primitiveCount;
        break;
      case gl.LINE_STRIP:
        primitiveCount = (count - 1) * instanceCount;
        renderStats.lines += primitiveCount;
        break;
      case gl.LINE_LOOP:
        primitiveCount = totalVertices;
        renderStats.lines += primitiveCount;
        break;
      case gl.TRIANGLES:
        primitiveCount = Math.floor(count / 3) * instanceCount;
        renderStats.triangles += primitiveCount;
        break;
      case gl.TRIANGLE_STRIP:
      case gl.TRIANGLE_FAN:
        primitiveCount = (count - 2) * instanceCount;
        renderStats.triangles += primitiveCount;
        break;
    }

    originalDrawArraysInstanced.call(this, mode, first, count, instanceCount);
  };
}

使用时,只要在WebGL上下文创建后、任何渲染操作执行前完成封装,之后就可以通过renderStats对象获取累计的统计数据了。

2. 使用WebGL查询对象(统计图元数)

如果只需要统计生成的图元数量(比如三角形、线的数量),可以使用WebGL的查询对象(Query Objects),它能直接从GPU获取渲染过程中生成的图元数:

// 创建查询对象
const primitiveQuery = gl.createQuery();

// 开始查询:统计本次绘制生成的图元数
gl.beginQuery(gl.QUERY_PRIMITIVES_GENERATED, primitiveQuery);

// 执行你的绘制操作
gl.drawArrays(gl.TRIANGLES, 0, 9);

// 结束查询
gl.endQuery(gl.QUERY_PRIMITIVES_GENERATED);

// 异步等待查询结果(避免阻塞CPU)
function checkQueryResult() {
  if (gl.getQueryParameter(primitiveQuery, gl.QUERY_RESULT_AVAILABLE)) {
    const triangleCount = gl.getQueryParameter(primitiveQuery, gl.QUERY_RESULT);
    console.log(`生成的三角形数:${triangleCount}`);
  } else {
    requestAnimationFrame(checkQueryResult);
  }
}
checkQueryResult();

注意:这种方法只能获取图元数,无法直接得到顶点数,适合只关心图元数量的场景。


关于Three.js的异常小数三角形数

你提到renderer.info.render显示10,134.3个三角形,这通常是因为:

  • 某次绘制调用的顶点数不是图元所需的整数倍(比如TRIANGLES模式下,顶点数不是3的倍数,计算时会出现小数);
  • 使用了实例化渲染(Instanced Rendering),Three.js在统计时将基础三角形数乘以实例数,若实例数是小数(虽然理论上实例数是整数,但可能内部计算精度问题)或者基础三角形数不是整数(比如动态生成的几何体有部分未完成的图元),就会出现这种情况。

如果要验证,可以查看对应场景中是否有使用实例化网格,或者检查几何体的顶点数是否为3的倍数。

内容的提问来源于stack exchange,提问作者duhaime

火山引擎 最新活动