阴影映射(Shadow Map)的研究(五)

阴影映射(Shadow Map)的研究(五)

我成功地将别人的例子加以改进,使用QOpenGLWidget作为渲染窗口,将阴影映射渲染了出来。目前可以确定的是,使用OpenGL ES 2.0作为渲染的接口要求,能够让目前绝大多数机器都能够顺利兼容,但是囿于渲染窗口,可能在某些平台上表现不好。如果移植到Qt Quick 2,这样能够支持的平台就更多了。现在我将这些接口统统使用Qt的方式实现了,移植到Qt Quick 2也很简单。

这里主要参考的是OpenGLUnderQML这个例子,自定义了一个QQuickItem的子类,使其作为渲染的视图。紧接着,定义了多个TexturedCube,指定相关属性,并且使用专用的TexturedCubeRenderer负责渲染,最后将这些类导出至QML环境中。这样就可以使用QML来编写场景了。这种方法,在Qt 3D中广泛使用,它也将成为我以后开发3D应用的脚本描述方式。例子与上一个例子的显示差别并不大,目前只实现了阴影映射这个功能,有感兴趣的同行们可以使用这个例子进行扩展,实现自己想要的效果。

作为参考范例,贴出Cube类的代码:

Cube.h

#ifndef MYCUBE_H
#define MYCUBE_H

#include <QUrl>
#include <QVector3D>
#include <QObject>

class View;
class CubeRenderer;
class Cube: public QObject
{
    Q_OBJECT
    Q_PROPERTY( qreal length READ length WRITE setLength NOTIFY lengthChanged )
    Q_PROPERTY( QUrl source READ source WRITE setSource NOTIFY sourceChanged )
    Q_PROPERTY( QVector3D translate READ translate WRITE setTranslate NOTIFY translateChanged )
public:
    explicit Cube( QObject* parent = Q_NULLPTR );

    void initialize( void );
    void render( void );
    void renderShadow( void );
    void sync( void );
    void release( void );
    void setView( View* view ) { m_view = view; }

    qreal length( void ) { return m_length; }
    void setLength( qreal length );

    QUrl source( void ) { return m_source; }
    void setSource( const QUrl& source );

    QVector3D translate( void ) { return m_translate; }
    void setTranslate( const QVector3D& translate );

    friend class CubeRenderer;
signals:
    void lengthChanged( void );
    void sourceChanged( void );
    void translateChanged( void );
protected:
    qreal           m_length;
    QUrl            m_source;
    QVector3D       m_translate;

    bool            m_lengthIsDirty: 1;
    bool            m_sourceIsDirty: 1;
    bool            m_translateIsDirty: 1;

    View* m_view;
    CubeRenderer*  m_renderer;
};

#endif // MYCUBE_H

Cube.cpp

#include <math.h>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QQmlFile>
#include "View.h"
#include "Cube.h"

#define VERTEX_COUNT   36
#define CUBE_LENGTH    25.0
#define TEXTURE_UNIT    GL_TEXTURE0
#define SHADOW_TEXTURE_UNIT GL_TEXTURE1

static void canonicalPosition( QVector3D& position )
{
    if ( !qFuzzyIsNull( position.x( ) ) )
        position.setX( position.x( ) / fabsf( position.x( ) ) );
    if ( !qFuzzyIsNull( position.y( ) ) )
        position.setY( position.y( ) / fabsf( position.y( ) ) );
    if ( !qFuzzyIsNull( position.z( ) ) )
        position.setZ( position.z( ) / fabsf( position.z( ) ) );
}

class CubeRenderer: protected QOpenGLFunctions
{
    struct Vertex
    {
        void set( const QVector3D& _position, const QVector3D& _normal,
                  const QVector2D& _texCoord )
        {
            position = _position;
            normal = _normal;
            texCoord = _texCoord;
        }

        QVector3D               position;
        QVector3D               normal;
        QVector2D               texCoord;
    };
public:
    enum ShadowType
    {
        NoShadow = 0,// 以后依次递增
        SimpleShadow,
        PCFShadow
    };

