It is currently Sun Aug 20, 2017 5:46 pm

All times are UTC - 5 hours




 Page 1 of 1 [ 8 posts ] 
Author Message
 Post subject:
PostPosted: Thu Apr 05, 2007 8:12 pm 
Site Admin

Joined: Sun Feb 11, 2007 8:59 am
Posts: 1105
Location: Ontario Canada
The problem when rendering transparent objects is fixed in this video by introducing two render passes.
VMK27A/B has been split into two parts so that the download file size is a little bit easier to work with. Because of this, VMK27A will only play up to the beginning of 20min. VMK27B contains the rest of the video.  After you download both parts, you can extract all VMK files (from 27A and 27B) into one directory to allow you to watch the entire VMK in one go.
 One thing that I forgot to show in the VMK is that you will need to call DeleteAllAlphaObjects(); from within the Scene::ClearAll() or else you will have a memory leak.


Offline
 Profile  
 
 Post subject: Re: GameDev VMK 27 - Two Render Passes
PostPosted: Thu Apr 26, 2007 6:07 am 
Site Admin

Joined: Sun Feb 11, 2007 8:59 am
Posts: 1105
Location: Ontario Canada
I just discovered that there is a problem with the way that GL_BLEND is handled. You should always pair all glEnable calls with glDisable, however if you look closely, this is not the case. The fix is shown in VMK29E.


Offline
 Profile  
 
 Post subject: Re: GameDev VMK 27 - Two Render Passes
PostPosted: Sat May 19, 2007 10:35 pm 
Site Admin

Joined: Sun Feb 11, 2007 8:59 am
Posts: 1105
Location: Ontario Canada
I was working on some code and I noticed a nasty bug in the 2nd render pass. To correct it, update the code found in Scene::Update.... from this:

glPushMatrix();

glLoadMatrixf(&(*it)->f16Matrix[0]);
(*it)->pShape->RenderOGL(true);

glPopMatrix();


to this...

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

glLoadMatrixf(&(*it)->f16Matrix[0]);
(*it)->pShape->RenderOGL(true);

glMatrixMode(GL_MODELVIEW);
glPopMatrix();


Last edited by Marek on Thu Nov 20, 2008 7:35 am, edited 1 time in total.

Offline
 Profile  
 
 Post subject: GameDev-VMK027-TwoRenderPasses: questions & improvements
PostPosted: Wed Nov 19, 2008 4:52 pm 

Joined: Wed Aug 06, 2008 7:53 pm
Posts: 182
Location: Russia
Marek once said, not wrote:
The idea under the hood of this tutorial is one of the kind, exclusive, 1st time viewing anywhere in the world, and also not very intuitive but actually quite simple.


A SMALL BUG (?)

It seems I found a bug. I'm not sure, so feel free to correct or even punish me. This bug isn't critical but may deteorate engine's performance.
--> The nature of NodeGeometry::Render() is recursive. Thus, if during the second pass you call Node::Render() for chosen (transparent) nodes this method calls the children recursively as well. The point is that for the 2nd pass this is undesirable behavior.


PERFORMANCE CONCERNS.

1. It should be said that local static variables do not safe CPU time (as you maybe know already). Actually "static" attribute prevents compiler of doing good optimization, such as arranging the local variables in registers only. If variable has "static" specifier then compiler has to hold it in memory.
From the other side, the allocation of the whole bunch of local (automatic) variables happens instantly, just one CPU opcode, such as "SUB ESP, size_of_all_local_vars". The destroying of local vars is also costs 1 or 0 opcodes: "ADD ESP, size_of_all_local_vars" (1 opcode) or "RET size_of_all_local_vars" (0 extra opcodes since compiler has to generate "RET" (return) to leave the function anyway).

2. Scene::Update(). There is a (pceudo) code from video:
   glMatrixMode( GL_MODELVIEW );
   for (...) {
      glMatrixMode( GL_MODELVIEW );
      glPushMatrix();

      glLoadMatrixf( ... );
      (*it)->Render( ... );

      glMatrixMode( GL_MODELVIEW );
      glPopMatrix();
   }

Can we remove Push/Pop matrix out of the cycle? Like this:
   glMatrixMode( GL_MODELVIEW );
   glPushMatrix();
   for (...) {
      glMatrixMode( GL_MODELVIEW );
      glLoadMatrixf( ... );
      (*it)->Render( ... );
   }
   glMatrixMode( GL_MODELVIEW );
   glPopMatrix();


3. A pceudo code, again, in Scene::Update(). My blending management:
//
// 1st pass: render all opaque objects and store all transparent ones
//
glPushAttrib( GL_BLEND );
glDisable( GL_BLEND );
GetRoot()->Render( .. );
//
// 2nd pass: render all transparent objects
//
glEnable( GL_BLEND );

for( ... ) {
... ->Render( .. ) chosen nodes ...
}

glPopAttrib(); // GL_BLEND


