Rendering Process Breakdown

The Logic::update function controls the entire rendering process. This is outlined in the steps below:

Contents

Loop

Detailed Processes

The Render Loop

Update the entities

Here we update the entities and lights, set up the ambient lights, and enable our depth test.

    // Update the scenegraph nodes and lights
    scene->graph()->updateAttachedEntities( delta, scene->lights() );
    
    // Load the identity matrix so we can position our lights
    glLoadIdentity();
    
    // Set up the ambient light
    GLColour ambient( 0, 0, 0, 1 );
    scene->lights().getAmbient( ambient );
    glLightModelfv( GL_LIGHT_MODEL_AMBIENT, &ambient.R );

    // Enable depth testing
    glEnable( GL_DEPTH_TEST );
    
    // Enable depth writes
    glDepthMask( true );

Write the Shadow Passes

Here we render each of our light's shadow passes. See Render the Shadow Passes.

Render the Cube Maps

Here we render each of our cube maps. See Render the Cube Maps.

Render the Depth Pass

Here we are rendering our first full-size scene. We apply our camera's matrices, clear the depth and colour buffers, activate our pass target and render it. Then, we upload the image to the render target. Now that we have rendered our full size image, we can deactivate depth writes to save time when rendering later passes.

    // Apply the camera
    pCamera->apply();

    // Clear the buffer
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    // Set the dof pass
    m_DepthPass->activate();

    // RENDER THE SCENEGRAPH NODES

    // Unset the depth pass
    m_DepthPass->deactivate();

    // Disable depth writes
    glDepthMask( false );

Render the Diffuse Pass

We activate our diffuse pass target and render to it. Then we upload it to the target.

    // Set up the diffuse pass
    m_DiffusePass->activate();

    // Render the diffuse pass
    renderDiffusePass( scene, delta );

    // Copy back
    m_DiffusePass->deactivate();

Render the Light Pass

Similarly, we render our light pass. However, to do this, we need to render for each light with blending in additive mode. This means clearing the buffer, enabling blending, and adding each light's contribution over the top of the last. See the Rendering the Light Passes section.

Render the Motion Blur

Now we need to turn on our depth writes again and render the motion vectors pass. See the Rendering the Motion Vectors section. After this we unbind all our textures.

    // Enable depth writes
    glDepthMask( true );

    // Bind the pass
    m_MotionPass->activate();
    
    // Render the motion vectors
    renderMotionPass( scene, delta );
    
    // Upload
    m_MotionPass->deactivate();

    // Unbind the textures
    unbindTextures();

Combine the Diffuse and Light Passes

Here we render a full screen quad with the diffuse and light targets applied to it. However, we do not call m_LightScenePass->deactivate() as we will be modifying this pass further in the next two stages.

    // Bind the lit scene pass
    m_LitScenePass->activate();

And rendering the pass:

    // Disable depth testing
    glDisable( GL_DEPTH_TEST );

    // Disable depth writes
    glDepthMask( false );

    // Reset the camera
    glMatrixMode( GL_PROJECTION );
        glPushMatrix();
        glLoadIdentity();
    glMatrixMode( GL_MODELVIEW );
    glPushMatrix();
    glLoadIdentity();

    // Use the shader
    m_LightShader->useProgram();
    m_LightShader->update( m_Time, delta );

    // Get the appropriate texture unit
    glActiveTextureARB( GL_TEXTURE0_ARB );
    diffuse->bind();
    glActiveTextureARB( GL_TEXTURE1_ARB );
    lightpass->bind();
    
    // Draw the quad
    renderFullScreenQuad();

    // Enable depth writes
    glDepthMask( true );

    // Enable depth testing
    glEnable( GL_DEPTH_TEST );

    // Revert back to the old matrices
    glMatrixMode( GL_PROJECTION );
    glPopMatrix();
    glMatrixMode( GL_MODELVIEW );
    glPopMatrix();

Render the Specular Pass