    explicit CubeRenderer( Cube* plane, ShadowType shadowType ):
        m_cube( plane ),
        m_shadowType( shadowType ),
        m_vertexBuffer( QOpenGLBuffer::VertexBuffer ),
        m_texture( QOpenGLTexture::Target2D )
    {
        initializeOpenGLFunctions( );

        // 根据创建的次数来创建着色器
        if ( s_count++ == 0 )
        {
            s_program = new QOpenGLShaderProgram;
            s_program->addShaderFromSourceFile( QOpenGLShader::Vertex,
                                                ":/Common.vert" );
            s_program->addShaderFromSourceFile( QOpenGLShader::Fragment,
                                                ":/Common.frag" );
            s_program->link( );
            s_program->bind( );
            s_positionLoc = s_program->attributeLocation( "position" );
            s_normalLoc = s_program->attributeLocation( "normal" );
            s_texCoordLoc = s_program->attributeLocation( "texCoord" );
            s_modelMatrixLoc = s_program->uniformLocation( "modelMatrix" );
            s_viewMatrixLoc = s_program->uniformLocation( "viewMatrix" );
            s_projectionMatrixLoc = s_program->uniformLocation( "projectionMatrix" );
            s_lightPositionLoc = s_program->uniformLocation( "lightPosition" );
            s_lightViewProjectionMatrixLoc = s_program->uniformLocation( "lightViewProjectionMatrix" );
            s_modelViewNormalMatrixLoc =
                    s_program->uniformLocation( "modelViewNormalMatrix" );
            s_shadowTypeLoc = s_program->uniformLocation( "shadowType" );
            int textureLoc = s_program->uniformLocation( "texture" );
            int shadowLoc = s_program->uniformLocation( "shadowTexture" );
            s_program->setUniformValue( textureLoc,
                                        TEXTURE_UNIT - GL_TEXTURE0 );
            s_program->setUniformValue( shadowLoc,
                                        SHADOW_TEXTURE_UNIT - GL_TEXTURE0 );

            s_program->release( );
        }

        // 设置顶点坐标
        qreal semi = CUBE_LENGTH / 2.0;
        const QVector3D basicVertices[] =
        {
            QVector3D( semi, -semi, semi ),
            QVector3D( semi, -semi, -semi ),
            QVector3D( -semi, -semi, -semi ),
            QVector3D( -semi, -semi, semi ),
            QVector3D( semi, semi, semi ),
            QVector3D( semi, semi, -semi ),
            QVector3D( -semi, semi, -semi ),
            QVector3D( -semi, semi, semi )
        };

        const QVector3D normals[] =
        {
            QVector3D( 1.0, 0.0, 0.0 ),
            QVector3D( 0.0, 1.0, 0.0 ),
            QVector3D( 0.0, 0.0, 1.0 ),
            QVector3D( -1.0, 0.0, 0.0 ),
            QVector3D( 0.0, -1.0, 0.0 ),
            QVector3D( 0.0, 0.0, -1.0 )
        };

        const QVector2D texCoords[] =
        {
            QVector2D( 0.0, 0.0 ),
            QVector2D( 0.0, 1.0 ),
            QVector2D( 1.0, 0.0 ),
            QVector2D( 1.0, 1.0 )
        };

        m_vertices = new Vertex[VERTEX_COUNT];
        Vertex* v = m_vertices;

        // 前面
        v[0].set( basicVertices[7], normals[2], texCoords[2] );
        v[1].set( basicVertices[3], normals[2], texCoords[0] );
        v[2].set( basicVertices[0], normals[2], texCoords[1] );
        v[3].set( basicVertices[4], normals[2], texCoords[3] );
        v[4].set( basicVertices[7], normals[2], texCoords[2] );
        v[5].set( basicVertices[0], normals[2], texCoords[1] );

        // 后面
        v[6].set( basicVertices[5], normals[5], texCoords[2] );
        v[7].set( basicVertices[2], normals[5], texCoords[1] );
        v[8].set( basicVertices[6], normals[5], texCoords[3] );
        v[9].set( basicVertices[5], normals[5], texCoords[2] );
        v[10].set( basicVertices[1], normals[5], texCoords[0] );
        v[11].set( basicVertices[2], normals[5], texCoords[1] );

        // 上面
        v[12].set( basicVertices[4], normals[1], texCoords[2] );
        v[13].set( basicVertices[5], normals[1], texCoords[3] );
        v[14].set( basicVertices[6], normals[1], texCoords[1] );
        v[15].set( basicVertices[4], normals[1], texCoords[2] );
        v[16].set( basicVertices[6], normals[1], texCoords[1] );
        v[17].set( basicVertices[7], normals[1], texCoords[0] );

        // 下面
        v[18].set( basicVertices[0], normals[4], texCoords[3] );
        v[19].set( basicVertices[2], normals[4], texCoords[0] );
        v[20].set( basicVertices[1], normals[4], texCoords[1] );
        v[21].set( basicVertices[0], normals[4], texCoords[3] );
        v[22].set( basicVertices[3], normals[4], texCoords[2] );
        v[23].set( basicVertices[2], normals[4], texCoords[0] );

        // 左面
        v[24].set( basicVertices[2], normals[3], texCoords[0] );
        v[25].set( basicVertices[3], normals[3], texCoords[1] );
        v[26].set( basicVertices[7], normals[3], texCoords[3] );
        v[27].set( basicVertices[2], normals[3], texCoords[0] );
        v[28].set( basicVertices[7], normals[3], texCoords[3] );
        v[29].set( basicVertices[6], normals[3], texCoords[2] );

        // 右面
        v[30].set( basicVertices[4], normals[0], texCoords[2] );
        v[31].set( basicVertices[1], normals[0], texCoords[1] );
        v[32].set( basicVertices[5], normals[0], texCoords[3] );
        v[33].set( basicVertices[1], normals[0], texCoords[1] );
        v[34].set( basicVertices[4], normals[0], texCoords[2] );
        v[35].set( basicVertices[0], normals[0], texCoords[0] );

        m_vertexBuffer.setUsagePattern( QOpenGLBuffer::DynamicDraw );
        m_vertexBuffer.create( );
        m_vertexBuffer.bind( );
        m_vertexBuffer.allocate( v, VERTEX_COUNT * sizeof( Vertex ) );
        m_vertexBuffer.release( );

        // 设置纹理滤波
        m_texture.setMinificationFilter( QOpenGLTexture::LinearMipMapLinear );
        m_texture.setMagnificationFilter( QOpenGLTexture::Linear );
    }
    ~CubeRenderer( void )
    {
        m_vertexBuffer.destroy( );
        m_texture.destroy( );
        delete []m_vertices;
        if ( --s_count == 0 )
        {
            delete s_program;
        }
    }
    void render( void )
    {
        s_program->bind( );
        m_vertexBuffer.bind( );

        // 绘制box
        int offset = 0;
        setVertexAttribute( s_positionLoc, GL_FLOAT, 3, offset );
        offset += 3 * sizeof( GLfloat );
        setVertexAttribute( s_normalLoc, GL_FLOAT, 3, offset );
        offset += 3 * sizeof( GLfloat );
        setVertexAttribute( s_texCoordLoc, GL_FLOAT, 2, offset );

        // 摄像机的MVP矩阵
        QMatrix4x4& viewMatrix = m_cube->m_view->viewMatrix( );
        s_program->setUniformValue( s_modelMatrixLoc, m_modelMatrix );
        s_program->setUniformValue( s_viewMatrixLoc, viewMatrix );
        s_program->setUniformValue( s_projectionMatrixLoc, m_cube->m_view->projectionMatrix( ) );
        s_program->setUniformValue( s_modelViewNormalMatrixLoc,
                                    ( viewMatrix * m_modelMatrix ).normalMatrix( ) );

        // 是否启用实时阴影
        //s_program->setUniformValue( s_shadowTypeLoc, m_shadowType );

        if ( m_shadowType != NoShadow )
        {
            s_program->setUniformValue( s_lightPositionLoc, m_cube->m_view->lightPosition( ) );
            s_program->setUniformValue( s_lightViewProjectionMatrixLoc,
                                        m_cube->m_view->lightViewProjectionMatrix( ) );

            glActiveTexture( SHADOW_TEXTURE_UNIT );
            glBindTexture( GL_TEXTURE_2D, m_cube->m_view->shadowTexture( ) );
        }

        glActiveTexture( TEXTURE_UNIT );
        m_texture.bind( );
        glDrawArrays( GL_TRIANGLES, 0, VERTEX_COUNT );
        m_texture.release( );
        m_vertexBuffer.release( );

        s_program->release( );
    }
    void renderShadow( void )
    {
        m_vertexBuffer.bind( );
        QOpenGLShaderProgram* depthProgram = m_cube->m_view->depthProgram( );
        depthProgram->enableAttributeArray( "position" );
        depthProgram->setAttributeBuffer(
                    "position",             // 位置
                    GL_FLOAT,               // 类型
                    0,                      // 偏移
                    3,                      // 元大小
                    sizeof( Vertex ) );     // 迈

        depthProgram->setUniformValue( "modelMatrix", m_modelMatrix );
        glDrawArrays( GL_TRIANGLES, 0, VERTEX_COUNT );
        m_vertexBuffer.release( );
    }
    void resize( qreal length )
    {
        qreal semi = length / 2.0;
        m_vertexBuffer.bind( );
        Vertex* v = (Vertex*)m_vertexBuffer.map( QOpenGLBuffer::WriteOnly );
        for ( int i = 0; i < VERTEX_COUNT; ++i )
        {
            canonicalPosition( v[i].position );
            v[i].position *= semi;
        }
        m_vertexBuffer.unmap( );
        m_vertexBuffer.release( );
    }
    void setVertexAttribute( int attributeLocation,
                             GLenum elementType,
                             quint32 elementSize,
                             quint32 offset )
    {
        s_program->enableAttributeArray( attributeLocation );
        s_program->setAttributeBuffer( attributeLocation,      // 位置
                                       elementType,            // 类型
                                       offset,                 // 偏移
                                       elementSize,            // 元大小
                                       sizeof( Vertex ) );     // 迈
    }
    void loadTextureFromSource( const QUrl& source )
    {
        QString imagePath = QQmlFile::urlToLocalFileOrQrc( source );
        m_texture.setData( QImage( imagePath ).mirrored( ) );

        // 设置纹理滤波
        m_texture.setMinificationFilter( QOpenGLTexture::LinearMipMapLinear );
        m_texture.setMagnificationFilter( QOpenGLTexture::Linear );
    }
    void translate( const QVector3D& translate )
    {
        m_modelMatrix.setToIdentity( );
        m_modelMatrix.translate( translate );
    }
protected:
    Cube*                   m_cube;

