【发布时间】:2011-08-20 00:22:57
【问题描述】:
我已经使用 lwjgl (opengl) 成功实现了一个简单的 2-d 游戏,其中对象随着远离玩家而逐渐消失。这种淡入淡出最初是通过计算从玩家到每个对象的原点的距离来实现的,并使用它来缩放对象的 alpha/opacity。
但是,当使用较大的对象时,这种方法显得有些过于粗糙。我的解决方案是为对象中的每个像素实现 alpha/opacity 缩放。这不仅看起来更好,而且还将计算时间从 CPU 转移到 GPU。
我想我可以使用 FBO 和临时纹理来实现它。
通过绘制到 FBO 并使用特殊的混合模式使用预先计算的距离图(纹理)对其进行遮罩,我打算实现效果。
算法是这样的:
0) 初始化 opengl 并设置 FBO
1) 将背景渲染到标准缓冲区
2) 切换到自定义 FBO 并清除它
3) 渲染对象(到 FBO)
4) 使用距离纹理遮罩 FBO
5) 切换到标准缓冲区
6) 渲染 FBO 临时纹理(到标准缓冲区)
7) 渲染 hud 元素
一些额外的信息:
- 临时纹理与窗口(以及标准缓冲区)大小相同
- 第 4 步使用特殊的混合模式来达到预期的效果:
GL11.glBlendFunc(GL11.GL_ZERO, GL11.GL_SRC_ALPHA); - 我的临时纹理是使用 min/mag 过滤器创建的:GL11.GL_NEAREST
- 数据分配使用:org.lwjgl.BufferUtils.createByteBuffer(4 * width * height);
- 使用以下方法初始化纹理: GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, 宽度, 高度, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, dataBuffer);
- 我的代码中没有 GL 错误。
这确实达到了预期的结果。 然而,当我进行一些性能测试时,我发现我的 FBO 方法会削弱性能。我通过请求 1000 次连续渲染并测量时间来进行测试。结果如下:
在 512x512 分辨率下:
- 正常:~1.7s
- FBO:~2.5s
- (FBO - 步骤 6:~1.7 秒)
- (FBO - 第 4 步:~1.7 秒)
在 1680x1050 分辨率下:
- 正常:~1.7s
- FBO:~7s
- (FBO - 步骤 6:~3.5s)
- (FBO - 第 4 步:~6.0s)
如您所见,这扩展性非常差。更糟糕的是,我打算做第二次这种类型的传球。就我的目标受众而言,我测试的机器应该是高端的,所以我可以预期人们使用这种方法的帧率远低于 60 fps,这对于这么简单的游戏来说是难以接受的。
我可以做些什么来挽救我的表现?
【问题讨论】:
-
效果到底应该如何?在边界淡出或类似的东西?
-
距离图目前使用
double dist = Math.hypot(nRadius-i,nRadius-j); double a = Math.max( 0, 1 - dist / nRadius );实现。其中 a*a 用作 alpha。 -
i,j 在哪里迭代对象中的所有像素?我无法回答为什么你的 FBO 操作很慢,但我会使用多重纹理来实现它,你的距离图作为第二个纹理,如果达到一定距离,可能还会额外缩放 alpha
-
这是一个片段着色器上下跳跃的例子,喊着“please, please, please, use meeeeee!” :-) 尽管这是您思考和编程方式的重大变化,但您还是应该考虑一下。从您作为统一传递的某个点到 gl_Fragcoord.xy 的平方距离归结为一个向量减法和一个点积。如果你想要线性衰减,它是另一个recp和multiply,但它仍然尽可能简单,并且将超级超级快(大约十几个循环,没有额外的纹理,没有额外的混合,没有模糊的混合模式)。
-
我同意达蒙的观点。您的 FBO 解决方案很聪明,但与基于着色器的方法相比,它最终总是很慢。最好只使用 FBO 来渲染不需要每一帧都改变的纹理。