Here we turn on alpha blending and render the scene again, but this time overlaying the cubemapped surfaces. As we are changing the blend mode to a linear interpolation, any surfaces without cubemaps will not affect the current image. Also, we can render the cube mapped surfaces with an alpha to blend it with the current pass.

    // Enable blending
    glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

    // Apply the camera
    pCamera->apply();

For rendering the pass, see Rendering the Specular Pass.

Render the Emissive Pass

The emissive pass is a pass that does not get affected by light, so it must be added on at the end. We switch back to additive blend mode and render again. Blending is already active from the last pass, so all we need to do is change the blend mode.

    // Enable blending
    glBlendFunc( GL_ONE, GL_ONE );
    
    // RENDER EMISSIVE PASS

    // Disable blending
    glDisable( GL_BLEND );

    // Deactivate the litscene pass
    m_LitScenePass->deactivate();

For rendering the pass, see Rendering the Emissive Pass.

Reset the Camera

Now we are rendering fullscreen quads for the rest of the frame. Therefore, we should reset our camera.

    resetCamera();

Render the Bloom Passes

First, we render our bloom preparation pass, which cuts off the darker areas of the image. See the bloomprep.shader shader for how this is done.

    // Reset the viewport
    m_BlurV->activate();
    
    // Clear the buffer 
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    // Use the shader
    m_BlurPrepShader->useProgram();
    m_BlurPrepShader->update( m_Time, delta );
    
    // Set the blur
    m_BlurPrepShader->getUniform( "threshold", temp );
    m_BlurPrepShader->setUniform1f( temp, threshold );
    
    // Get the appropriate texture unit
    glActiveTextureARB( GL_TEXTURE0_ARB );
    glBindTexture( GL_TEXTURE_2D, m_LitScenePass->target()->id() );
    
    // Draw the quad
    renderFullScreenQuad();

    // Copy it
    m_BlurV->deactivate();

Now we render the horizontal and vertical blur passes. The code is similar to the above, only different targets and shaders are used. The final result is stored in the pass called m_BlurV. The shaders for this are blurpph.shader and blurppv.shader.

Set Up Depth of Field

Now we set the viewport to the size of the screen and bind our final, lit scene and our depth passes to different texture slots. This is preparing for the depth of field pass that happens next.

    // Set the viewport
    GLPass::SetDefaultViewport( m_DiffusePass->target()->width(), m_DiffusePass->target()->height() );

    // Bind the default texture
    glActiveTextureARB( GL_TEXTURE0_ARB );
    glBindTexture( GL_TEXTURE_2D, m_LitScenePass->target()->id() );

    // Set the depth buffer
    glActiveTextureARB( GL_TEXTURE1_ARB );
    glBindTexture( GL_TEXTURE_2D, m_DepthPass->target()->id() );

Render the Depth of Field

We render this to a full size target so we can combine it with the bloom pass in the next step. Our textures are already bound, so it's a simple matter of rendering it.

    // Activate the diffuse pass
    m_DiffusePass->activate();

    // Use the DOF shader
    m_DOFShader->useProgram();
    m_DOFShader->update( m_Time, delta );

    // Set the bias
    m_DOFShader->getUniform( "bias", temp );
    m_DOFShader->setUniform1f( temp, DOFbias );
    m_DOFShader->getUniform( "blurclamp", temp );
    m_DOFShader->setUniform1f( temp, DOFclamp );

    // Draw the quad
    renderFullScreenQuad();
        
    // Upload to the diffuse pass
    m_DiffusePass->deactivate();

Add the Bloom

Now we set the diffuse pass and bloom passes as texture units and combine them using a shader that will add them together.

    // Bind the default texture
    glActiveTextureARB( GL_TEXTURE0_ARB );
    glBindTexture( GL_TEXTURE_2D, m_DiffusePass->target()->id() );
    
    // Use the shader
    m_PassCombineShader->useProgram();
    m_PassCombineShader->update( m_Time, delta );

    // Set the count variable
    m_PassCombineShader->getUniform( "count", temp );
    m_PassCombineShader->setUniform1f( temp, 2 );
    m_PassCombineShader->getUniform( "mult", temp );
    m_PassCombineShader->setUniform1f( temp, 1 );

    // Get the appropriate extra texture unit
    glActiveTextureARB( GL_TEXTURE1_ARB );
    glBindTexture( GL_TEXTURE_2D, m_BlurV->target()->id() );
    
    // Draw the quad
    renderFullScreenQuad();

