CloudCompare源码分析_显示渲染的程序架构

这一讲介绍一下CloudCompare的大致绘制流程或者说绘图框架的结构。

根据前面一篇对CloudCompare读取PLY文件的介绍 ,很容易找出当cloudcompare读取到mesh文件(newGroup)后,会把这个文件通过addDB加载到根对象下面,

//ref. mainwindow.cpp
void MainWindow::addToDB(    const QStringList& filenames,
                            QString fileFilter/*=QString()*/,
                            ccGLWindow* destWin/*=nullptr*/)
{
    addToDB(newGroup, true, true, false);
    ....
}



void MainWindow::addToDB(    ccHObject* obj,
                            bool updateZoom/*=true*/,
                            bool autoExpandDBTree/*=true*/,
                            bool checkDimensions/*=true*/,
                            bool autoRedraw/*=true*/)
{
    ......
    //add object to DB root
    if (m_ccRoot)
    {
        //force a 'global zoom' if the DB was emtpy!
        if (!m_ccRoot->getRootEntity() || m_ccRoot->getRootEntity()->getChildrenNumber() == 0)
        {
            updateZoom = true;
        }
        m_ccRoot->addElement(obj, autoExpandDBTree);
    }
    ......
}

这个根对象,就是ccDBRoot,

//ref. ccDBRoot.h
//! GUI database tree root
class ccDBRoot : public QAbstractItemModel
{
    Q_OBJECT

    //! Associated DB root
    ccHObject* m_treeRoot;

    //! Associated widget for DB tree
    QTreeView* m_dbTreeWidget;

    //! Associated widget for selected entity's properties tree
    QTreeView* m_propertiesTreeWidget;

    //! Selected entity's properties data model
    QStandardItemModel* m_propertiesModel;
    //! Selected entity's properties delegate
    ccPropertiesTreeDelegate* m_ccPropDelegate;
......
}

其内部定义了一个ccHObject的指针,m_treeRoot,最终用来容纳子对像

void ccDBRoot::addElement(ccHObject* object, bool autoExpand/*=true*/)
{
    ......
    //look for object's parent
    ccHObject* parentObject = object->getParent();
    if (!parentObject)
    {
        //if the object has no parent, it will be inserted at tree root
        parentObject = m_treeRoot;
        m_treeRoot->addChild(object);
    }

    ......
}

当然,这些都不是重点。重点是:CloudCompare是如何完成3D渲染的,相关的OpenGL函数,或者是Qt的OpenGL相关函数又封装在哪里?

下面我们慢慢来看。

相关的OpenGL函数在哪里?

OpenGL扩展函数

首先我们来看OpenGLExtensions,这些函数也就是OpenGL的扩展函数。

比如我的目录是这样的,

C:\Qt\qt5\5.15.0\msvc2019_64\include\QtOpenGLExtensions\qopenglextensions.h

在这个头文件中,不同的类封装了大量的OpenGL函数,例如,

class QOpenGLExtension_ARB_framebuffer_object : public QAbstractOpenGLExtension
{
public:
    QOpenGLExtension_ARB_framebuffer_object();

    bool initializeOpenGLFunctions() final;

    void glFramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
    void glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
    void glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
    void glGenerateMipmap(GLenum target);
    void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint *params);
    void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
    void glFramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
    void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
    void glFramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
    GLenum glCheckFramebufferStatus(GLenum target);
    void glGenFramebuffers(GLsizei n, GLuint *framebuffers);
    void glDeleteFramebuffers(GLsizei n, const GLuint *framebuffers);
    void glBindFramebuffer(GLenum target, GLuint framebuffer);
    GLboolean glIsFramebuffer(GLuint framebuffer);
    void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint *params);
    void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
    void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers);
    void glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers);
    void glBindRenderbuffer(GLenum target, GLuint renderbuffer);
    GLboolean glIsRenderbuffer(GLuint renderbuffer);

protected:
    Q_DECLARE_PRIVATE(QOpenGLExtension_ARB_framebuffer_object)
};

又例如,

class QOpenGLExtension_ARB_gpu_shader_fp64 : public QAbstractOpenGLExtension
{
public:
    QOpenGLExtension_ARB_gpu_shader_fp64();

    bool initializeOpenGLFunctions() final;

