将Cocos2dx渲染到Qt的QOpenGLWidget,在mac上黑屏

255 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情

今天尝试着使用QOpenGLWidget从零绘制一个三角形,发现始终黑屏。

经过反复摸索,最终可以了,这里是DEMO

总结需要注意的几点:

  • 必须在paintGL回调里面draw,不然qt也就没有必要提供这个回调。

  • 继承QOpenGLExtraFunctions/QOpenGLFunctions,并在initializeGL中进行OpenGL函数寻址

    void QOpenGLWidget::initializeGL(){
        this->initializeOpenGLFunctions();
        // ...
    }
    
    classOpenGL ES最直观的区别
    QOpenGLExtraFunctions3.0/3.1/3.2可以使用glBindVertexArray
    QOpenGLFunctions2.0无法使用glBindVertexArray
  • shader报错: version '330' is not supported

    这里的报错问题其实是和OpenGL的版本有关系,每个OpenGL版本之间是存在差异的。正因为存在不同的OpenGL版本,所以shader里面才有了

    #version 330 core

    解决办法:

    #include <qsurfaceformat.h>
    QSurfaceFormat fmt;
    fmt.setVersion(3, 3);
    fmt.setProfile(QSurfaceFormat::CoreProfile);
    QSurfaceFormat::setDefaultFormat(fmt);
    

    当然配套的shader也得同步修改

shader编译失败导致的黑屏

首先你需要确认是debug模式,否则你看不到任何的cocos engine log,也就看不到shader编译的报错,当然也就不会怀疑是shader导致的问题。

glCompileShader如果出现问题,只有在debug模式下能看到报错,其实我感觉这种问题是无论如何都要log的,不然会增加排查难度,坑了我好几天的时间!

例如这个shader

uniform mat4 CC_PMatrix;
uniform mat4 CC_MultiViewPMatrix[4];
uniform mat4 CC_MVMatrix;
uniform mat4 CC_MVPMatrix;
uniform mat4 CC_MultiViewMVPMatrix[4];
uniform mat3 CC_NormalMatrix;
uniform vec4 CC_Time;
uniform vec4 CC_SinTime;
uniform vec4 CC_CosTime;
uniform vec4 CC_Random01;
uniform sampler2D CC_Texture0;
uniform sampler2D CC_Texture1;
uniform sampler2D CC_Texture2;
uniform sampler2D CC_Texture3;
//CC INCLUDES END


attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;

#ifdef GL_ES
varying lowp vec4 v_fragmentColor;
varying mediump vec2 v_texCoord;
#else
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
#endif

void main()
{
    gl_Position = CC_MVPMatrix * a_position;
    v_fragmentColor = a_color;
    v_texCoord = a_texCoord;
}

报错信息如下:

cocos2d: ERROR: 0:1: '' :  #version required and missing.
ERROR: 0:18: 'attribute' : syntax error: syntax error

那很明显就是OpenGL的版本导致的问题,修改shader是不可能的,我们只能调整OpenGL版本,那么这种shader写法应该是哪个版本的呢?

查询当前使用的OpenGL相关信息:

QSurfaceFormat fmt = QSurfaceFormat::defaultFormat();
auto version = fmt.version();
qDebug() << "use surface version: " << version.first << "." << version.second; // 返回的是2.0
const GLubyte* versionString = glGetString(GL_VERSION);
qDebug() << "use opengl version: " << QString(QLatin1String((char*)versionString)); // 返回的是4.1

QSurfaceFormat::setVersion()默认值就是2.0

glGetString(GL_VERSION)可能获取的和surface不太一致,至于为啥,我就没有再深究了。

#version必须写是opengl3.3之后规定的,所以这里我直接设置QOpenGLWidget使用的版本,注意必须放在QOpenGLWidget的构造函数之前,我是放在了main入口

QSurfaceFormat fmt;
fmt.setVersion(2, 1);
fmt.setProfile(QSurfaceFormat::NoProfile);
QSurfaceFormat::setDefaultFormat(fmt);

3个模式,后续有时间再慢慢了解:

  • NoProfile
  • CompatibilityProfile
  • CoreProfile

至此,shader就能正常通过编译了

framebuffer导致的黑屏

FrameBuffer Status Error 33305,

OpenGL error 0x0506 in cocos2d-x/cocos/renderer/CCRenderer.cpp saveRenderState 155

第二个错误是因为第一个错误导致的,这里我们需要重点关注第一个错误即可

glCheckFramebufferStatus返回值33305(0X8219)的含义是GL_FRAMEBUFFER_UNDEFINED,也就是没有framebuffer

产生原因

不同于OpenGL,Qt中的QOpenGLWidget类的对象不存在默认的Framebuffer,即当前Framebuffer的id不一定是0。

通过context()->defaultFramebufferObject()可以获取当前Framebuffer的id。

经过实验发现,在initializeGL()中的Framebuffer初始为0,而paintGL()中则默认新创建了一个Framebuffer

因此,需要在paintGL()中通过context()->defaultFramebufferObject()获取id,之后才能进行glBindFramebuffer()等操作。

cocos2dx 使用的glfw版本为3.2,glfw会默认创建一个有效的framebuffer,所以默认的cocos xcode工程没有问题。

解决办法

在paintGL中进行初始化engine,这样frameBuffer就正常了,engine也就正常了。

其实在这个项目中就提到了这个问题,刚好曾经看过,有印象,所以我也就这么做了

OpenGL版本对应的GLSL版本

OpenGL版本号GLSL版本号发布时间
2.01102004年9月7日
2.11202006年7月2日
3.01302008年8月11日
3.11402009年3月24日
3.21502009年8月3日
3.33302010年3月11日
4.04002010年3月11日
4.14102010年7月26日
4.24202011年8月8日
4.34302012年8月6日
4.44402013年7月23日
4.54502014年7月
4.64602017年7月
OpenGL ES 版本号GLSL ES版本号
2.0100
3.0300
3.1310
3.2320

每个GLSL版本之间的差异