And that's the end.

Render the Shadow Passes

Rendering All the Shadow Maps

Here we disable the colour writes and render only to the depth buffer. Using glPolygonOffset reduces aliasing artefacts by offsetting each polygon by a small amount. Then we render each spotlight's and each pointlight's shadow map. The result is stored in the light itself.

    // Ignore if we haven't got a shadow shader
    if ( !m_WhiteShader ) return;
    
    // Clear to black
    glClear( GL_COLOR_BUFFER_BIT );

    // Switch of colour writes
    glColorMask( false, false, false, false );

    // This helps to reduce artefacts
    glPolygonOffset(8.0f, 4.0f);

    // This will reduce artefacts
    glEnable( GL_POLYGON_OFFSET_FILL );

    // Get the scenegraph
    GLSceneGraph * graph = scene->graph();

    // For each spot light...
    for ( uint i = 0; i < scene->lights().spotLightCount(); ++i )
    {
        // Get the light
        const GLSpotLight * light = scene->lights().getSpotLight( i );
        
        // Render the pass
        renderShadowPasses( graph, delta, light );
    }
        
    // For each point light...
    for ( uint i = 0; i < scene->lights().pointLightCount(); ++i )
    {
        // Get the light
        const GLPointLight * light = scene->lights().getPointLight( i );
        
        // Render the pass
        renderShadowPasses( graph, delta, light );
    }
        
    // Switch on colour writes
    glColorMask( true, true, true, true );
    
    // Disable the offset fill
    glDisable( GL_POLYGON_OFFSET_FILL );

Setting up the Shadow Map

Here we render the scene from the light's point of view. This is followed be Rendering the Shadow Map.

    void    Logic::renderShadowPasses( const GLSceneGraph * graph, float delta, const GLLight * light )
    {
        // If it's not shadowed, ignore it
        if ( !light->shadowed() ) return;

        // The shadow map settings
        const GLRenderTarget * target = 0;
        
        // Get each shadow map
        for ( uint m = 0; m < light->shadowMapCount(); ++m )
        {
            // Get the shadow target
            if ( FAILED( light->getShadowMapTarget( m, &target ) ) )
                return;
        
            // Render the scene
            renderShadowPass( graph, delta, light, m, target );

            // Reset the target
            target = 0;
        }
    }

Rendering the Shadow Map

This renders the depth map to the target.

    void  Logic::renderShadowPass( const GLSceneGraph * graph, float delta, const GLLight * light, uint map, const GLRenderTarget * target )
    {
        GLGeometry * oldGeom = 0;
        GLShader * shader = m_ZShader;
        
        // Bind the target
        target->prepare();

        // Get the matrices
        TBMath::CMatrix P, V;

        // Get the light's projection and view matrix
        light->getMatrices( map, V, P );

        // Set the matices
        glMatrixMode( GL_PROJECTION );
            glLoadMatrix( P );
        glMatrixMode( GL_MODELVIEW );
        glLoadMatrix( V );

        // Clear the depth buffer
        glClear( GL_DEPTH_BUFFER_BIT );

        // Draw the scenegraph
        for ( uint i = 0; i < (uint)graph->nodeCount(); ++i )
        {
            // Get the node
            GLSceneGraphNode * node = graph->getNode( i );

            // Get it's geometry
            GLGeometry * geometry = node->geometry();

            // If it has no geometry, skip this node
            if ( !initRenderID( oldGeom, geometry, &oldGeom ) ) continue;

            // Set up the shader
            bindShader( shader, geometry, m_Time, delta );

            // Render the geometry
            renderNode( node, geometry, shader );
        
            // Finish
            shader->endVertexAttrs();
        }
        
        // Copy to the target
        target->upload();
    }

Render the Cube Maps

Rendering All the Cube Maps