    void glGetUniformdv(GLuint program, GLint location, GLdouble *params);
    void glUniformMatrix4x3dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);
    void glUniformMatrix4x2dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);
    void glUniformMatrix3x4dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);
    void glUniformMatrix3x2dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);
    void glUniformMatrix2x4dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);
    void glUniformMatrix2x3dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);
    void glUniformMatrix4dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);
    void glUniformMatrix3dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);
    void glUniformMatrix2dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);
    void glUniform4dv(GLint location, GLsizei count, const GLdouble *value);
    void glUniform3dv(GLint location, GLsizei count, const GLdouble *value);
    void glUniform2dv(GLint location, GLsizei count, const GLdouble *value);
    void glUniform1dv(GLint location, GLsizei count, const GLdouble *value);
    void glUniform4d(GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
    void glUniform3d(GLint location, GLdouble x, GLdouble y, GLdouble z);
    void glUniform2d(GLint location, GLdouble x, GLdouble y);
    void glUniform1d(GLint location, GLdouble x);

protected:
    Q_DECLARE_PRIVATE(QOpenGLExtension_ARB_gpu_shader_fp64)
};

这样的函数还有非常多,这里我就不一一列举了。如果要详细了解的话,可以参考相关文档,例如,上面两个类的第一个函数分别可参考,

https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glFramebufferTextureLayer.xhtml

https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetUniform.xhtml

相关的函数在QCC_GL_LIB项目中用到,我们可以在CloudCompare的子项目中找到,目录地址是

libs\CCFbo\include

OpenGL各版本的函数

在Qt的源码中,这些函数的目录地址是,

qt5\5.15.0\msvc2019_64\include\QtGui\

例如我的本地地址,对应OpenGL4.5版本的函数地址是,

C:\Qt\qt5\5.15.0\msvc2019_64\include\QtGui\qopenglfunctions_4_5_core.h

由于这些类中包含的函数体量过大,这里我只贴了部分源码,如下,

class Q_GUI_EXPORT QOpenGLFunctions_4_5_Core : public QAbstractOpenGLFunctions
{
public:
    QOpenGLFunctions_4_5_Core();
    ~QOpenGLFunctions_4_5_Core();

    bool initializeOpenGLFunctions() override;

    // OpenGL 1.0 core functions
    void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
    void glDepthRange(GLdouble nearVal, GLdouble farVal);
    GLboolean glIsEnabled(GLenum cap);
    void glGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params);
    void glGetTexLevelParameterfv(GLenum target, GLint level, GLenum pname, GLfloat *params);
    void glGetTexParameteriv(GLenum target, GLenum pname, GLint *params);
    void glGetTexParameterfv(GLenum target, GLenum pname, GLfloat *params);
    void glGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void *pixels);
    const GLubyte * glGetString(GLenum name);
    void glGetIntegerv(GLenum pname, GLint *data);
    void glGetFloatv(GLenum pname, GLfloat *data);
    GLenum glGetError();
    void glGetDoublev(GLenum pname, GLdouble *data);
    void glGetBooleanv(GLenum pname, GLboolean *data);
    void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
    void glReadBuffer(GLenum src);
    void glPixelStorei(GLenum pname, GLint param);
    void glPixelStoref(GLenum pname, GLfloat param);
    void glDepthFunc(GLenum func);
    void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass);
    void glStencilFunc(GLenum func, GLint ref, GLuint mask);
    void glLogicOp(GLenum opcode);
    void glBlendFunc(GLenum sfactor, GLenum dfactor);
    void glFlush();
    void glFinish();
    void glEnable(GLenum cap);
    void glDisable(GLenum cap);
    void glDepthMask(GLboolean flag);
    void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
    void glStencilMask(GLuint mask);
    void glClearDepth(GLdouble depth);
    void glClearStencil(GLint s);
    void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
    void glClear(GLbitfield mask);
    void glDrawBuffer(GLenum buf);
    void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
    void glTexImage1D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels);
    void glTexParameteriv(GLenum target, GLenum pname, const GLint *params);
    void glTexParameteri(GLenum target, GLenum pname, GLint param);
    void glTexParameterfv(GLenum target, GLenum pname, const GLfloat *params);
    void glTexParameterf(GLenum target, GLenum pname, GLfloat param);
    void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
    void glPolygonMode(GLenum face, GLenum mode);
    void glPointSize(GLfloat size);
    void glLineWidth(GLfloat width);
    void glHint(GLenum target, GLenum mode);
    void glFrontFace(GLenum mode);
    void glCullFace(GLenum mode);
 ......
}

libs\qCC_glWindow\src\ccGLUtils.cpp中了函数

DisplayTexture2DPosition

为例,其中通过QOpenGLFunctions_2_1调用了OpenGLES2.1中的函数完成相关功能,

