在理想情况下,如果屏幕分辨率为1024x768,那么在给定时刻只需要786432 texel,2兆字节的内存就足够了。但在现实世界中,有管理成本,所以纹理需要更多的内存。
纹理流可以降低纹理的内存成本,也就是说,在给定时刻并不需要所有纹理的mipmap。纹理需要0级mipmap,因为它靠近相机,如果它离当前相机很远,5到11级mipmap可能就足够了。相机在场景中移动一段时间,一些mipmap可以加载,一些mipmap可以卸载。
我的问题是如何有效地做到这一点。
假设我在场景中有一个512x512 OpenGL纹理,所以它将有10个mipmap。从0级到9级,有512x512、256x256、128x128。。。和1x1 mipmap。简单地上传如下数据:
glBindTexture(GL_TEXTURE_2D, texId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, p1);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, p2);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, p3);
...
glBindTexture(GL_TEXTURE_2D, 0);
过了一段时间,摄影机将远离场景中的该纹理,64x64 mipmap就足够了,因此将卸载前3个mipmap:
glBindTexture(GL_TEXTURE_2D, texId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, p4);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, p5);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, p6);
...
glBindTexture(GL_TEXTURE_2D, 0);
然后,相机移动到这个纹理,需要256x256 mipmap:
glBindTexture(GL_TEXTURE_2D, texId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, p4);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, p5);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, p6);
...
glBindTexture(GL_TEXTURE_2D, 0);
这就是效率低下。基本上,它每次都会重新创建纹理,尽管纹理id没有改变。PBOs可以加快速度,但数据复制仍然需要成本。
对于1024x1024 OpenGL纹理,我是否可以让它只使用较低的mipmap,比如级别1到9,并将级别0的mipmap留空(不在视频内存中分配)?换句话说:始终将MIPMAP的子集从低级别保留到高级别。加载或卸载较高级别的mipmap,而不更改纹理的较低mipmap。我认为从硬件的角度来看,这是可能的。
这是我尝试的:如果我不为0级mipmap调用glTexImage2D,则此纹理可能处于不完整状态。但如果我使用空数据指针调用glTexImage2D,它将在视频内存中分配零数据(通过gDEBugger进行分析)。
所以纹理需要更多的记忆。
实际上没有。(编辑)对于一维纹理,第0个mipmap级别消耗N个字节,第一个N/2,第二个N/4,依此类推。所以总的字节消耗量是
sum(i in 0…){ N * 2^-i } = N * sum(i in 0…){2^-i}
这是一个收敛到2的几何级数。因此,mipmapped纹理消耗的内存是未mipmapped纹理的两倍。对于2D纹理,它是1/4、1/16等等。纹理的维数越大,mipmap开销越小。
这不是“很多”。
纹理需要0级mipmap,因为它靠近相机,如果它远离当前相机,5到11级mipmap可能就足够了。
OpenGL中没有摄像头,mipmapping级别也不是这样确定的。使用的Mipmap级别由屏幕坐标中纹理坐标的变化率(纹理坐标的梯度)决定。
过了一段时间,相机远离场景中的纹理,64x64 mipmap就足够了
也许在使用浅纹理坐标渐变绘制基本体之后,使用相同的纹理在具有非常陡峭的纹理坐标渐变(低mipmap级别)的基本体上进行渲染。
但在现实世界中,需要管理成本,
我想说的是,与简单地加载所有mipmap并一天调用相比,尝试正确地流式传输mipmap的管理开销要高得多,并且消耗更多的GPU资源。
此外,现代GPU能够自行获取数据;图形RAM只是系统RAM的缓存,当加载纹理时,它实际上不会首先进入图形内存。GPU将获取所需的数据。
如果您的目标是限制可用的活动纹理内存量,那么OpenGL 4.5中的任何内容都无法帮助您做到这一点。将纹理重新分配为较小的纹理是一个糟糕的想法(不,虚幻引擎不会这样做)。
有两种情况下,你所说的可能很重要。案例1是虚幻引擎使用它的目的:加载性能。它分配整个纹理及其所有mipmap,但只首先加载较低的mipmap。这样可以节省在水平仪中加载的时间。这也可以通过做同样的事情来提高流媒体性能。
这在OpenGL 4.5中很容易完成。这就是mipmap范围设置的目的;您只加载较低的mipmap并将GL_TEXTURE_BASE_LEVEL
和GL_TEXTURE_MAX_LEVEL
设置为该范围。OpenGL保证它不会尝试访问该范围之外的内存。
但是动态地从内存中删除mipmap并不是OpenGL 4.5的机制。
然而,ARB_稀疏_纹理确实如此。它允许您声明某些mipmap是“稀疏的”。您可以按正常方式为它们分配存储,但可以声明较高级别的内存可能会被逐出。您可以通过提供虚拟页面来决定级别何时可用。你可以删除这些页面,这样GPU就可以为其他人使用这些内存。
从理论上讲,这样做的目的是能够在不耗尽GPU内存的情况下使用更多纹理(并因此产生抖动)。然而,你并没有真正的工具来有效地做到这一点。
这是因为OpenGL不会告诉你有多少可用内存。它也没有说明为缓冲区/纹理分配了多少内存。它也没有说明这些分配的缓冲区/纹理中有多少当前处于活动状态并驻留在GPU上。所以你真的不知道什么时候你就要挨揍了。
这意味着,如果您“接近”许多大型纹理,您仍然可以突破内存限制。OpenGL不会告诉您何时这样做。
即便如此,如果你围绕它计划关卡布局,这仍然很有帮助。哦,稀疏纹理对虚幻引擎也很有用。
我在想,OpenGL纹理的每个mipmap都是独立存储的,我们可以在运行时将较低级别的mipmap附加到纹理,为什么不能附加较高级别的mipmap呢。
不要错误地认为硬件遵循API所说的。
您只能对第0层以外的mipmap发出glTexImage*D
调用吗?是的;只需使用基本/最大级别来防止访问超出分配的范围(这将保持纹理完整)。
这是否能保证实现只为那些特定的mipmap级别分配内存?否;实现可能会在该范围之外分配mipmap级别。
事实上,有证据表明它不是那样工作的。考虑ARB_纹理_存储。这个扩展是OpenGL 4.3的核心,它提供了一个函数,可以一次分配纹理的所有mipmap级别。因此,不是为每个级别调用glTexImage2D
,而是调用glTexStorage2D
一次,它将为指定的大小分配所有指定的mipmap级别。你可以在小范围内留下一些,但不能在顶部留下。
这也使纹理不可变,因此您无法再次更改该纹理的存储。您可以上传到它,但不能在上面调用glTexStorage*D
或glTexImage*D
。所以没有重新分配。
如果硬件实际上支持单个分配,为什么ARB会创建一个扩展,其全部目的是阻止您分配单个mipmap?如果您认为这是侥幸,也请考虑一下。
在创建ARB_direct_state_access时,他们显然添加了DSA风格的纹理分配函数。但请注意,他们没有添加DSA样式的函数来制作非不变的纹理;他们没有添加glTextureImage*D
。他们的理由是什么?“不变纹理是处理纹理的一种更稳健的方法”。
显然,ARB在API中看不到任何价值,让用户说出哪些mipmap被分配,哪些没有。
需要注意的是,Direct3D 12(一个更低级别的API)中也没有任何东西允许这样做。纹理需要多少内存的问题的答案纯粹是字节数对齐。它不是一系列字节数,每mipmap一个。分配资源的函数类似地不允许扩展mipmap或类似的东西。
我甚至查了Mantle的文件。它没有使图像的内存不连续的功能。grBindObjectMemory(将内存存储与纹理关联的函数)不接受指定mipmap级别的参数。
为了完整起见,Vulkan也没有。它将图像mipmap金字塔视为单个连续的存储块。稀疏图像可以工作,但在页面边界上工作,而不是在整个mipmap级别。
因此,我不会假设基于旧OpenGL API的硬件的性质。
加载纹理贴图集 可以使用Pixi的loader来加载纹理贴图集。如果是用Texture Packer生成的JSON,loader会自动读取数据,并对每一个帧创建纹理。下面就是怎么用loader来加载treasureHunter.json。当它成功加载,setup方法将会执行。 loader .add("images/treasureHunter.json") .load(setup); 现
我有一个从路径创建纹理的类,但当我尝试加载具有3个通道(rgb)的纹理时,它会在运行这一行时出现读取访问冲突异常 我试图改变stbi加载函数中的参数,但是没有用。 当我加载图像时,它设置了正确的宽度、高度和通道数据,所以我不知道我做错了什么 指向数据的指针也不是nullptr
嘿,我不能让我的纹理出现,我不知道怎么了。辅导没有帮助。这是我的代码: spriteX=0,spriteY=0,spriteW=400,spriteL=400,x=0,y=0 这是我得到的输出:输出窗口 正如你所见,黑色三角形显示得很好,但没有纹理
我一直试图让纹理在opengl 3.1下工作,在ubuntu 13.04上运行的intelHD图形2000/3000显卡上。我遇到的问题是纹理要么不加载,我试图纹理的基本三角形变成黑色,要么纹理中的一些颜色会加载,但不会加载整个图像。我使用原始图像文件作为源或使用libjpeg加载jpeg得到相同的结果。 我的着色器如下所示: 创建纹理glGenTexture(1, 还有渲染功能 几何和纹理坐标
纹理贴图是Threejs一个很重要的内容,游戏、产品720展示、物联网3D可视化等项目程序员加载模型的同时需要处理纹理贴图。 刚开始学习的学习的时候,也没必要去掌握Threejs所有种类纹理贴图的细节,关键是建立一个整体概念,用到的时候,知道需要查找那一节课,但是学习的时候,还是需要都打开相应的案例源码体验一边,跟着文字介绍,调试一下参数,体验感受一下。在以后的开发中遇到没有学过的纹理贴图,知道如
我正在学习一个关于在屏幕上渲染文本的教程,但我似乎找不到加载字体纹理的有效方法。我尝试了slick库,但它已经过时了:它使用lwjgl2的方法,lwjgl3中不再存在,因此它抛出了一个java。lang.NoSuchMethodError。在互联网上,我发现glfw(集成在lwjgl中)有一种称为glfwLoadTexture2D的方法,但它似乎只在glfw的C版本中可用。我还在openGL UT