OpenGL3.3_C++_Windows(20)

demo演示

1
2

实例化 VS 批处理

 理解一次绘制的顺序(每帧): 

  1. 顶点数据布局,所有顶点分组(分为一个个顶点,每个顶点包含顶点属性)
  2. 绑定着色器,表明接下来要使用哪个着色器program
  3. uniform / uniform buffer传入glsl中,配置好glsl所有数据
  4. 调用DrawCall()时,绑定VAO,根据在VAO查找每个顶点offest 以及 每个顶点属性offest,每个顶点调用一次绑定的着色器program,所有顶点依次渲染

 理解多次绘制的顺序(每帧): 

  1. 首先,为什么大量绘制物体,要调用这么多次DrawCall()?
  2. 因为我们定义的顶点数组只是一个数组,每次调用DrawCall(),仅绘制了这个数组中的所有顶点,若想大量绘制物体,需要多次调用。
  3. 过程:
  4. ……   34步:for(){……DrawCall()}
  5. for中每次调用DrawCall()时,都会重新进行glsl配置(非必须如果想位置相同的话,完全一致),绑定VAO,顶点依次渲染

 非实例化渲染函数缺点:

  1. CPU -> GPU的通信:
  2. 当每次将VAO中的数据,传输到glsl(GPU),都要告诉GPU该从哪个缓冲读取数据,从哪寻找顶点属性……)
  3. 比如绘制大量同样图元,传入相同的顶点数据,每次DrawCall(),都要重新 通信/查找
  4. 注意:
  5. 影响性能的非渲染,GPU并行渲染计算速度非常快,GPU一直在等待CPU
  6. 当所有图元的顶点数据都一致,可以用实例化渲染函数(),仅调用一次DrawCall Instanced(),需要一个额外的参数,叫做实例数量(Instance Count)
  7. 本质:
  8. 原来的 DrawCall()仅可以绘制一组顶点数据,这回调用一次渲染函数,可以绘制指定数量的(一组顶点数据)
  9. 但是目前绘制都是相同的,也就是位置一致,如何将每个实体放到不同的位置?
    1. 利用内建变量:使用实例化渲染调用时,GLSL在顶点着色器中嵌入了另一个内建变量,gl_InstanceID。gl_InstanceID会从0开始,在每个实例被渲染时递增1
    2. 利用uniform【】,传入每个图元偏移量offest,对于同一个gl_InstanceID图元,的所有顶点的offest都是相同的
  10. 但是:

  11. uniform数据大小有上限:当绘制大量实体时,代替方案是实例化数组
    1. 首先我们将offest定义为一个顶点属性(能够让我们储存更多的数据),
    2. 新建VBO存储到,AttribPointer其中一个属性指针上
    3. glVertexAttribDivisor(顶点属性,属性除数)
    4. 属性除数默认为0,表示每几个实例更新顶点属性。

 批处理:看看Hazel引擎架构如何进行批处理的

  1. 首先什么是批处理?核心思想是将多个图元组合成一个大的图元进行绘制,从而减少OpenGL的绘图调用次数,以及状态的切换
  2. 也就是init->drawAPI->renderer,非交错布局 111->222->333
  3. 绘制不同实体,都要经历下面的过程
    1. 建立变量:struct表示实体顶点属性,数组【struct】每个顶点的属性(每个顶点struct都会添加到数组),ptr指针负责记录所有顶点添加完成后,最后ptr更新到的位置,int IndexCount记录所有顶点数目。
    2. init():对不同的VAO(VBO,EBO,layout),shader,初始化,和我们正常绘制init是一样的
    3. 调用Draw():传入每个顶点属性,添加到 数组【struct】,ptr后移,顶点个数+=;
    4. renderer:
      1. 通过glBufferSubData()一点点填充buffer,其中offest设置为0,覆盖原有的数据(不用担心这一次顶点数量变少,有ptr重新更新,会记录本次的位置)
      2. ptr- 数组起点 =  当前顶点数据(非索引数,顶点个数 !=索引个数)
      3. bind()需要的各种状态
      4. glDrawElements()绘制所有顶点数据

 静态/离线合批 batching VS 动态/实时合批 batching VS  实例化instancing:参考

  1. 合批和实例化是两种强大的渲染优化技术,获得最佳的渲染性能,
  2. 减少DrawCall(是指CPU向GPU发送的一次绘制命令,cpu会将顶点数据传入显存,之后由显卡进行绘制,,以及减少GPU切换渲染状态的次数
  3. 合批:
    1. 显著减少Draw Call的数量
    2. 静态合批:在非运行时间进行,提前准备好合并后的网格并存储在文件中,Mesh合并不占用运行时效率,但它会占用一定的CPU和带宽资源
    3. 运行时顶点、索引信息也不会发生变化,所以无需CPU消耗算力维护,减少了DrawCall()的次数
    4. 引擎中:静态本质就是对标记为static的Mesh自动合并
    5. 动态合批:在游戏运行时相关资源做合批处理,每一帧都进行一遍的Mesh合并,用复制数据的性能消耗换取提交Drawcall()的性能消耗,需要消耗CPU资源进行实时计算和带宽资源
  4. 实例化:
    1. 只提交一个模型网格让GPU绘制很多个地方,通知显卡在绘制这些顶点时,重复绘制的次数。
    2. 重复绘制大量相同或相似物体的场景
  5. 静态合批 > Instancing > 动态合批(负优化)

unsigned int amount = 1000;//总数
glm::mat4 *modelMatrices;
modelMatrices = new glm::mat4[amount];//变换矩阵数组
srand(glfwGetTime()); // 初始化随机种子    
float radius = 50.0;//半径
float offset = 2.5f;//偏移量
for(unsigned int i = 0; i < amount; i++)//求每个随机的变换矩阵
{
    glm::mat4 model;
    //位移:求xyz,偏移的范围是 [-offset, offset]
    float angle = (float)i / (float)amount * 360.0f;//角度,将360分成实体个数份,【0——360】°
    float displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;//(rand % 500)约束到0--499,约等/100(0--5),-offset[-2.5-2.5]
    float x = sin(angle) * radius + displacement;//angle(0,90,180,360,0)->sin(angle)(0,1,0,-1,0) * 50(0,50,0,-50,0),+随机[-2.5-2.5]
    displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;//rand会更新
    float y = displacement * 0.4f; // [-2.5-2.5] * 0.4
    displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
    float z = cos(angle) * radius + displacement;angle(0,90,180,360,0)->cos(1,0,-1,0,1),也就是从+z开始,逆时针的矩阵
    model = glm::translate(model, glm::vec3(x, y, z));//获得随机位置

    //缩放:
    float scale = (rand() % 20) / 100.0f + 0.05;
    model = glm::scale(model, glm::vec3(scale));

    //旋转:
    float rotAngle = (rand() % 360);
    model = glm::rotate(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f));

    //添加
    modelMatrices[i] = model;
}  

 制大量实例:

  1. 创建glm::mat4变换数组【】(相对于圆环),存储每个实体的变换(随机旋转,缩放,偏移)
  2. 对每个model 的meshes的VAO layout

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值