2021SC@SDUSC
目录
分析概括
模块功能
ModelOffscreenRender模块主要包含两部分内容:
1.控制几何体在世界坐标内的移动、旋转以及视角的变换
2、将几何体使用帧渲染的形式渲染后转换为二维纹理贴图形式后进行处理并返回处理图像
类函数基础
QSize类
QSize 类代表一个矩形区域的大小,实现在 QtCore 共享库中,是由一个整型的宽度和整型的高度组合而成的。
//QSize 常用成员函数
void setWidth(int width); // 设置宽度
void setHeight(int height); // 设置高度
int width() const; // 获得宽度
int height() const; // 获得高度
QImage类
QImage类提供了独立于硬件的图像表示形式,该图像表示形式可以直接访问像素数据。
//缩放操作:将原图像缩放到(width,height)大小
QImage* imgScaled = new QImage;
*imgScaled=img->scaled(width,height,Qt::KeepAspectRatio);
//旋转操作
QImage* imgRatate = new QImage;
*imgRotate = img->transformed(matrix);
ModelOffscreenRender.h分析
ModelOffscreenRender.h中对应的子函数分别对应了Dust3D针对生成三位几何体的不同操作,在项目中对应菜单-编辑部分的翻转、旋转、着色以及移动等操作对应的实现代码:
具体函数功能如下:
class ModelOffscreenRender : QOffscreenSurface
{
public:
ModelOffscreenRender(const QSurfaceFormat &format, QScreen *targetScreen = Q_NULLPTR);//构造函数
~ModelOffscreenRender();//析构函数
//设置X,y,z方向的旋转,旋转角度为angle
void setXRotation(int angle);
void setYRotation(int angle);
void setZRotation(int angle);
void setEyePosition(const QVector3D &eyePosition);//设置视角
void setMoveToPosition(const QVector3D &moveToPosition);//设置位置移动
void setRenderPurpose(int purpose);
void setRenderThread(QThread *thread);//渲染线程
void enableWireframe();//是否启用线框
void enableEnvironmentLight();//是否启用环境光
void updateMesh(Model *mesh);//网格更新
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);//更新Toon法线和深度贴图
void setToonShading(bool toonShading);//着色
QImage toImage(const QSize &size);//转换图像
private:
int m_xRot = 0;
int m_yRot = 0;
int m_zRot = 0;
QVector3D m_eyePosition;
QVector3D m_moveToPosition;
int m_renderPurpose = 0;
QOpenGLContext *m_context = nullptr;
QOpenGLFramebufferObject *m_renderFbo = nullptr;
Model *m_mesh = nullptr;
QImage *m_normalMap = nullptr;
QImage *m_depthMap = nullptr;
bool m_toonShading = false;
bool m_isWireframeVisible = false;
bool m_isEnvironmentLightEnabled = false;
};
ModelOffscreenRender.cpp
RRT渲染技术
在OpenGL中所有的图形,都会绘制到 FrameBufferObject 上。如果要使用界面的做分屏渲染,或需要屏幕图像制成贴图以备后期处理,就需要用到 FrameBufferObject 技术。其原理为先渲染到帧缓冲FBO,再把渲染目标从帧缓存变成一个纹理。(参考网址:(27条消息) OpenGL(九)使用 FrameBufferObject_阿修罗道-CSDN博客)
在toimage()中的应用如下:
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);//将帧缓冲器对象的附件配置设置为附件,返回附加到帧缓冲器对象的深度缓冲区和模具缓冲区的配置
format.setSamples(4);//将多采样帧缓冲器对象的每个像素的样本数设置为样本
format.setTextureTarget(GL_TEXTURE_2D);//将附加到帧缓冲对象的纹理的纹理目标设置为目标
format.setInternalTextureFormat(GL_RGBA32F_ARB);//将帧缓冲器对象的纹理或多样本帧缓冲器对象的颜色缓冲区的内部格式设置为internalTextureFormat
m_renderFbo = new QOpenGLFramebufferObject(size, format);
m_renderFbo->bind();
m_context->functions()->glViewport(0, 0, size.width(), size.height());
反走样设置
走样(aliasing)时在光栅图形显示器上绘制非水平且非垂直的直线或多边形边界时,或多或少会呈现锯齿状或台阶状外观。这是因为直线、多边形、色彩边界等是连续的,而光栅则是由离散的点组成,在光栅显示设备上表现直线、多边形等,必须在离散位置采样。用于减少或消除这种效果的技术称为反走样(antialiasing)。
在Dust3D中的走样主要表现为锯齿形边和纹理绘制的失真,针对线框的失真采用函数为
m_context->functions()->glEnable(GL_LINE_SMOOTH);
ToImage()函数分析
QImage ModelOffscreenRender::toImage(const QSize &size)
{
QImage image;
m_context = new QOpenGLContext();//设置上下文
m_context->setFormat(format());//设置上下文布局
if (!m_context->create())
{//针对上下文未设置成功的处理
delete m_context;
m_context = nullptr;
qDebug() << "QOpenGLContext create failed";
return image;
}
if (!m_context->makeCurrent(this)) {
delete m_context;
m_context = nullptr;
qDebug() << "QOpenGLContext makeCurrent failed";
return image;
}
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);//将帧缓冲器对象的附件配置设置为附件,返回附加到帧缓冲器对象的深度缓冲区和模具缓冲区的配置
format.setSamples(4);//将多采样帧缓冲器对象的每个像素的样本数设置为样本
format.setTextureTarget(GL_TEXTURE_2D);//将附加到帧缓冲对象的纹理的纹理目标设置为目标
format.setInternalTextureFormat(GL_RGBA32F_ARB);//将帧缓冲器对象的纹理或多样本帧缓冲器对象的颜色缓冲区的内部格式设置为internalTextureFormat
m_renderFbo = new QOpenGLFramebufferObject(size, format);
m_renderFbo->bind();
m_context->functions()->glViewport(0, 0, size.width(), size.height());
if (nullptr != m_mesh) {//如果网格非空
QMatrix4x4 projection;
QMatrix4x4 world;
QMatrix4x4 camera;
//争对版本字符串的处理
bool isCoreProfile = false;
const char *versionString = (const char *)m_context->functions()->glGetString(GL_VERSION);
if (nullptr != versionString &&
'\0' != versionString[0] &&
0 == strstr(versionString, "Mesa")) {
isCoreProfile = m_context->format().profile() == QSurfaceFormat::CoreProfile;
}
ModelShaderProgram *program = new ModelShaderProgram(isCoreProfile);
ModelMeshBinder meshBinder;//初始化模型网格连接
meshBinder.initialize();
if (m_isWireframeVisible)//点击了线框
meshBinder.showWireframe();//显示线框
else
meshBinder.hideWireframe();//隐藏线框
if (m_isEnvironmentLightEnabled)
meshBinder.enableEnvironmentLight();//显示环境光
if (nullptr != m_normalMap && nullptr != m_depthMap) {
meshBinder.updateToonNormalAndDepthMaps(m_normalMap, m_depthMap);
m_normalMap = nullptr;
m_depthMap = nullptr;
}
m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓存区
m_context->functions()->glEnable(GL_BLEND);
m_context->functions()->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);//颜色混合技术,将源色和目标色以某种方式混合生成特效的技术。
m_context->functions()->glEnable(GL_DEPTH_TEST);
#ifdef GL_LINE_SMOOTH//反走样设置
m_context->functions()->glEnable(GL_LINE_SMOOTH);
#endif
world.setToIdentity();
//世界坐标的旋转
world.rotate(m_xRot / 16.0f, 1, 0, 0);
world.rotate(m_yRot / 16.0f, 0, 1, 0);
world.rotate(m_zRot / 16.0f, 0, 0, 1);
projection.setToIdentity();
//世界坐标的平移
projection.translate(m_moveToPosition.x(), m_moveToPosition.y(), m_moveToPosition.z());
//物体透明度设置
projection.perspective(45.0f, GLfloat(size.width()) / size.height(), 0.01f, 100.0f);
camera.setToIdentity();
//用户视角变换
camera.translate(m_eyePosition);
program->bind();
//为不同的对象设置统一值
program->setUniformValue(program->eyePosLoc(), m_eyePosition);
program->setUniformValue(program->toonShadingEnabledLoc(), m_toonShading ? 1 : 0);
program->setUniformValue(program->projectionMatrixLoc(), projection);
program->setUniformValue(program->modelMatrixLoc(), world);
QMatrix3x3 normalMatrix = world.normalMatrix();
program->setUniformValue(program->normalMatrixLoc(), normalMatrix);
program->setUniformValue(program->viewMatrixLoc(), camera);
program->setUniformValue(program->textureEnabledLoc(), 0);
program->setUniformValue(program->normalMapEnabledLoc(), 0);
program->setUniformValue(program->mousePickEnabledLoc(), 0);
program->setUniformValue(program->renderPurposeLoc(), m_renderPurpose);
program->setUniformValue(program->toonEdgeEnabledLoc(), 0);
program->setUniformValue(program->screenWidthLoc(), (GLfloat)size.width());
program->setUniformValue(program->screenHeightLoc(), (GLfloat)size.height());
program->setUniformValue(program->toonNormalMapIdLoc(), 0);
program->setUniformValue(program->toonDepthMapIdLoc(), 0);
//更新网格
meshBinder.updateMesh(m_mesh);
//为网格绑定纹理
meshBinder.paint(program);
meshBinder.cleanup();
program->release();
delete program;
m_mesh = nullptr;
}
m_context->functions()->glFlush();
image = m_renderFbo->toImage();
m_renderFbo->release();
m_context->doneCurrent();
delete m_context;
m_context = nullptr;
return image;
}