在Qml场景下使用OpenGL渲染时出现奇怪的错误

5
作为一项业余爱好,我正在使用Qt+OpenGL实验OpenGL并编写自己的引擎。最近成功构建了实体系统框架,可以通过渲染到RenderBuffer(渲染缓冲区)来呈现带有阴影的场景,并附加物理引擎(BulletPhysics)。
之后,我升级到了Qt 5.2,并对Qml(之前没有尝试过)非常感兴趣。我稍微试验了一下2D Qml图形,并决定将其用于用户界面,但拒绝了2D项目并返回3D。我创建了一个新的qml项目,尝试像Scene Graph - OpenGL Under QML示例中那样在qml下渲染一些东西。之后,我将我的旧代码从3D项目移动到新项目中,并进行了一些小的重构(不应该影响任何内容)。
此时,我遇到了一些奇怪的bug。似乎第一帧被正确地呈现,但之后就出了问题。
这是一些比较正常的帧(在任何移动之前)的场景视图。
在左上角输出深度缓冲区(阴影图)。在右上角,我使用qml制作了一些自定义滚动条。我使用这些滚动条来改变阴影图的旋转和移动相机。当滚动条居中时,没有任何移动。当我改变一些东西时,场景变得丑陋,我不知道为什么。
我看到阴影图深度缓冲区没有改变(为什么?o_0),场景中的阴影出现了问题。但是,在将我的旧代码重构为qml项目后,不仅阴影失败了。当我移动相机时,错误变得更加奇怪。
场景是通过以下方式在qml下呈现的: Scene view after some shadow map rotate 这是一些阴影图旋转后的帧 Scene after moving camera down (-Z) 这是将相机向下移动(-Z)后的场景
connect(window, &QQuickWindow::beforeRendering, this, &MyApp::renderScene, Qt::DirectConnection);

在 renderScene 中,我:
  • Do some OpenGL conveyor setup (to prevent changes by Qml Scene Graph, may be excess);

    glViewport(0, 0, window->width(), window->height());
    
    glClearColor(0, 0, 0, 1);
    glClearDepth(1);
    glDepthRange(0,1);
    glDepthFunc(GL_LEQUAL);
    
  • Call BulletPhysics to calculate physics (in test scene - two falling cubes)

  • Render shadow map (depth-only) to RenderBuffer (2048x2048 texture, GL_DEPTH_COMPONENT24)
  • Render scene to RenderBuffer (two GL_RGBA8 textures for color and normals and one GL_DEPTH_COMPONENT24 for depth)
  • Render two 2d quads - fullscreen (scene render with all textures from renderbuffer to render with shadows) and side screen (top left corner's texture from depthbuffer)

我已经很累了,但是 glGetError 没有显示任何东西。

移动相机的场景 通过 XY 轴再次移动相机。人角的剪影依然可见,并产生奇怪的效果

更多截图:

旧项目 旧项目(不包含 qml)

旧项目-大截图 旧项目(大截图)

调整大小出问题 在新项目中,窗口调整大小也出现了问题

这可能是什么原因呢?

环境: Qt Creator 3.0.0, Qt 5.2.0, Linux (Kubuntu 13.10) 64-bit, HD 7750

OpenGL: 在旧项目中,我设置了 3.3,在新项目中使用了 4.3(据我所记)

P.S. 深度图像失真是因为我在着色器中使用了一些额外的畸变来改善场景中心的阴影。

1个回答

4

在一天没有答案的情况下,以及之前的许多天后,我得到了灵感!

我怀疑深度缓冲区存在问题,并尝试输出调试纹理id,这些id用于渲染全屏幕四边形。我尝试关闭渲染到纹理缓冲区交换等操作,但我甚至无法想象真正的问题在Qml方面。

glDepthMask(GL_TRUE); // 42!

这一行就像是所有问题的答案。似乎Qml在第一次渲染帧之前(但在发出&QQuickWindow::beforeRendering信号之后)调用了glDepthMask(GL_FALSE);。这次调用的结果是只有我的第一帧实际上被写入深度缓冲区。为什么我认为glDepthMask(GL_FALSE);只被调用了一次?因为我在插入我的“42”之后,FPS明显下降,在插入glDepthMask(GL_FALSE);在用于渲染场景的插槽末尾之后,FPS增加了。

现在,我可以自由旋转“太阳”(阴影)并移动相机,而不会出现任何视觉错误。

耶!

Result in new project


1
这对我来说是答案:D 我使用glEnable(GL_DEPTH_TEST)没有结果,但在QML中使用glDepthMask(GL_TRUE)就可以解决问题。 - Diego Fernando Murillo Valenci
1
当我与那个bug作斗争时,我尝试了启用和禁用所有状态的glEnable和glDisable - 深度测试、混合、剔除面...但是没有任何实际结果。 glDepthMask太不明显了,以至于被怀疑并不容易记住... - user2271079
非常好的答案 - 尽管可悲的是我不知道为什么会这样(这让我对未来感到有些担忧),我花了很长时间尝试在QML下使OpenGL工作,遵循Sean Harmer的博客 - 虽然它看起来不对劲,我认为这可能与深度缓冲有关,但如果没有你的帮助,我就找不到这个问题所在。谢谢。 - StuReeks
StuReeks,我将我的场景在 QML 层之前渲染。当 QML 首先进行渲染时,它会关闭深度缓冲区修改。太阳位置的阴影缓冲区在第一帧后停止刷新,这导致阴影凝固。此外,当移动相机或调整窗口大小时 - 深度缓冲区没有被重置。还有阴影故障。有时,一些对象或部件由于深度缓冲区阻止其进行此操作而无法呈现。 - user2271079

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接