Here we do the following:
  1. Set the perspective to a 90 degree same-aspect viewport,
  2. Check through each node for a cubemap,
  3. If a node has one, set the ignore flag and render its cubemap.

See Rendering a Cube Map.

    const GLSceneGraph * graph = scene->graph();

    // Load it
    glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluPerspective( 90, 1, 0.01f, 50.0f );
    glMatrixMode( GL_MODELVIEW );

    // Draw the scenegraph
    for ( uint i = 0; i < (uint)graph->nodeCount(); ++i )
    {
        // Get the node
        GLSceneGraphNode * node = graph->getNode( i );

        // Get the cubemap
        const GLCubeMapTarget * cube = node->cubeMapTarget();

        // If there is no cubemap, skip
        if ( !cube ) continue;

        // Ignore this node
        m_IgnoreNode = i;

        // Get the position
        GLVector p = node->transform().matrix().Origin();

        // Render this cube map
        renderCubeMap( scene, delta, cube, p );
    }

    // Reset the ignore flag
    m_IgnoreNode = -1;

Rendering a Cube Map

Here we position our camera at the heart of an object and render in each direction.

    // For each face...
    for ( uint i = 0; i < viewCubeFace; ++i )
    {
        // Clear the matrix
        glLoadIdentity();

        // Look in the face's direction
        // Rotate so we are facing in the correct direction
        switch ( i )
        {
        case GL_CUBEMAP_RIGHT:      gluLookAt( p.x, p.y, p.z, p.x + 1, p.y    , p.z    , 0, 1, 0 ); break; // +x
        case GL_CUBEMAP_LEFT:       gluLookAt( p.x, p.y, p.z, p.x - 1, p.y    , p.z    , 0, 1, 0 ); break; // -x
        case GL_CUBEMAP_UP:         gluLookAt( p.x, p.y, p.z, p.x    , p.y - 1, p.z    , 0, 0, 1 ); break; // +y
        case GL_CUBEMAP_DOWN:       gluLookAt( p.x, p.y, p.z, p.x    , p.y + 1, p.z    , 0, 0, 1 ); break; // -y
        case GL_CUBEMAP_FORWARD:    gluLookAt( p.x, p.y, p.z, p.x    , p.y    , p.z + 1, 0, 1, 0 ); break; // -z .
        case GL_CUBEMAP_BACKWARD:   gluLookAt( p.x, p.y, p.z, p.x    , p.y    , p.z - 1, 0, 1, 0 ); break; // +z .
        }

        // Clear the depth buffer
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

        // Render the diffuse scene
        m_CubeMapFaces[0]->activate();
        renderDiffusePass( scene, delta );
        m_CubeMapFaces[0]->deactivate();
    
        // Render the lights
        m_CubeMapFaces[1]->activate();
        renderLightPass( scene, delta );
        m_CubeMapFaces[1]->deactivate();

        // Set up the target
        cubemap->prepare();
        
        // Render the scene
        glPushMatrix();
            renderLitScene( scene, delta, m_CubeMapFaces[0]->target(), m_CubeMapFaces[1]->target() );
        glPopMatrix();

        // Push the target
        cubemap->upload( i );
    }

Rendering the Light Passes

Rendering All the Light Passes

Here we enable our light sources and render the scene for each light, adding the result to the previous frame.

    const GLSceneGraph * graph = scene->graph();
    
    // Clear the buffers
    glClear( GL_COLOR_BUFFER_BIT );

    // Switch on blending
    glEnable( GL_BLEND );
    
    // Set to add
    glBlendFunc( GL_ONE, GL_ONE );
        
    // Switch on the lights
    glEnable( GL_LIGHTING );
    
    // Write all the point light passes
    for ( uint i = 0; i < scene->lights().pointLightCount(); ++i )
    {
        // Get the light
        const GLPointLight * light = scene->lights().getPointLight( i );
        
        // Render the light
        renderLightPass( graph, delta, light, m_NormalPointShader );
    }

    // Write all the point light passes
    for ( uint i = 0; i < scene->lights().spotLightCount(); ++i )
    {
        // Get the light
        const GLSpotLight * light = scene->lights().getSpotLight( i );

        // Render the light
        renderLightPass( graph, delta, light, light->projectedTexture() ? m_NormalProjShader : m_NormalSpotShader );
    }
    
    // Disable blending
    glDisable( GL_BLEND );

    // Disable the lights
    glDisable( GL_LIGHTING );

