PDA

View Full Version : glColor4f vs Materials



Rodrix
04-15-2006, 11:25 PM
Hi Everyone!

I am developing a Particle System, and I want my particles to change color and alpha value. I can do it either with glColor4f or either by changing the materials properties of my particle (Ambient, Diffuse, Specular, and Transparency).

Then, a glcolor4f or Material command must be called for EACH particle. That means they are called multiple times. Which one should I choose to use to have the least performace hit?

My scene uses lightning, not glcolor. That means I would have to enable glcolor materials on the middle of the scene and then turn it off.

Thanks so much everyone in advance!
Cheers,
Rod

Andrew Adinetz
04-16-2006, 01:29 AM
What if you consider using arrays to draw particles? In this case, you can specify the array of colors and pass it to OpenGL using only one rendering call (so using colors will definitely be better, as there is no way to specify a separate GL material for each vertex).

jide
04-16-2006, 04:39 AM
If you have lighting enabled, then do your particles define normals ? If not, then definately disable lighting when rendering your particles and use colors.

Also, I guess that using glColorMaterial imply GL to call glMaterialfv at least for ambient and diffuse component. So this might give approximately the same result.

Komat
04-16-2006, 06:55 AM
If possible, with lighting go for the glColor and glColorMaterial. The drivers are likely to have that path optimized much more than full material setup.

zeoverlord
04-16-2006, 04:00 PM
What Komat said.
Allmost all rendering done on modern graphics cards use shaders in one form or another, and there are few real hardwired optimizations, just a collection of shaders that do different things depending on the current state.
So using only glColor should be faster, besides, lighting is perhaps not something you should apply to the particles because of the nature of particles, so using glColorMaterial and or glMaterialfv is a moot point.
if some kind of lighting is needed (like when used on smoke or dust) then add the lighting to glColor directly.

Rodrix
04-16-2006, 11:02 PM
Thanks guys!
My particles are created by point sprites, so I guess they don't use normals.
I will then go for disabling lighting and enabling in glcolor before calling particlesystem.Render().

Andrew: I think your point goes for quad particle systems or with multiple vertices, if I did understand you right, but my system has no vertices and just sprite points. The particles can each have a different color so I don't see a way of a calling a single glcolor for all the system. Did I get you right?

Thanks everyone so much!
Cheers!
Rodrix

