Qt与OpenGL趣味开发 - 绘制炫酷倒影

我们来用Qt和OpenGL绘制一个倒影,先看一下显示效果:

绘制方法比较简单,步骤如下:

  1. 绘制正常的两个箱子。
  2. 绘制地板。绘制前,禁止 深度测试 写入;开启 蒙版测试 ,将蒙版测试函数设置为 GL_ALWAYS ,绘制时将蒙版缓冲区写入1。绘制后,开启深度测试写入。
  3. 绘制倒影的箱子。绘制前,设置模板测试函数,参数为 GL_EQUAL ,表示蒙版缓冲区为1的时候才会通过蒙版测试。绘制时只要把箱子绘制的灰一点,就可以实现了倒影的效果。

绘制部分的关键代码如下:

// 开启深度测试
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);

// 开启蒙版测试
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 1, 0xFF);

glClearColor(100.0f / 255.0f, 100.0f / 255.0f, 100.0f / 255.0f, 1.0f);
glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

// 绘制灯光
m_pCamera->activeCamera(m_pLightShaderProgram);
drawLight();

// 使用shader
m_pShaderProgram->bind();

// 设置光的信息
m_pShaderProgram->setUniformValue("lightMaterial.enabled", true);
m_pShaderProgram->setUniformValue("lightMaterial.direction", m_light.lightPos);
m_pShaderProgram->setUniformValue("lightMaterial.ambient", m_light.ambientColor);
m_pShaderProgram->setUniformValue("lightMaterial.diffuse", m_light.diffuesColor);
m_pShaderProgram->setUniformValue("lightMaterial.specular", m_light.specularColor);
m_pShaderProgram->setUniformValue("objectMaterial.shininess", 32.0f);

// 設置眼睛的位置
if (m_pCamera)
{
    QVector3D cameraPos = m_pCamera->getCameraPostion();
    m_pShaderProgram->setUniformValue("M_ViewPostion", cameraPos);
}

// 设置视图矩阵和投影矩阵
m_pCamera->activeCamera(m_pShaderProgram);

// ======================= 以下为真正的绘制部分 ======================
glStencilMask(0x00);
// 绘制盒子
drawTwoBox();

// 写入蒙版缓冲区
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
// 绘制地板
glDepthMask(GL_FALSE);
drawFloor();
glDepthMask(GL_TRUE);

// 绘制盒子的倒影
glStencilFunc(GL_EQUAL, 1, 0xFF);
glStencilMask(0x00);
drawInvertedBox();
glStencilMask(0xFF);
m_pShaderProgram->release();

因为这里的地板足够的长,因此这里的蒙版缓冲相关的设置的效果没有那么的明显,如果我们把地板改的小一点,就会有下面的效果:

倒影会根据地板的大小而显示应该显示的大小,这正是我们想要的效果。因为只有绘制地板的区域将蒙版缓冲区写入了1,而绘制倒影的时候,也只有蒙版缓冲区为1的片段才会通过蒙版测试。

绘制倒影的箱子,只需要设置模型矩阵的缩放时,将Y轴缩放-1倍,就是实现了倒影的效果

void drawBox(const QVector3D& pos, bool isInverted)
{
    QMatrix4x4 mat;
    mat.translate(pos);
    mat.rotate(m_angle, QVector3D(0.0f, 1.0f, 0.0f));
    if (isInverted)
        mat.scale(1.0f, -1.0f, 1.0f);
    // 设置模型矩阵
    m_pShaderProgram->setUniformValue("M", mat);

    // 绘制箱子
    m_pMesh->draw();
}

最后的,将倒影的箱子变灰一点,这里我直接对每个片段乘以了一个向量 (0.2, 0.2, 0.2)
shader中的直接乘以这个设置的向量即可:

gl_FragColor = vec4((ambient + diffuse) * objectFactor, 1.0);

CPU中设置该向量:

void drawInvertedBox(void)
{
    m_pShaderProgram->setUniformValue("objectFactor", QVector3D(0.2f, 0.2f, 0.2f));

    // 绘制盒子
    drawBox(QVector3D(0.0f, -2.5f, 0.0f), true);
    drawBox(QVector3D(2.0f, -2.5f, 2.0f), true);
}

完整代码下载-第17个样例(目前实现了20个样例,持续更新中):
https://github.com/douzhongqiang/QtLearnOpenGL

代码中用到了Assimp的库,我这里提供了编译好的VS2019 x64的库
https://download.csdn.net/download/douzhq/15137709

不会飞的纸飞机
扫一扫二维码,了解我的更多动态。

下一篇文章:蒙皮骨骼动画