Render a Light Pass

First, we need to set up our texture matrix to convert the [-1,1] texture coordinates to [0,1] to sample our shadow map.
    static const float mNormalizeMatrix[] = 
    { 
        0.5f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.5f, 0.0f, 0.0f, 
        0.0f, 0.0f, 0.5f, 0.0f, 
        0.5f, 0.5f, 0.5f, 1.0f
    };
    
    uint shadowCount = 0;
    TBMath::CMatrix V, P, I;
    
    // If there is no shader, skip this
    if ( !shader ) return;

Get the inverse of the model-view matrix so we can convert our shadow maps into a different space.

    // Get the inverse of the view matrix
    glGetFloatv( GL_MODELVIEW_MATRIX, V.m );
    V.InverseQuick( I );

Update the shader and loop through each entity.

    // Use the shader
    shader->useProgram();
    
    // Update the shader with some 
    // standard variables
    shader->update( m_Time, delta );
        
    // Bind the shadow maps to all the slots above 0
    int maps = 1;
    for ( uint i = 0; i < light->shadowMapCount(); ++i )
    {

Now get our shadow map from the light and bind it to the relevant slot, setting up the texture compare parameters.

        // Get the target
        const GLRenderTarget * target = 0;
        if ( FAILED( light->getShadowMapTarget( i, &target ) ) )
            continue;

        // Activate the slot
        glActiveTextureARB( GL_TEXTURE0_ARB + maps );

        // Bind the shadow map
        glBindTexture( GL_TEXTURE_2D, target->id() );
        // Set up the shadow parameters
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB );
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL );

Get the light's projection and view matrix, so we can look from it's direction. Stick this in the first texture matrix for our shader to access.

        // Get the light's projection and view matrix
        // and the camera's inverse view matrix
        light->getMatrices( i, V, P );
        
        // Switch to texture mode for this texture
        glMatrixMode( GL_TEXTURE );

        // Bind the light's projection matrix and the inverse view matrix
        glLoadMatrixf( mNormalizeMatrix );
        glMultMatrix( P );
        glMultMatrix( V );
        glMultMatrix( I );

        // Switch back to modelview
        glMatrixMode( GL_MODELVIEW );

        // Increment the map counter
        maps++;
    }

Get the projected texture (if any) and activate it in the last free slot. Set the shadow map count too.

    // Get the projected texture
    const GLTexture * project = light->projectedTexture();

    // Set the projected texture?
    glActiveTextureARB( GL_TEXTURE0_ARB + maps );
    if ( project )  glBindTexture( GL_TEXTURE_2D, project->id() );
    else            glBindTexture( GL_TEXTURE_2D, 0 );

    // Set the shadow count
    if ( !shader->getUniform( "vShadowCount", shadowCount ) )
        shader->setUniform1i( shadowCount, maps );

Now bind the light's parameters, enable it, and render each node.

    // Bind it
    light->bindParameters( 0 );

    // Set up the light
    light->enable( 0 );

    // The geometry and normal map
    GLGeometry * oldGeom = 0;
    const GLTexture * normalMap = 0;

    // Draw the scenegraph
    for ( uint i = 0; i < (uint)graph->nodeCount(); ++i )
    {
        // Ignore?
        if ( (int)i == m_IgnoreNode ) continue;

        // Get the node
        GLSceneGraphNode * node = graph->getNode( i );

        // Get it's geometry
        GLGeometry * geometry = node->geometry();

        // If it has no geometry, skip this node
        if ( !initRenderID( oldGeom, geometry, &oldGeom ) ) continue;

        // Set the material
        geometry->material().apply();
        
        // Activate the texture slot
        glActiveTextureARB( GL_TEXTURE0_ARB );
        normalMap = geometry->texture( Logic::TEXTURE_NORMAL );
        
        // Bind it
        if ( normalMap )
            normalMap->bind();
        else
            glBindTexture( GL_TEXTURE_2D, GLTexture::DefaultID() );
            
        // Bind the vertex attributes
        shader->beginVertexAttrs( geometry->vertexBufferData(), geometry->vertexBufferStride() );

        // Render the geometry
        renderNode( node, geometry, shader );
    
        // Finish
        shader->endVertexAttrs();
    }
    
    Finally, disable our light:
    \code
    // Switch of the base light
    light->disable( 0 );

