欢迎访问宙启技术站
智能推送

在QOpenGLWidget()中实现阴影效果

发布时间:2024-01-02 10:12:01

在QOpenGLWidget中实现阴影效果可以使用阴影贴图(Shadow Mapping)的方法。阴影贴图是一种实时计算阴影的技术,它通过渲染场景的深度信息到一个纹理中,然后在渲染阴影时使用这个纹理进行比较,从而判断哪些片元处于阴影之中。

以下是一个简单的例子,演示了如何在QOpenGLWidget中实现阴影效果:

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>

class ShadowWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
    ShadowWidget(QWidget *parent = nullptr)
        : QOpenGLWidget(parent)
    {
    }

protected:
    void initializeGL() override
    {
        initializeOpenGLFunctions();

        glEnable(GL_DEPTH_TEST);
        glEnable(GL_LIGHTING);

        // 创建和编译顶点着色器和片段着色器
        m_shadowShaderProgram = new QOpenGLShaderProgram;
        m_shadowShaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, "vertex_shader.glsl");
        m_shadowShaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, "fragment_shader.glsl");
        m_shadowShaderProgram->link();

        // 创建纹理
        m_shadowMapTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
        m_shadowMapTexture->create();
        m_shadowMapTexture->setSize(width(), height());
        m_shadowMapTexture->setFormat(QOpenGLTexture::D16);
        m_shadowMapTexture->setMinMagFilters(QOpenGLTexture::Nearest, QOpenGLTexture::Nearest);
        m_shadowMapTexture->setWrapMode(QOpenGLTexture::ClampToEdge);

        // 创建渲染缓冲对象
        m_depthRenderBuffer = new QOpenGLFramebufferObject(width(), height(), QOpenGLFramebufferObject::Depth);

        // 将纹理和渲染缓冲对象绑定到帧缓冲对象
        m_fbo = new QOpenGLFramebufferObject(width(), height());
        m_fbo->bind();
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_shadowMapTexture->textureId(), 0);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRenderBuffer->handle());
        m_fbo->release();
    }

    void resizeGL(int width, int height) override
    {
        m_fbo->bind();
        m_shadowMapTexture->setSize(width, height);
        m_depthRenderBuffer->setSize(width, height);
        m_fbo->release();
    }

    void paintGL() override
    {
        // 渲染场景到阴影贴图
        m_fbo->bind();
        glViewport(0, 0, width(), height());

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        m_shadowShaderProgram->bind();

        // 将阴影贴图传递给片段着色器
        m_shadowShaderProgram->setUniformValue("shadowMap", 0);

        // 渲染场景

        m_shadowShaderProgram->release();
        m_fbo->release();

        // 渲染正常场景
        glViewport(0, 0, width(), height());
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // 渲染物体

        // 渲染阴影
        m_shadowShaderProgram->bind();

        // 使用阴影贴图进行渲染
        m_shadowMapTexture->bind();

        // 渲染阴影

        m_shadowMapTexture->release();
        m_shadowShaderProgram->release();
    }

private:
    QOpenGLShaderProgram *m_shadowShaderProgram;
    QOpenGLTexture *m_shadowMapTexture;
    QOpenGLFramebufferObject *m_fbo;
    QOpenGLFramebufferObject *m_depthRenderBuffer;
};

上述代码中,ShadowWidget是继承自QOpenGLWidget的自定义类。在initializeGL()函数中,我们进行了OpenGL的初始化工作,包括初始化函数指针、启用深度测试、启用光照、创建和编译着色器、创建纹理和渲染缓冲对象等。

在paintGL()函数中,我们首先通过绑定帧缓冲对象,将场景渲染到阴影贴图上。然后,我们渲染正常的场景,并将阴影贴图传递给片段着色器。最后,我们再次渲染阴影,使用阴影贴图进行渲染。

需要注意的是,上述代码中使用到了vertex_shader.glsl和fragment_shader.glsl两个着色器文件,你需要根据自己的场景和需求来编写这两个着色器文件。

以上就是在QOpenGLWidget中实现阴影效果的一个简单示例。当然,实际应用中还会涉及到更多的细节和优化,如深度偏移、阴影边缘柔化等。但是通过这个示例,你可以了解到如何在QOpenGLWidget中使用阴影贴图来实现阴影效果。