void ccGLUtils::DisplayTexture2DPosition(GLuint texID, int x, int y, int w, int h, unsigned char alpha/*=255*/)
{
    QOpenGLContext* context = QOpenGLContext::currentContext();
    if (!context)
    {
        assert(false);
        return;
    }
    QOpenGLFunctions_2_1* glFunc = context->versionFunctions();
    if (glFunc)
    {
        glFunc->glBindTexture(GL_TEXTURE_2D, texID);

        glFunc->glPushAttrib(GL_ENABLE_BIT);
        glFunc->glEnable(GL_TEXTURE_2D);

        glFunc->glColor4ub(255, 255, 255, alpha);
        glFunc->glBegin(GL_QUADS);
        glFunc->glTexCoord2f(0.0, 1.0);
        glFunc->glVertex2i(x, y + h);
        glFunc->glTexCoord2f(0.0, 0.0);
        glFunc->glVertex2i(x, y);
        glFunc->glTexCoord2f(1.0, 0.0);
        glFunc->glVertex2i(x + w, y);
        glFunc->glTexCoord2f(1.0, 1.0);
        glFunc->glVertex2i(x + w, y + h);
        glFunc->glEnd();

        glFunc->glPopAttrib();

        glFunc->glBindTexture(GL_TEXTURE_2D, 0);
    }
}

同样,在

.\libs\qCC_glWindow\src\ccGLWindow.cpp

中,对3D的渲染使用的也是QOpenGLFunctions_2_1,源码如下,

    //Default OpenGL functions set
    using ccQOpenGLFunctions = QOpenGLFunctions_2_1;

void ccGLWindow::draw3D(CC_DRAW_CONTEXT& CONTEXT, RenderingParams& renderingParams)
{
    ccQOpenGLFunctions* glFunc = functions();
    assert(glFunc);

    glFunc->glPointSize(m_viewportParams.defaultPointSize);
    glFunc->glLineWidth(m_viewportParams.defaultLineWidth);

    glFunc->glEnable(GL_DEPTH_TEST);
    ......

}

CloudCompare是如何完成3D渲染的

在cloudcompare中,有一个轻量级的项目,ccViewer,如果想了解渲染的话,单看这个就够了。

渲染的函数路线如下,

ccGLWindow:

void ccGLWindow::paintGL() -->

ccGLWindow::fullRenderingPass(CONTEXT, renderingParams); -->

ccGLWindow::draw3D(CONTEXT, renderingParams); -->

ccGLWindow::drawCross()

这里,主体的绘图函数其实就是

void ccGLWindow::draw3D(CC_DRAW_CONTEXT& context, RenderingParams& params);

在该函数中,最终会调用ccHObject::draw进行绘制,

ccHObject:

ccGLWindow::draw3D(CONTEXT, renderingParams); -->

void ccHObject::draw(CC_DRAW_CONTEXT& context)

前一讲已经介绍过,打开一个mesh的时候,最终是加入到ccHObject中的container,因为打开的mesh本身也是一个ccHObject,所以实际上就是放在其m_children中,

//Definition

class QCC_DB_LIB_API ccHObject : public ccObject, public ccDrawableObject
{
public: //construction
    ......
    //! Standard instances container (for children, etc.)
    using Container = std::vector;
    ......
    //! Children
    Container m_children;
    ......
}

所以在ccHObject中,绘制函数是

void ccHObject::draw(CC_DRAW_CONTEXT& context)
{
    ......
    //draw entity
    if (m_visible && drawInThisContext)
    {
        if (( !m_selected || !MACRO_SkipSelected(context) ) &&
            (  m_selected || !MACRO_SkipUnselected(context) ))
        {
            //apply default color (in case of)
            ccGL::Color4v(glFunc, context.pointsDefaultCol.rgba);

            //enable clipping planes (if any)
            bool useClipPlanes = (draw3D && !m_clipPlanes.empty());
            if (useClipPlanes)
            {
                toggleClipPlanes(context, true);
            }

            drawMeOnly(context);

            //disable clipping planes (if any)
            if (useClipPlanes)
            {
                toggleClipPlanes(context, false);
            }
        }
    }
    ......
    //draw entity's children
    for (auto child : m_children)
    {
        child->draw(context);
    }
    ......    
}

这是一个递归调用函数,当要对mesh进行绘制的时候,会调用下面的这个drawMeOnly函数,这也就是Mesh的最终绘制函数,顶点和面的计算都在这里进行。

void ccMesh::drawMeOnly(CC_DRAW_CONTEXT& context)

该函数因为内容比较多,这里就不展开了,有兴趣的读者不妨自己打开看看源码。

好了,到现在为止,我们对CloudCompare的大致绘制流程或者说框架已经有了 一个初略的了解,后面有机会我们进一步深入。

本文结束。

你可能感兴趣的:(3D,C/C++,qt,开发语言)