Rendering the Specular Pass

Here we have to pass our cube map, various texture maps and the inverse modelview matrix into the shaders so that we can perform our cube mapping. See the shaders cubereflect.shader, cubereflectnormal.shader, cubereflectshinymap.shader, cubereflectshinymapnormal.shader, cuberefract.shader, cuberefractnormal.shader, cuberefractshinymap.shader, cuberefractshinymapnormal.shader.

    // Enable cube mapping
    glActiveTextureARB( GL_TEXTURE2_ARB );
    glEnable( GL_TEXTURE_CUBE_MAP );
    glDisable( GL_TEXTURE_2D );

    // The matrices used in this process
    TBMath::CMatrix mViewMatrix, mViewInvMatrix;

    // Draw the scenegraph
    for ( uint i = 0; i < (uint)graph->nodeCount(); ++i )
    {
        // Get the node
        GLSceneGraphNode * node = graph->getNode( i );

        // Get it's geometry
        GLGeometry * geometry = node->geometry();

        // If it has no geometry, skip this node
        if ( !initRenderID( oldGeom, geometry, &oldGeom ) ) continue;

        // Set the material
        geometry->material().apply();

        // Activate the texture slot
        diffusemap = geometry->texture( Logic::TEXTURE_DIFFUSE );
        normalmap = geometry->texture( Logic::TEXTURE_NORMAL );
        shinymap  = geometry->texture( Logic::TEXTURE_SHINYMAP );

        // Get the cubemap
        const IGLCubeMap * cube = node->cubeMapTarget();

        // If there's no cube, break
        if ( !cube ) continue;

        // Use the test cube?
        if ( useTestCube && testCube ) cube = testCube;
        
        // Bind the diffuse map
        glActiveTextureARB( GL_TEXTURE0_ARB );
        if ( diffusemap )diffusemap->bind();
        else            glBindTexture( GL_TEXTURE_2D, GLTexture::DefaultID() );
    
        // Bind the normal map
        glActiveTextureARB( GL_TEXTURE1_ARB );
        if ( normalmap )normalmap->bind();
        else            glBindTexture( GL_TEXTURE_2D, 0 );
    
        // Bind the cube map
        glActiveTextureARB( GL_TEXTURE2_ARB );
        if ( cube )     cube->bind();
        else            glBindTexture( GL_TEXTURE_CUBE_MAP, 0 );

        // Bind the shininess map
        glActiveTextureARB( GL_TEXTURE3_ARB );
        if ( shinymap ) shinymap->bind();
        else            glBindTexture( GL_TEXTURE_2D, 0 );
    
        // Get the shader
        const GLShader * shader = selectShader( node, true, 0 );
        
        // Set up the shader
        if ( shader ) bindShader( shader, geometry, m_Time, delta );

        // Push the matrix
        glPushMatrix();

        // Get the eye position
        TBMath::CMatrix mViewMatrix, mViewInvMatrix;
        glGetFloatv( GL_MODELVIEW_MATRIX, mViewMatrix.m );
        mViewMatrix.InverseQuick( mViewInvMatrix );

        // Plug the inverted modelview matrix into the first texture matrix
        glActiveTextureARB( GL_TEXTURE0_ARB );
        glMatrixMode( GL_TEXTURE );
            glLoadMatrix( mViewInvMatrix );
        glMatrixMode( GL_MODELVIEW );

        // Multiply the node's matrix with the current world matrix
        glMultMatrix( node->transform().matrix() );
        
        // Spin
        glRotatef( m_Time * node->spin(), 0, 1, 0 );

        // Set the reflectivity and refractive index
        uint tmp = 0;
        if ( !shader->getUniform( "reflectivity", tmp ) )
            shader->setUniform1f( tmp, geometry->material().reflectivity );
        if ( !shader->getUniform( "refractiveindex", tmp ) )
            shader->setUniform1f( tmp, geometry->material().refractiveindex );

        // Render the geometry
        geometry->render( shader );

        // Pop the world matrix
        glPopMatrix();

        // Finish
        if ( shader ) shader->endVertexAttrs();
    }

    // Disable cube mapping
    glActiveTextureARB( GL_TEXTURE2_ARB );
    glDisable( GL_TEXTURE_CUBE_MAP );
    glEnable( GL_TEXTURE_2D );

