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