    QMatrix4x4              m_modelMatrix;
    ShadowType              m_shadowType;
    QOpenGLBuffer           m_vertexBuffer;
    QOpenGLTexture          m_texture;
    Vertex*                 m_vertices;

    static QOpenGLShaderProgram* s_program;
    static int s_positionLoc, s_normalLoc,
    s_texCoordLoc, s_modelMatrixLoc,
    s_viewMatrixLoc, s_projectionMatrixLoc,
    s_lightViewProjectionMatrixLoc,
    s_lightPositionLoc, s_modelViewNormalMatrixLoc, s_shadowTypeLoc;
    static int              s_count;        // 计数
};

QOpenGLShaderProgram* CubeRenderer::s_program = Q_NULLPTR;
int CubeRenderer::s_positionLoc,
CubeRenderer::s_normalLoc,
CubeRenderer::s_texCoordLoc,
CubeRenderer::s_modelMatrixLoc,
CubeRenderer::s_viewMatrixLoc,
CubeRenderer::s_projectionMatrixLoc,
CubeRenderer::s_lightViewProjectionMatrixLoc,
CubeRenderer::s_lightPositionLoc,
CubeRenderer::s_modelViewNormalMatrixLoc,
CubeRenderer::s_shadowTypeLoc,
CubeRenderer::s_count = 0;