Rendering the Emissive Pass

Here we bind only our emissive texture and render. If the node doesn't have an emissive texture, the node is rendered in black.

    // Draw the scenegraph
    for ( uint i = 0; i < (uint)graph->nodeCount(); ++i )
    {
        // Get the node
        GLSceneGraphNode * node = graph->getNode( i );

        // Get it's geometry
        GLGeometry * geometry = node->geometry();

        // If it has no geometry, skip this node
        if ( !initRenderID( oldGeom, geometry, &oldGeom ) ) continue;

        // Set the material
        geometry->material().apply();

        // Activate the texture slot
        emissive = geometry->texture( Logic::TEXTURE_EMISSIVE );
        
        // Ignore if no emissive
        if ( !emissive ) continue;

        // Bind the emissive map
        glActiveTextureARB( GL_TEXTURE0_ARB );
        if ( emissive ) emissive->bind();
        else            glBindTexture( GL_TEXTURE_2D, 0 );
    
        // Get the shader
        const GLShader * shader = selectShader( node, true, 0 );
        
        // Set up the shader
        if ( shader ) bindShader( shader, geometry, m_Time, delta );

        // Render the node
        renderNode( node, geometry, shader );

        // Finish
        if ( shader ) shader->endVertexAttrs();
    }

Rendering the Motion Vectors

This passes in the modelviewprojection matrix for the previous frame. Each vertex is transformed into the old modelviewprojection space and then compared with the new values for the same vertex. The result is a motion vector in screen space, which is used as a blur coefficient. The shaders are motionblur.shader and motionblurdof.shader.

    // Clear the buffers
    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
    
    // Get the projection matrix
    float mProjection[16];
    glGetFloatv( GL_PROJECTION_MATRIX, mProjection );

    // Draw the scenegraph
    for ( uint i = 0; i < (uint)graph->nodeCount(); ++i )
    {
        // Get the node
        GLSceneGraphNode * node = graph->getNode( i );

        // Get it's geometry
        GLGeometry * geometry = node->geometry();

        // If it has no geometry, skip this node
        if ( !initRenderID( oldGeom, geometry, &oldGeom ) ) continue;
        
        // Get the previous world matrix
        M = node->oldTransform();

        // Get the shader
        const GLShader * shader = m_MotionVecs;
        
        // Set up the shader
        if ( shader ) bindShader( shader, geometry, m_Time, delta );
                
        // Update with the previous matrix
        uint temp = 0;
        if ( !m_MotionVecs->getUniform( "motionscale", temp ) )
            m_MotionVecs->setUniform1f( temp, motionscale );
        
        // Load the motion matrix into the texture matrix
        glActiveTextureARB( GL_TEXTURE0_ARB );
        glMatrixMode( GL_TEXTURE );
            glLoadMatrixf( mProjection );
            glMultMatrix ( M );
        glMatrixMode( GL_MODELVIEW );

        // Render the node
        renderNode( node, geometry, shader, true );

        // Finish
        if ( shader ) shader->endVertexAttrs();
    }

Generated on Fri Mar 23 12:55:04 2007 for glsldemo by  doxygen 1.5.1-p1