为了账号安全,请及时绑定邮箱和手机立即绑定

WebGL:使用不同的程序渲染大量对象

WebGL:使用不同的程序渲染大量对象

慕妹3242003 2022-07-08 17:43:50
我现在正在学习 WebGL。我用 10 个三角形制作了一个简单的场景,当我将三角形数量增加到 1000 个时,场景开始冻结。我使用 3 个着色器和 2 个程序(用于模拟真实环境)。我知道我应该从渲染循环体中取出一些东西,但我不知道是什么。我的代码如下:function render() {  requestAnimationFrame(render);  context.clear(context.COLOR_BUFFER_BIT);  for (let i = 0; i < 10; i++) {    const currentProgram = i % 2 === 0 ? blueProgram : redProgram;    context.useProgram(currentProgram);    const a_Position = context.getAttribLocation(currentProgram, "a_Position");    const triangleGeometry = getTriangleGeometry(); // returns Float32Array filled with randoms    const buffer = context.createBuffer();    context.bindBuffer(context.ARRAY_BUFFER, buffer);    context.bufferData(context.ARRAY_BUFFER, triangleGeometry, context.STATIC_DRAW);    context.enableVertexAttribArray(a_Position);    context.vertexAttribPointer(      a_Position,      2,      context.FLOAT,      false,      0,      0,    );    context.drawArrays(context.TRIANGLES, 0, 3);  }}requestAnimationFrame(render);任何想法我可以为性能优化做些什么?
查看完整描述

1 回答

?
慕无忌1623718

TA贡献1744条经验 获得超4个赞

有很多方法可以优化绘制很多东西,但由于 yoi 刚刚开始,最重要的是通常设置缓冲区应该发生在初始化时间,而不是渲染时间。

请参阅在 WebGL 中绘制多个模型

问题中的代码是查找每个三角形的位置。它应该将位置查找为初始时间。

该代码还为每个三角形创建一个新缓冲区。创建一个缓冲区并使用新三角形更新它会更快,当然最终它会耗尽内存来创建新缓冲区。

const context = document.querySelector('canvas').getContext('webgl');


const vs = `

attribute vec4 a_Position;

void main() {

  gl_Position = a_Position;

}

`;


const redFS = `

precision highp float;

void main() {

  gl_FragColor = vec4(1, 0, 0, 1);

}

`;


const blueFS = `

precision highp float;

void main() {

  gl_FragColor = vec4(0, 0, 1, 1);

}

`;


const blueProgram = twgl.createProgram(context, [vs, blueFS]);

const blueProgramInfo = {

  program: blueProgram,

  a_PositionLocation: context.getAttribLocation(blueProgram, "a_Position"),

};


const redProgram = twgl.createProgram(context, [vs, redFS]);

const redProgramInfo = {

  program: redProgram,

  a_PositionLocation: context.getAttribLocation(redProgram, "a_Position"),

};


const buffer = context.createBuffer();


function rand(min, max) {

  return Math.random() * (max - min) + min;

}


// pre allocate

const triangleData = new Float32Array(6);  // 3 vertices, 2 values per


function getTriangleGeometry() {

  const x = rand(-1, 1);

  const y = rand(-1, 1);

  triangleData[0] = x; 

  triangleData[1] = y;

  triangleData[2] = x + rand(-0.1, 0.1);

  triangleData[3] = y + rand(-0.1, 0.1);

  triangleData[4] = x + rand(-0.1, 0.1);

  triangleData[5] = y + rand(-0.1, 0.1);

  return triangleData;

}


function render() {

  context.clear(context.COLOR_BUFFER_BIT);


  for (let i = 0; i < 100; i++) {

    const currentProgramInfo = i % 2 === 0 ? blueProgramInfo : redProgramInfo;

    context.useProgram(currentProgramInfo.program);


    const a_Position = currentProgramInfo.a_PositionLocation;


    const triangleGeometry = getTriangleGeometry(); // returns Float32Array filled with randoms


    context.bindBuffer(context.ARRAY_BUFFER, buffer);

    context.bufferData(context.ARRAY_BUFFER, triangleGeometry, context.STATIC_DRAW);

    context.enableVertexAttribArray(a_Position);

    context.vertexAttribPointer(

      a_Position,

      2,

      context.FLOAT,

      false,

      0,

      0,

    );


    context.drawArrays(context.TRIANGLES, 0, 3);

  }

  requestAnimationFrame(render);

}


requestAnimationFrame(render);

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

<canvas></canvas>

问题中的代码似乎使用了 2 个程序,一个绘制蓝色,一个绘制红色。让一个带有制服的程序来选择颜色可能会更快。

const context = document.querySelector('canvas').getContext('webgl');


const vs = `

attribute vec4 a_Position;

void main() {

  gl_Position = a_Position;

}

`;


const fs = `

precision highp float;

uniform vec4 u_Color;

void main() {

  gl_FragColor = u_Color;

}

`;



const program = twgl.createProgram(context, [vs, fs]);

const programInfo = {

  program: program,

  a_PositionLocation: context.getAttribLocation(program, "a_Position"),

  u_ColorLocation: context.getUniformLocation(program, "u_Color"),

};


const buffer = context.createBuffer();


function rand(min, max) {

  return Math.random() * (max - min) + min;

}


// pre allocate

const triangleData = new Float32Array(6);  // 3 vertices, 2 values per


function getTriangleGeometry() {

  const x = rand(-1, 1);

  const y = rand(-1, 1);

  triangleData[0] = x; 

  triangleData[1] = y;

  triangleData[2] = x + rand(-0.1, 0.1);

  triangleData[3] = y + rand(-0.1, 0.1);

  triangleData[4] = x + rand(-0.1, 0.1);

  triangleData[5] = y + rand(-0.1, 0.1);

  return triangleData;

}


const blue = [0, 0, 1, 1];

const red = [1, 0, 0, 1];


function render() {

  context.clear(context.COLOR_BUFFER_BIT);


  context.useProgram(programInfo.program);

  

  const a_Position = programInfo.a_PositionLocation;

  context.bindBuffer(context.ARRAY_BUFFER, buffer);

  context.enableVertexAttribArray(a_Position);

  context.vertexAttribPointer(

    a_Position,

    2,

    context.FLOAT,

    false,

    0,

    0,

  );


  for (let i = 0; i < 100; i++) {

    const color = i % 2 === 0 ? blue : red;

    context.uniform4fv(programInfo.u_ColorLocation, color);

   

    const triangleGeometry = getTriangleGeometry(); // returns Float32Array filled with randoms

    context.bufferData(context.ARRAY_BUFFER, triangleGeometry, context.STATIC_DRAW);


    context.drawArrays(context.TRIANGLES, 0, 3);

  }

  requestAnimationFrame(render);

}


requestAnimationFrame(render);

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

<canvas></canvas>

如果在初始化时将所有三角形放在一个缓冲区中,并且在初始化时将每个三角形的顶点颜色放在缓冲区中,然后在渲染时使用单个绘制调用绘制它们,速度会明显更快。如果您想要每帧随机三角形,那么在初始化时创建一个缓冲区,在单个缓冲区中填充 N 个随机三角形,然后在一次绘制调用中将它们全部绘制出来,仍然会更快。

const context = document.querySelector('canvas').getContext('webgl');


const vs = `

attribute vec4 a_Position;

attribute vec4 a_Color;


varying vec4 v_Color;


void main() {

  gl_Position = a_Position;

  v_Color = a_Color;

}

`;


const fs = `

precision highp float;

varying vec4 v_Color;

void main() {

  gl_FragColor = v_Color;

}

`;



const program = twgl.createProgram(context, [vs, fs]);

const programInfo = {

  program: program,

  a_PositionLocation: context.getAttribLocation(program, "a_Position"),

  a_ColorLocation: context.getAttribLocation(program, "a_Color"),

  u_ColorLocation: context.getUniformLocation(program, "u_Color"),

};


const positionBuffer = context.createBuffer();

const colorBuffer = context.createBuffer();


function rand(min, max) {

  return Math.random() * (max - min) + min;

}


const numTriangles = 1000;


const positionData = new Float32Array(numTriangles * 3 * 2);  

const colorData = new Float32Array(numTriangles * 3 * 4);


const blue = [0, 0, 1, 1];

const red = [1, 0, 0, 1];


// the color data does not change so fill it out at init time

for (let i = 0; i < numTriangles; ++i) {

  const offset = i * 4;

  colorData.set(i % 2 === 0 ? blue : red, offset);

}

context.bindBuffer(context.ARRAY_BUFFER, colorBuffer);

context.bufferData(context.ARRAY_BUFFER, colorData, context.STATIC_DRAW);


function getTriangleGeometry() {

  for (let i = 0; i < numTriangles; ++i) {

    const offset = i * 3 * 2;  // 3 verts per tri, 2 values per ver

    const x = rand(-1, 1);

    const y = rand(-1, 1);

    positionData[offset    ] = x; 

    positionData[offset + 1] = y;

    positionData[offset + 2] = x + rand(-0.1, 0.1);

    positionData[offset + 3] = y + rand(-0.1, 0.1);

    positionData[offset + 4] = x + rand(-0.1, 0.1);

    positionData[offset + 5] = y + rand(-0.1, 0.1);

  }

  return positionData;

}



function render() {

  context.clear(context.COLOR_BUFFER_BIT);


  context.useProgram(programInfo.program);

  

  const a_Position = programInfo.a_PositionLocation;

  context.bindBuffer(context.ARRAY_BUFFER, positionBuffer);

  const triangleGeometry = getTriangleGeometry(); // returns Float32Array filled with randoms

  context.bufferData(context.ARRAY_BUFFER, triangleGeometry, context.DYNAMIC_DRAW);

  context.enableVertexAttribArray(a_Position);

  context.vertexAttribPointer(

    a_Position,

    2,

    context.FLOAT,

    false,

    0,

    0,

  );

  

  const a_Color = programInfo.a_ColorLocation;

  context.bindBuffer(context.ARRAY_BUFFER, colorBuffer);

  context.enableVertexAttribArray(a_Color);

  context.vertexAttribPointer(

    a_Color,

    4,

    context.FLOAT,

    false,

    0,

    0,

  );


  context.drawArrays(context.TRIANGLES, 0, numTriangles * 3);

  requestAnimationFrame(render);

}


requestAnimationFrame(render);

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

<canvas></canvas>

像上面那样在每一帧中绘制一堆随机三角形可以说是一个例外。大多数 WebGL 应用程序绘制在建模包中创建的 3D 模型,因此更常见的是在初始化时将数据放入缓冲区一次(如立方体、球体、汽车、人、树的数据),然后在渲染时绘制它。

另请注意,GPU 只能绘制这么多像素,因此如果您的三角形很大(如整个屏幕的大小),您将只能绘制 10 到几百个)。1920x1080 的屏幕约为 2 百万像素。所以每个全屏三角形也将是大约 200 万像素。绘制其中的 1000 个是 2000 * 200 万或 40 亿像素。每秒 60 帧 2400 亿像素。中高端 GPU 每秒只能绘制 100 亿帧,这是理论上的最大值,因此它最多只能以每秒 2 帧的速度进行绘制。

大多数 3D 应用程序绘制的场景中,大多数三角形都很远且很小。他们还使用深度缓冲区并从前到后绘制不透明对象,这样后面的像素就不会被绘制。


查看完整回答
反对 回复 2022-07-08
  • 1 回答
  • 0 关注
  • 97 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号