码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-05-25 16:44:51      阅读:171      评论:0      收藏:0      [点我收藏+]

标签:qt   opengl   

阴影映射(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;
}

       程序运行的截图如下:

技术分享

       源代码下载地址:这里

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

标签:qt   opengl   

原文地址:http://blog.csdn.net/gamesdev/article/details/45969845

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!