Cube::Cube( QObject* parent ): QObject( parent )
{
    m_length = CUBE_LENGTH;
    m_lengthIsDirty = false;
    m_sourceIsDirty = false;
    m_translateIsDirty = false;
}

void Cube::initialize( void )
{
    m_renderer = new CubeRenderer( this, CubeRenderer::SimpleShadow );
}

void Cube::render( void )
{
    m_renderer->render( );
}

void Cube::renderShadow( void )
{
    m_renderer->renderShadow( );
}

void Cube::sync( void )
{
    if ( m_lengthIsDirty )
    {
        m_renderer->resize( m_length );
        m_lengthIsDirty = false;
    }
    if ( m_sourceIsDirty )
    {
        m_renderer->loadTextureFromSource( m_source );
        m_sourceIsDirty = false;
    }
    if ( m_translateIsDirty )
    {
        m_renderer->translate( m_translate );
        m_translateIsDirty = false;
    }
}

void Cube::release( void )
{
    delete m_renderer;
}

void Cube::setLength( qreal length )
{
    if ( m_length == length ) return;
    m_length = length;
    emit lengthChanged( );
    m_lengthIsDirty = true;
}

void Cube::setSource( const QUrl& source )
{
    if ( m_source == source ) return;
    m_source = source;
    emit sourceChanged( );
    m_sourceIsDirty = true;
}