Komat
04-17-2006, 05:20 AM
Andrew: I think your point goes for quad particle systems or with multiple vertices, if I did understand you right, but my system has no vertices and just sprite points. The particles can each have a different color so I don't see a way of a calling a single glcolor for all the system. Did I get you right?
What he meant is: Instead of calling glVertex, glColor and so on for each point you can store those values for all points in one (containing both positions and color) or more (one for positions, one for colors) arrays and tell the OGL to use values from those arrays using glVertexPointer/glColorPointer calls. Then you will draw all points by single call to glDrawArrays and OGL will automatically retrieve corresponding positions and colors from that array. See chapter "Vertex Arrays" in OGL specification (http://www.opengl.org/documentation/specs/version2.0/glspec20.pdf)

Rodrix
04-17-2006, 11:19 AM
Oh! Nice...
This means that I can do a single color call right?!
Cool!
Thanks! I will give it a try.

Cheers!
Rod

Rodrix
04-17-2006, 02:12 PM
...wait! :D
I read that the main cause for performance hit on particle systems is memory allocation.
And my particles are stored using pointer lists that work very well. However I cant just pass the particle structure to the glvertexpointer call (at least to what I know), so I would have to first copy all the particles' vertex data to a new global variable called TheVertexList (and do the same with the color) before calling glVertexPointer/glVertexColor.

What is the best? Copying the whole values to the new list in every render and then call glVertexpointer/glColorpointer and avoding multiple glcolor and glvertex calls, or just using multiple glcolor/vertex calls and avoiding multiple memory allocation operations?

My particle structure is:

struct Particle
{
vector3f m_vCurPos; // Current position of particle
vector3f m_vCurVel; // Current velocity of particle
float m_fInitTime; // Time of creation of particle

vector3f m_vColor; //RGB Particle's Color
float m_Alpha; //Alpha Value of Particle

Particle *m_pNext; // Next particle in list
};Is there a way I can use my particle structure to pass the parameter to the glVertexPointer/glColorPointer calls without copying them to a new array?

Thanks so much!
Cheers,
Rod

Komat
04-17-2006, 02:32 PM
What is faster depends on several factors so you have to measure what is better in your case however you do not need to allocate memory in each frame. Simply allocate one big array sufficient to store some number of particles (ideally maximal number of particles you support) and use that array in consecutive frames reallocating it only to increase its size if you run out of space (after increase you will use that increased array until you run out of space again).

rgpc
04-17-2006, 06:30 PM
What you have with your structure is a linked list, which may seem nice, but as you are aware can't be used with the GL Pointer functions.

If you change it to use an array then you can use the Pointer functions. I assume that when you add a particle it's a new memory allocation to create the node in the Linked list? And when a particle dies it's deallocated? Or are you using a buffer and just filling in the unused parts of the buffer as you go with the nodes?

Either way it's FAR better to use an array. With an array you know exactly how many nodes you have, you can use a simple for loop to traverse it, and you can use buffering techniques like Komat suggested. Plus you can use the gl Pointer functions. It also has the added benefit of not fragmenting your RAM (or rather reducing the fragmentation) - which was a topic of discussion of the gd-algorithms mailing list recently.

Rodrix
04-18-2006, 01:37 AM
Hi rgpc,
The system works like this:
If a new particle must be released and there are no particles in free lists and nParticles <MaxParticles then a new particle is allocated, else particles are 'recycled'. Dead particles go to free lists and are never deleted. This way particles are constantly recycled. Here is the basic code if you want to check it out. It's based on "The Advanced OpenGL book" source code examples:


int CParticleSystem::Update( FLOAT fElpasedTime )
Particle *pParticle;
Particle **ppParticle;

vector3f vOldPosition;

m_fCurrentTime += fElpasedTime; // Update our particle system timer...

ppParticle = &amp;m_pActiveList; // Start at the head of the active list

while( *ppParticle )
{
pParticle = *ppParticle; // Set a pointer to the head

// Calculate new position
float fTimePassed = m_fCurrentTime - pParticle->m_fInitTime;

if( fTimePassed >= m_fLifeCycle )
{
// Time is up, put the particle back on the free list...
*ppParticle = pParticle->m_pNext;
pParticle->m_pNext = m_pFreeList;
m_pFreeList = pParticle;

--m_dwActiveCount;
}
else
{
// Update particle position and velocity

// Update velocity with respect to Gravity (Constant Accelaration)
pParticle->m_vCurVel += m_vGravity * fElpasedTime;

// Update velocity with respect to Wind (Accelaration based on
// difference of vectors)
if( m_bAirResistence == true )
pParticle->m_vCurVel += (m_vWind - pParticle->m_vCurVel) * fElpasedTime;

// Finally, update position with respect to velocity
vOldPosition = pParticle->m_vCurPos;
pParticle->m_vCurPos += pParticle->m_vCurVel * fElpasedTime;


//-------------------------------------------------------------------------
// Emit new particles in accordance to the flow rate...
//
// NOTE: The system operates with a finite number of particles.
// New particles will be created until the max amount has
// been reached, after that, only old particles that have
// been recycled can be reintialized and used again.
//-------------------------------------------------------------------------

if( m_fCurrentTime - m_fLastUpdate > m_fReleaseInterval )
{
// Reset update timing...
m_fLastUpdate = m_fCurrentTime;

// Emit new particles at specified flow rate...
for( DWORD i = 0; i < m_dwNumToRelease; ++i )
{
// Do we have any free particles to put back to work?
if( m_pFreeList )
{
// If so, hand over the first free one to be reused.
pParticle = m_pFreeList;
// Then make the next free particle in the list next to go!
m_pFreeList = pParticle->m_pNext;
}
else
{
// There are no free particles to recycle...
// We'll have to create a new one from scratch!
if( m_dwActiveCount < m_dwMaxParticles )
{
if( NULL == ( pParticle = new Particle ) )
return E_OUTOFMEMORY;
}
}

if( m_dwActiveCount < m_dwMaxParticles )
{
pParticle->m_pNext = m_pActiveList; // Make it the new head...
m_pActiveList = pParticle;

// Set the attributes for our new particle...
pParticle->m_vCurVel = m_vVelocity;

if( m_fVelocityVar != 0.0f )
{
vector3f vRandomVec = getRandomVector();
pParticle->m_vCurVel += vRandomVec * m_fVelocityVar;
}

pParticle->m_fInitTime = m_fCurrentTime;
pParticle->m_vCurPos = m_vPosition;


++m_dwActiveCount;
}
}
}

// Query for the max point size supported by the hardware
float maxSize = 0.0f;
glGetFloatv( GL_POINT_SIZE_MAX_ARB, &amp;m_fMaxPointSize );

return S_OK;
Cheers!
Rod

CrazyButcher
04-18-2006, 01:52 AM
just want to add that, once you want to use billboards and not points/point sprites, that you cant use the particles as array anyway... or you code multiple specialized particle systems for the different primitive types.

rgpc
04-18-2006, 02:03 AM
Other than using arrays to store your particles, your only other option (as far as I can see) would be to allocate your two arrays for position and colour (or just one - you can interleave the data) and update the contents of the array(s) each frame - then using that data for rendering.

It's a trade off really - your existing code doesn't have many alloc's/memcpy's - but will fragment your RAM something fierce and will limit you to Immediate mode.

Or you can copy the data for the position and colour of each particle into an array and use the copied data in DrawArrays()/DrawElements().

Or you can use an array which will give you at least memcpy's when your particles expire, won't fragment your RAM and will allow you to use the data in DrawArrays()/DrawElements() (ie. faster rendering).

Rodrix
04-19-2006, 01:45 AM
Thanks guys so much for helping!

rgpc- I didn't get your last line:


Or you can use an array which will give you at least memcpy's when your particles expire What do you mean?

...and CrazyButcher, did you imply that my coding approach is not useful for point sprites but for billboards?

Thanks so much everyone!
Cheers,
Rod

CrazyButcher
04-19-2006, 02:11 AM
I mean that for point sprites the array approach is useful, because you can directly use the particle's color and position values. however if you do billboards you need 4 values per particle, so imo a list approach is still fine for billboards (however I would also suggest the memory management others have mentioned)

rgpc
04-19-2006, 05:25 PM
What I meant was that if you are using an array to store your particle information, then when they expire you have to reshuffle the array (ie. memcpy) as the particles expire. If all the particles expired after the same amount of time then you could minimize the amount of reshuffling that is required - but if their expiry age varies (eg. by a random value) then you might find that it creates gaps in your array that you need to fill in (multiple memcpys).

However, depending on the blending mode you are using to draw the particles, you might be able to simply allocate the array (and grow it if you need to) then when particles expire you (a) set a flag so you know it's expired and (b) set the alpha to 0.0f and use alpha testing to prevent the particle from drawing. Then as new particles come along you use the expired particles.

And to make it quicker to find the expired particles (rather than looping through the array) you could use your linked list to keep track of where the expired particles are in the array. Though in this case it would be just need to have the pointer to the particle and the pointer to the next node in the linked list.

I hope that makes sense.