您应该如何有效地批处理复杂的网格?

2024-04-10

渲染复杂网格的最佳方法是什么?我在下面写了不同的解决方案,想知道您对它们有何看法。

让我们举个例子:如何渲染“Crytek-Sponza”网格?

PS:我不使用Ubershader,只使用单独的着色器

如果您通过以下链接下载网格:

http://graphics.cs.williams.edu/data/meshes.xml

并将其加载到 Blender 中,您将看到整个网格由大约 400 个分别具有自己的材质/纹理的子网格组成。

虚拟渲染器(版本 1)将分别渲染 400 个子网格中的每一个!这意味着(为了简化情况)400 个绘制调用,每个调用都绑定到材质/纹理。对性能非常不利。非常慢!

pseudo-code version_1:

foreach mesh in meshList //400 iterations :(!
 mesh->BindVBO();

  Material material = mesh->GetMaterial();
  Shader bsdf = ShaderManager::GetBSDFByMaterial(material);

  bsdf->Bind();
   bsdf->SetMaterial(material);
   bsdf->SetTexture(material->GetTexture()); //Bind texture

    mesh->Render();

现在,如果我们处理正在加载的材料,我们可以注意到 Sponza 实际上仅由(如果我记忆力好的话:)) 25 种不同的材料组成!

因此,更聪明的解决方案(版本 2)应该是批量收集所有顶点/索引数据(在我们的示例中为 25 个),而不是将 VBO/IBO 存储到子网格类中,而是存储到名为 Batch 的新类中。

pseudo-code version_2:

foreach batch in batchList //25 iterations :)!
  batch->BindVBO();

  Material material = batch->GetMaterial();
  Shader bsdf = ShaderManager::GetBSDFByMaterial(material);

  bsdf->Bind();
   bsdf->SetMaterial(material);
   bsdf->SetTexture(material->GetTexture()); //Bind texture

    batch->Render();

在这种情况下,每个 VBO 包含共享完全相同的纹理/材质设置的数据!

好多了!现在我认为 25 个 VBO 来渲染 Sponza 太多了!问题是渲染海绵的缓冲区绑定数量!我认为一个好的解决方案应该是,如果第一个 VBO 已“满”,则分配一个新的 VBO(例如,我们假设 VBO 的最大大小(在 VBO 类中定义为属性的值)为 4MB 或 8MB)。

pseudo-code version_3:

foreach vbo in vboList //for example 5 VBOs (depends on the maxVBOSize)

 vbo->Bind();

 BatchList batchList = vbo->GetBatchList();

 foreach batch in batchList

  Material material = batch->GetMaterial();
  Shader bsdf = ShaderManager::GetBSDFByMaterial(material);

  bsdf->Bind();
   bsdf->SetMaterial(material);
   bsdf->SetTexture(material->GetTexture()); //Bind texture

    batch->Render();

在这种情况下,每个 VBO 不包含共享完全相同的纹理/材质设置的必要数据!这取决于子网格加载顺序!

好吧,VBO/IBO 绑定减少了,但绘制调用不一定减少! (您对此确认还满意吗?)。但总的来说,我认为这个版本 3 比前一个版本更好!你怎么看待这件事 ?

另一个优化应该是将 Sponza 模型的所有纹理(或纹理组)存储在纹理数组中!但如果你下载 sponza 包,你会看到所有纹理都有不同的大小!所以我认为由于格式差异,它们不能绑定在一起。

但如果可能的话,渲染器的第 4 版应该仅使用更少的纹理绑定,而不是整个网格的 25 个绑定!你认为这可能吗?

那么,根据您的说法,渲染斯庞扎网格的最佳方法是什么?您还有其他建议吗?


你专注于错误的事情。有两种方式。

首先,没有理由不坚持all将网格体的顶点数据放入单个缓冲区对象中。请注意,这有nothing与批处理有关。请记住:批处理是关于数量绘图调用,而不是您使用的缓冲区数量。您可以从同一缓冲区渲染 400 个绘制调用。

您似乎想要拥有的这个“最大尺寸”是fiction,完全不基于现实世界。如果你想要的话,你可以拥有它。只是不要指望它会让你的代码更快。

因此,在渲染此网格时,根本没有理由切换缓冲区。

其次,批处理实际上与绘制调用的数量无关(在 OpenGL 中)。这实际上是关于状态改变的成本between绘制调用。