void Cube::setTranslate( const QVector3D& translate )
{
    if ( m_translate == translate ) return;
    m_translate = translate;
    emit translateChanged( );
    m_translateIsDirty = true;
}

程序运行的截图如下:

源代码下载地址:这里

时间: 2024-10-13 10:53:26

阴影映射(Shadow Map)的研究(五)的相关文章

阴影映射(Shadow Map)的研究(三)

阴影映射(Shadow Map)的研究(三) 最近为了自己制作的项目可是吃了不少苦头,这其中关键的一点就是想要实现阴影映射(Shadow Map).为了实现目标,我参考了网络上很多相关的资料,也看了一些案例,最终花了我一个月的时间将这个效果实现了. 阴影映射这样的效果,其实在即将发布的Qt 3D中已经有相关的介绍,KDAB中有一篇文章<Shadow Mappingin Qt3D 2.0>就在Qt 3D的框架上实现了阴影映射.不过当时这个效果是假定目标机器支持OpenGL 3.0规范的,目前大部

阴影映射(Shadow Map)的研究(二)

阴影映射(Shadow Map)的研究(二) 上一篇文章介绍了我对Z缓存的较为详细的研究.这里之所以对Ze求导函数,是因为的我们需要寻找它的变化曲线,从而找到极值点,这样就能够确定Ze相对于zw的疏密分布情况.幸运的是,我们找到的导函数是双曲函数,并且我们关心的的右侧是单调递增的. 蒋彩阳原创文章,首发地址:http://blog.csdn.net/gamesdev/article/details/44946763.欢迎同行前来探讨. 引出上一篇文章的结论,当 时,导函数取得最大值.但是在Zw∈

阴影映射(Shadow Map)的研究(四)

阴影映射(Shadow Map)的研究(四) 上一篇文章粗略地介绍了要实现OpenGL ES 2.0的阴影映射所需的知识难点,现在简略地说明一下:1.FBO:2.着色器:3.float的分拆以及组合.上篇文章虽然说已经成功地移植了来自Java编写的Android下阴影映射的效果,但这边采用的很大程度上是OpenGL原生代码编写的内容,接下来的目标是采用自Qt 5起就逐渐采用的Qt对OpenGL的封装类,用面向对象的思维来处理OpenGL对象,这样让代码更加优雅. 1.FBO 首先说一下FBO.在

