这一讲介绍一下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相关函数又封装在哪里?
下面我们慢慢来看。
首先我们来看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
在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中,有一个轻量级的项目,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的大致绘制流程或者说框架已经有了 一个初略的了解,后面有机会我们进一步深入。
本文结束。