4. I've managed to avoid the memory allocation (for transparent objects). They say (again) that it is strongly not recommended to allocate memory during the Frame. The idea is that vector::clear() actually do not return memory back to the system, but keeps it as a reserve! More on that can be read in "Effective STL" by Scott Meyers. To better understand this fact try this code:
#include <iostream>
#include <vector>
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
    vector< size_t > v;

    cout << "capacity() by default = " << v.capacity() << endl;

    for (size_t t = 0; t < 1024; ++t)
        v.push_back( t );

    cout << "capacity() after inserting " << v.size() << " items is " << v.capacity() << endl;

    v.clear();

    cout << "capacity() after clear() is " << v.capacity()
         << "; size() after clear() is " << v.size() << " ;)" << endl;

    v.reserve( 0 );
    cout << "capacity() after reserve( 0 ) is " << v.capacity() << endl;

    vector< size_t >(v).swap(v); // swap trick to make vector::reserved = 0

    cout << "capacity() after swap trick vector< size_t >(v).swap(v) is " << v.capacity() << endl;

    return 0;
}

Output (may vary):
Quote:
capacity() by default = 0
capacity() after inserting 1024 items is 1066
capacity() after clear() is 1066; size() after clear() is 0 ;)
capacity() after reserve( 0 ) is 1066
capacity() after swap trick vector< size_t >(v).swap(v) is 0

Using this fact we can write code, that does not allocate memory continuously in each game frame (but only once).

PS. I must admit it is much easier to improve than invent. :roll:
PS. To enforce vector to return memory back to the system heap we can use famous swap() trick:
vector< size_t >( v ).swap( v );


Last edited by BugHunter on Wed Nov 26, 2008 7:48 am, edited 5 times in total.


_________________
«Computer scientists deal with algorithms that you may call practical in theory but unpractical in practice.» © Timothy Gowers
Offline
 Profile  
 
 Post subject: The Code
PostPosted: Wed Nov 19, 2008 5:16 pm 

Joined: Wed Aug 06, 2008 7:53 pm
Posts: 182
Location: Russia
To keep the code cleaner I added an extra container the only purpose of which is to hold sorted array of stuff we use for 2-pass rendering as Marek proposed in VMK27. Internally 2 vectors are used. The 1st is unsorted vector of stuructures is used as a raw storage. The 2nd vector contains indices for the 1st vector. 2nd vector is sorted by the "distance" attribute.


// in H file

//------------------------------------------------------------------------------
// Forward declarations
//------------------------------------------------------------------------------
class Node;
class NodeTransform;
class NodeShape;
class NodeManager;

//------------------------------------------------------------------------------
// Double pass rendering support. Container for sorted alpha nodes (for 2nd pass)
//------------------------------------------------------------------------------
class SortedAlphaNodes
{
    DISALLOW_COPY_AND_ASSIGN( SortedAlphaNodes );

    //
    // Keep the state of the shape node for the second pass.
    //
    struct NodeState
    {
        NodeState() { }
        NodeState( float * m4x4_, float dist_, NodeShape * node_ )
        :   dist( dist_ ), node( node_ )
        {
            ::memcpy( m4x4, m4x4_, 16 * sizeof( float ) );
        }

        float       m4x4[16];   // model view matrix of the node (OpenGL)
        float       dist;       // distance^2
        NodeShape * node;       // NodeShape
    };

    typedef std::vector< size_t >       SortedIndices;
    typedef std::vector< NodeState >    NodesContainer;

    NodesContainer  m_container; // (unsorted) node state container
    SortedIndices   m_indices;   // (sorted) indices for m_container

public:
    SortedAlphaNodes() { }

public: // main interface
    size_t      GetSize() const { return m_container.size(); }
    void        Clear() { m_container.clear(); m_indices.clear(); }
    void        Add( float * m4x4, NodeShape * node );
    NodeShape * GetNode( size_t index );
    float *     GetMatrix( size_t index );
};

//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
inline float * SortedAlphaNodes::GetMatrix( size_t index )
{
    _ASSERTE( index < GetSize() );
    return m_container[ m_indices[ index ] ].m4x4;   
}

//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
inline NodeShape * SortedAlphaNodes::GetNode( size_t index )
{
    _ASSERTE( index < GetSize() );
    return m_container[ m_indices[ index ] ].node;   
}

//------------------------------------------------------------------------------
// Abstract.
// Push NodeState at the back of unsorted vector "m_container";
// Insert the index of last object in the proper place in "m_indices".
// So "m_indices" is sorted array (in descendant order).
//------------------------------------------------------------------------------
inline void SortedAlphaNodes::Add( float * m4x4, NodeShape * node )
{
    float dist = m4x4[12] * m4x4[12] + m4x4[13] * m4x4[13] + m4x4[14] * m4x4[14];

    m_container.push_back( NodeState( m4x4, dist, node ) );
   
    const size_t position = m_container.size() - 1;

    for ( SortedIndices::iterator it = m_indices.begin(); it != m_indices.end(); ++it )
    {
        if ( m_container[ *it ].dist < dist )
        {
            m_indices.insert( it, position );
            return;
        }
    }
    m_indices.push_back( position );   
}