这个视频清楚地说明了(大约31分钟) https://www.youtube.com/watch?v=-bCeNzgiJ8I,不同状态变化的相对成本。发出两个绘制调用且它们之间没有状态变化是很便宜的(相对而言)。但不同类型的状态变化具有不同的成本。

更改缓冲区绑定的成本非常小(假设您正在使用单独的顶点格式 https://www.opengl.org/wiki/Separate_Attribute_Format,因此更改缓冲区并不意味着更改顶点格式)。改变的成本programs甚至纹理绑定也更大。因此,即使您必须创建多个缓冲区对象(同样,您也不必这样做),这也不会成为主要瓶颈。

因此,如果性能是您的目标,那么您最好关注昂贵的状态更改,而不是便宜的状态更改。制作一个可以处理整个网格的所有材质设置的着色器,这样您只需更改它们之间的制服即可。使用数组纹理,这样您就只有一次纹理绑定调用。这会将纹理绑定转变为统一设置,这是一种更便宜的状态更改。

您甚至可以做一些更奇特的事情,包括基本实例计数等。但对于像这样的小例子来说,这有点过分了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

您应该如何有效地批处理复杂的网格? 的相关文章

  • 着色器/矩阵问题 - 看不到对象

    我试图在屏幕上放置一个立方体并点亮它 我想要在立方体上添加 phong 阴影 当我运行代码时 我可以看到背景图像 但看不到立方体 我相当确定立方体本身是正确的 因为我已经设法用纯色着色器显示它 我已经设法编译着色器程序 但我根本看不到立方体
  • 如何以编程方式在 qml 中渲染 vtk 项目?

    到目前为止 我了解到我们在 QML 中有两个线程 我们的主应用程序线程和我们的 场景图 线程 http doc qt io qt 5 qtquick visualcanvas scenegraph html http doc qt io q
  • OpenGL 将着色器附加到程序

    有没有办法访问附加到程序的着色器 也就是说 给定一个程序 我可以做类似的事情 vertexShader getVertexShaderFromProgram program 我想在验证我的程序的函数中记录着色器编译状态 但我只保留对程序的引
  • 哪个对缓存最友好?

    我试图很好地掌握面向数据的设计以及如何在考虑缓存的情况下进行最佳编程 基本上有两种情况我无法完全确定哪个更好以及为什么 是拥有一个对象向量更好 还是拥有对象原子数据的多个向量更好 A 对象向量示例 struct A GLsizei mInd
  • 在 OpenGL 中,为什么 glVertexAttribPointer 要求“指针”参数以 void* 形式传入?

    规格为glVertexAttribPointer如下 void glVertexAttribPointer GLuint index GLint size GLenum type GLboolean normalized GLsizei s
  • OpenGL:调试“单通道线框渲染”

    我正在尝试实现论文 单通道线框渲染 它看起来很简单 但它给了我所期望的厚暗值 论文没有给出计算海拔高度的确切代码 所以我按照自己认为合适的方式进行了操作 代码应该将三个顶点投影到视口空间中 获取它们的 高度 并将它们发送到片段着色器 片段着
  • OpenGL z轴指向哪里?

    我正在尝试了解 OpenGL 坐标系 我到处都看到它被描述为右撇子 但这与我的经验不符 我尝试绘制一些形状和 3 d 对象 我发现 z 轴显然指向 屏幕 而 x 指向右侧 y 指向上方 这是左手坐标系的描述 我缺少什么 编辑 例如 http
  • 使用 glDrawElements 时在 OpenGL 核心配置文件中选取三角形

    我正在使用 glDrawElements 绘制三角形网格 并且希望能够使用鼠标单击来拾取 选择三角形 三角形的网格可以很大 在固定功能 OpenGL 中 可以使用 GL SELECT http content gpwiki org inde
  • 如何安装适用于 Windows C++ 的最新版本 OpenGL?

    我正在使用 Visual Studio 2010 运行 Windows 7 包含的 OpenGL 版本 include 是版本 1 1 我希望使用合理的当前版本 某种版本 3 或 4 我需要做什么才能达到该状态 OpenGL SDK 页面位
  • 简单的线框格式?

    我正在寻找一种用于线框模型的简单文件格式 我知道 VRML u3D 等 但这些对于我的需求来说似乎很重要 我的标准是 必须有明确的规格 要么是开放的 要么是非常完善 记录的 我只需要 想要 简单的模型 顶点和边 我不想处理面孔或物体 如果格
  • LibGDX - 着色器适用于桌面但不适用于 Android

    我编写了一个简单的程序 可以在 3D 环境中渲染球体 并根据球体周围的四个光源为其着色 当我在桌面上运行该程序时 它工作得很好 但在 Android 设备上 球体只是纯色的 下面是一些图片来说明我正在谈论的内容 gt Desktop gt
  • 使用 GLSL 着色器在同一片段着色器中定义的多个子例程类型无法正常工作

    我正在开发一个使用 GLSL 着色器的程序 我编写了 2 种不同的方法来用 2 种不同的方法计算 ADS 环境光 漫反射 镜面反射 着色 为了正确完成这项工作 我使用子例程来使用一种或另一种方法来计算 ADS 着色 这是片段着色器代码的一部
  • 对齐坐标系

    Let s say I have 2 coordinate systems as it is shown in image attached 如何对齐这个坐标系 我知道我需要将第二个坐标系围绕 X 平移 180 度 然后将其平移到第一个坐标
  • OpenGL 与 Eclipse CDT + MinGW + GLEW + GLFW:未定义的参考

    Edit 与此同时 我已经弄清楚了这一点 并在下面写了详细的答案 我刚刚尝试在 Win7 上从 Express 版本的 MSVC 10 切换到 Eclipse CDT 在配置时遇到了以下简单 OpenGL 代码的问题 在 Visual St
  • NV_path_rendering替代方案[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我刚刚观看了 Siggraph 2012 的一个非常令人印象深刻的演示 http nvidia fullviewmedia com sig
  • 为什么OpenGL使用float而不是double? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • PyQt5 的 OpenGL 模块和版本控制问题(调用不正确的 _QOpenGLFunctions_(ver))

    我一直在努力得到PyQt5 helloGL 示例代码 https github com baoboa pyqt5 blob master examples opengl hellogl py编译 当我尝试构建解决方案时 我得到 Traceb
  • WebGL - 如何传递无符号字节顶点属性颜色值?

    我的顶点由具有以下结构的数组组成 Position colour float float float byte byte byte byte 传递顶点位置没有问题 gl bindBuffer gl ARRAY BUFFER this vbo
  • 无法在 WSL2 上运行 OpenGL

    我尝试在 WSL2 上运行 OpenGL 代码 但在尝试运行可执行文件时出现以下错误 GLFW error 65543 GLX Failed to create context GLXBadFBConfig Unable to create
  • 在 GLUT 中使用鼠标滚轮

    我想在 OpenGL GLUT 程序中使用鼠标滚轮来放大和缩小场景 我怎么做 Freeglut 的 glutMouseWheelFunc 回调与版本相关 并且在 X 中不可靠 使用标准鼠标功能并测试按钮 3 和 4 OpenGlut 对 g

随机推荐

  • 如何强制执行新的空 EF 迁移?

    好的 所以我完全依赖我的迁移和种子代码来维护所有数据库结构和初始数据 因此 我面临的情况是 我在此版本中所做的所有更改都是直接在数据库 存储过程和更新 上进行的 并且 C 代码本身没有任何更改 问题是 由于我想使用新的迁移来执行这些数据库特
  • 生产中的 GWT 源映射

    GWT 支持超级开发模式下的源映射 不幸的是 尽管我在 gwt xml 文件中添加了源映射选项 但它们似乎无法在生产模式下工作 如何在那里启用它们 看看 GWT 自己的网站是如何做到这一点的 https gwt googlesource c
  • 如何离线安装Flask? [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我已经在我的电脑上下载了 Flask 然后我就断开了连接 现在我需要在没有互联网连接的情况下安装 Flask 离线安装 Flask 还需要什
  • Wildfly 中有多个持久单元?

    Wildfly 9 0 2 应用程序中是否可以有两个持久性单元 我收到 WFLYJPA0061 未指定持久性单元名称 并且应用程序部署部署 jasper web war 中有 2 个持久性单元定义 要么将应用程序部署更改为只有一个持久性单元
  • Java 中使用分隔符“.”的分词问题

    我需要使用分隔符分割文本 例如我想要这个字符串 Washington is the U S Capital Barack is living there 分为两部分 Washington is the U S Capital Barack
  • Spring Security 中具有密码授予的 oAuth2 客户端

    我正在使用一组受 oAuth2 保护的服务 目前的工作原理如下 客户端使用用户名和密码登录 我用这些换取代币 我将令牌保留在会话中 并在每次想要调用服务时提交它 它可以工作 但问题是我完全手动执行此操作 而没有使用 Spring Secur
  • 使用命令中断循环

    在我的 Python Discord Bot 中 我想创建一个命令 这会导致循环运行 当我输入第二个命令时 循环应该停止 那么粗略地说 client event async def on message message if message
  • Node.js 中多线程处理文件

    我需要从在线 url 读取文件并在多线程中处理它并将其写入另一个输出中 https www w3 org TR PNG iso 8859 1 txt https www w3 org TR PNG iso 8859 1 txt我尝试过的解决
  • 如何测量图像上绘制的线的长度? C#

    我想编写一个应用程序来测量在显微镜下检查的样本碎片 我认为最好的方法是捕获图像并在样本的选定部分上绘制 然后以像素为单位计算绘制线的值 然后将该值转换为适当的单位 是否有任何东西可以帮助解决已经实现的此类问题 或者有任何工具 包或允许此类计
  • C# LINQ 用有意义的字符串替换空值

    从列表中 class Delivery public string ProductCode get set public DateTime OrderedDate get set public DateTime DeliveryDate g
  • 如何在 iPhone 上运行的应用程序中浏览核心数据?

    我正在开发一个使用核心数据的 iPhone 项目 并试图找到一种方法来轻松浏览数据的存储方式 具体来说 我将图像存储在核心数据中 并想查看它们占用了多少空间等 我发现了一个名为 核心数据编辑器 的应用程序 可以让你在 iPhone 模拟器上
  • 将球体上的 3d 点转换为 UV 坐标

    我在球体上有一个 3d 点 想要将其转换为球体纹理上的 UV 点 有人可以指出正确的方向吗 我可以采用纯数学解决方案 Edit 我目前有这个 它不会返回正确的 UV 坐标 p 是球体上的 3d 点 mesh position 是球体的位置
  • Phoenix:订购查询集

    我 一个菜鸟 为了好玩而玩弄 Phoenix 框架并构建一个小型 Twitter 克隆 我一切正常 但是 我想按updated at字段 升序 正如您从 tweet controller 中看到的 我尝试过使用 order by 子句 但这
  • (MySQL) 在文件特定列中加载数据

    我在将数据加载到表的特定列时遇到问题 CSV 文件是动态构建的 具有默认字段 ID LAST REFRESH ALIAS1 和可能含有ALIAS2 至 ALIAS8 当前 CSV 仅包含 ALIAS1 4 MySQL 表包含 ID LAST
  • 在 64 位 Windows 计算机上使用 32 位 com 运行 32 位应用程序

    我有一个使用 C COM 对象的 C 应用程序 两者都构建在 32 位计算机上 现在我必须在 64 位机器上运行它们 我注册了COM对象 在计算机 hkey classes root wow6432node clsid xxx 下的寄存器中
  • C++ 中的嵌套 Lambda 捕获 [重复]

    这个问题在这里已经有答案了 我有类似的东西 think of Synonym as a set vector of values the purpose of this function is to filter out elements
  • Javascript 函数显示所遵循城市的路线

    我正在 codewars com 上练习编码 我碰到这个问题 https www codewars com kata 5899a4b1a6648906fe000113 train javascript 我们正在追踪我们的流氓特工马修 奈特
  • WPF 多线程

    我正在绞尽脑汁地尝试让多线程在 WPF 中按照我想要的方式工作 我有一个名为 Manager 的对象 带有单例 它执行大量处理和查找 我希望它在与 UI 分开的线程中运行 UI 将调用 Manager 上的方法来执行 UI 应该做出反应的处
  • 使用.net 2.0 连接到 FTP 服务器 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我希望连接到现有的 FTP 服务器 上传文件 等待服务器生成报告 然后将该报告下载回 VB NET 2
  • 您应该如何有效地批处理复杂的网格?

    渲染复杂网格的最佳方法是什么 我在下面写了不同的解决方案 想知道您对它们有何看法 让我们举个例子 如何渲染 Crytek Sponza 网格 PS 我不使用Ubershader 只使用单独的着色器 如果您通过以下链接下载网格 http gr