阴影映射(Shadow Map)的研究(六)

阴影映射(Shadow Map)的研究(六) 成功地将阴影映射与Qt Quick 2整合之后,接下来可以将阴影映射的效果变得更漂亮一些.如果你成功地运行过我制作的演示程序,那么就会发现,阴影映射的效果并不是那么理想,可能有噪点(粉刺)的出现.这个是和阴影的产生相关,主要还是由于阴影映射这个算法它要求产生的阴影精度是有限的.很多改进的算法都是围绕着如何让阴影更加自然进行研究的.这里我也尝试模仿了一个稍微简单的算法:PCF算法. PCF算法的理念也比较简单,简言之就是让产生的阴影更加模糊.它主要在阴

Shadow Map阴影贴图技术之探 【转】

这两天勉勉强强把一个shadowmap的demo做出来了.参考资料多,苦头可不少.Shadow Map技术是目前与Shadow Volume技术并行的传统阴影渲染技术,而且在游戏领域可谓占很大优势.本篇是第一辑.——ZwqXin.comShadow Map的原理很简单,但是实现起来到处是雷.当然这只是我的体会.恩,不过就是“从光源处看场景,那些看不见的区域全部都该是阴影”.很容易看出,与针对 特定模型的Shadow Volume不同,Shadow Map是针对场景的.这就是说,对一个光源应用一次

[OpenGL] shadow mapping(实时阴影映射)

source:原文地址 code:点击可以直接下载源代码 1978年,Lance Williams在其发表的论文<Casting curved shadows on curved surfaces>中提出了Shadow mapping算法,从那以后,该算法在离线渲染和实时渲染两个领域都得到了广泛的应用.皮尔斯动画工作室的Renderman渲染器.以及一些知名电影如<玩具总动员>都使用了shadow mapping技术. 在众多图形应用的阴影技术中,shadow mapping只是产

Shadow Map阴影贴图技术之探

这两天勉勉强强把一个shadowmap的demo做出来了.参考资料多,苦头可不少.Shadow Map技术是目前与Shadow Volume技术并行的传统阴影渲染技术,而且在游戏领域可谓占很大优势.本篇是第一辑.--ZwqXin.comShadow Map的原理很简单,但是实现起来到处是雷.当然这只是我的体会.恩,不过就是"从光源处看场景,那些看不见的区域全部都该是阴影".很容易看出,与针对特定模型的Shadow Volume不同,Shadow Map是针对场景的.这就是说,对一个光源

OpenGL 阴影之Shadow Mapping和Shadow Volumes

先说下开发环境.VS2013,C++空项目,引用glut,glew.glut包含基本窗口操作,免去我们自己新建win32窗口一些操作.glew使我们能使用最新opengl的API,因winodw本身只包含opengl 1.1版本的API,根本是不能用的. 其中矩阵计算采用gitHub项目openvr中的三份文件, Vectors.h ,Matrices.h, Matrices.cpp,分别是矢量与点类,矩阵类,我们需要的一些操作,矢量的叉乘和点乘,矩阵转置,矩阵的逆,矩阵与矢量相剩等. 这里主要

OpenGL阴影,Shadow Mapping(附源程序)

实验平台:Win7,VS2010 先上结果截图(文章最后下载程序,解压后直接运行BIN文件夹下的EXE程序): 本文描述图形学的两个最常用的阴影技术之一,Shadow Mapping方法(另一种是Shadow Volumes方法).在讲解Shadow Mapping基本原理及其基本算法的OpenGL实现之后,将继续深入分析解决几个实际问题,包括如何处理全方向点光源.多个光源.平行光.最近还有可能写一篇Shadow Volumes的博文(目前已经将基本理论弄清楚了),在那里,将对Shadow Ma