Declaration of container of AlphNodes in Node class:
protected:
    static SortedAlphaNodes & AlphaNodes() { return s_alphaNodes; }
private:
    const NodeType              m_type;         // RTTI
    static SortedAlphaNodes     s_alphaNodes;   // alpha nodes for 2nd pass



A sample of use - inside NodeGeometry::Render()
    if ( firstPass && transparent )
    {
        float m4x4[ 16 ];
        glGetFloatv( GL_MODELVIEW_MATRIX, m4x4 );
        AlphaNodes().Add( m4x4, this );
    }
    else ...


Render scene graph:
void NodeManager::Render()
{
    _ASSERT( GetSize() ); // root must be created already

    Node::AlphaNodes().Clear();

    switch( Options::GetRendererType() )
    {
    case GR_OpenGL:
        //
        // 1st pass: render all opaque objects and store all transparent ones
        //
        glPushAttrib( GL_BLEND );
        glDisable( GL_BLEND );
        GetRoot()->Render( true );
        //
        // 2nd pass: render all transparent objects
        //
        glEnable( GL_BLEND );
        glMatrixMode( GL_MODELVIEW );
        glPushMatrix();

        for ( size_t t = 0; t < Node::AlphaNodes().GetSize(); ++t )
        {
            glMatrixMode( GL_MODELVIEW );
//          glPushMatrix();
            glLoadMatrixf( Node::AlphaNodes().GetMatrix( t ) );
            Node::AlphaNodes().GetNode( t )->Render( false );
//          glMatrixMode( GL_MODELVIEW );
//          glPopMatrix();
        }

        glMatrixMode( GL_MODELVIEW );
        glPopMatrix();

        glPopAttrib(); // GL_BLEND
        break;
    }
}


It is pretty hard to incorporate this code in alien's codebase, so I think it is not harmful to show it here. At least one can find something amusing (i.e. bugs).
P.S. I use the name "Shape" in lieu of "Geometry".


Last edited by BugHunter on Sun Nov 23, 2008 3:53 pm, edited 1 time in total.


_________________
«Computer scientists deal with algorithms that you may call practical in theory but unpractical in practice.» © Timothy Gowers
Offline
 Profile  
 
 Post subject: Re: GameDev-VMK027-TwoRenderPasses: questions & improvem
PostPosted: Sun Nov 23, 2008 12:00 pm 
Site Admin

Joined: Sun Feb 11, 2007 8:59 am
Posts: 1105
Location: Ontario Canada
BugHunter wrote:
A SMALL BUG (?)

It seems I found a bug. I'm not sure, so feel free to correct or even punish me. This bug isn't critical but may deteorate engine's performance.
--> The nature of NodeGeometry::Render() is recursive. Thus, if during the second pass you call Node::Render() for chosen (transparent) nodes this method calls the children recursively as well. The point is that for the 2nd pass this is undesirable behavior.


Good catch BugHunter. I missed that one!

I think this should fix the recursive problem. replace the last if statement in the RenderOGL code to the following:
if (m_pNext && !bSecondPass) {
   m_pNext->renderOGL(bSecondPass);
}


Offline
 Profile  
 
 Post subject: Je parie que si
PostPosted: Mon Nov 24, 2008 9:30 am 

Joined: Wed Aug 06, 2008 7:53 pm
Posts: 182
Location: Russia
Yup. We only want recursion at the 1st pass.



_________________
«Computer scientists deal with algorithms that you may call practical in theory but unpractical in practice.» © Timothy Gowers
Offline
 Profile  
 
 Post subject:
PostPosted: Sun Nov 14, 2010 8:07 pm 

Joined: Sun Nov 14, 2010 7:31 pm
Posts: 3
Location: Canada
This is my first post:) .. Just want to quickly say "Thank you Marek for your awesome job on these tutorial lessons."

Anyways, I just want to mention that there was a cheap way to see the objects through transparent textures with the old scene graph structure.

we could have something like this, assuming the Texture had an alpha channel (RGBA):

glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f);
glDisable(GL_BLEND);


Here inside RenderGL I commented out the GL_Blend and replaced it with above code:

void NodeShape::RenderGL(bool bRenderSolid)
{
   
    if (m_pTexture){

        //some codes........
         
        //If alpha used in texture then turn on blending
        if (!m_pTexture->m_bSolid){

            //glEnable(GL_BLEND);
            glEnable(GL_ALPHA_TEST);
            glAlphaFunc(GL_GREATER, 0.5f);
            glDisable(GL_BLEND);

            bObjectSolid = false;

        }
         
            m_pGeometry->RenderOGL(true);

    }
 
}


Offline
 Profile  
 
Display posts from previous:  Sort by  
 Page 1 of 1 [ 8 posts ] 

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to:  

cron