c++openglglslshadercinder

OpenGL: How do I sort points depending on the camera distance?


I have a structure made of 100.000 spheres as point-sprites using OpenGL. I have an issue when I rotate the structure on its centre axis. The point-sprite are rendered in order depending on their array, that means, the last ones overlap the first point-sprite created, not taking care of the depth in the three-dimensional space.

How can I sort and rearrange in real-time the order of the point-sprites to keep always the three-dimensional perspective? I guess the idea is to read the camera position against the particles and then sort the array to always show closer particles first. Is it possible to be fixed using shaders?

Here is my shader: shader.frag

#version 120
uniform sampler2D tex;
varying vec4 inColor;
//uniform vec3 lightDir;

void main (void) {
gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * inColor;
}

shader vert

#version 120
// define a few constants here, for faster rendering
attribute float particleRadius;
attribute vec4 myColor;
varying vec4 inColor;

void main(void)
{
vec4 eyeCoord = vec4(gl_ModelViewMatrix * gl_Vertex);
gl_Position = gl_ProjectionMatrix * eyeCoord;
float distance = length(eyeCoord);
float attenuation = 700.0 / distance;
gl_PointSize = particleRadius * attenuation;
//gl_PointSize = 1.0 / distance * SIZE;
//gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_FrontColor = gl_Color;
inColor = myColor;
}

draw method:

void MyApp::draw(){
//gl::clear( ColorA( 0.0f, 0.0f, 0.0f, 0.0f ), true );
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// SET MATRICES TO WINDOW
gl::setMatricesWindow( getWindowSize(), false );
gl::setViewport( getWindowBounds() );
gl::enableAlphaBlending();

gl::enable( GL_TEXTURE_2D );
gl::enable(GL_ALPHA_TEST);
glEnable(GL_DEPTH_TEST);
gl::color( ColorA( 1.0f, 1.0f, 1.0f, 1.0f ) );

mShader.bind();
// store current OpenGL state
glPushAttrib( GL_POINT_BIT | GL_ENABLE_BIT );

// enable point sprites and initialize it
gl::enable( GL_POINT_SPRITE_ARB );
glPointParameterfARB( GL_POINT_FADE_THRESHOLD_SIZE_ARB, -1.0f );
glPointParameterfARB( GL_POINT_SIZE_MIN_ARB, 0.1f );
glPointParameterfARB( GL_POINT_SIZE_MAX_ARB, 200.0f );

// allow vertex shader to change point size
gl::enable( GL_VERTEX_PROGRAM_POINT_SIZE );

GLint thisColor = mShader.getAttribLocation( "myColor" );
glEnableVertexAttribArray(thisColor);
glVertexAttribPointer(thisColor,4,GL_FLOAT,true,0,theColors);

GLint particleRadiusLocation = mShader.getAttribLocation( "particleRadius" );
glEnableVertexAttribArray(particleRadiusLocation);
glVertexAttribPointer(particleRadiusLocation, 1, GL_FLOAT, true, 0, mRadiuses);

glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
glEnableClientState(GL_VERTEX_ARRAY);

glVertexPointer(3, GL_FLOAT, 0, mPositions);

mTexture.enableAndBind();
glDrawArrays( GL_POINTS, 0, mNumParticles );
mTexture.unbind();

glDisableClientState(GL_VERTEX_ARRAY);
glDisableVertexAttribArrayARB(thisColor);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableVertexAttribArrayARB(particleRadiusLocation);
// unbind shader
mShader.unbind();

// restore OpenGL state
glPopAttrib();

}

Solution

  • You have two different blending cases in void MyApp::draw()

    1. additive - (src + dst)

      • Order independent

    2. alpha - (src * src.a + (dst * (1.0 - src.a))

      • Order depdendent

    The first blending function would not cause the issues you are discussing, so I am assuming that mRoom.isPowerOn() == false and that we are dealing with alpha blending.

    To solve order dependency issues with the latter case you need to transform your points into eye-space and sort using their z coordinates. The problem here is that this is not something that can be easily solved in GLSL - you need to sort the data before your vertex shader runs (so the most straight-forward approach involves doing this on the CPU). GPU-based solutions are possible and may be necessary to do this in real-time given the huge number of data points involved, but you should start out by doing this on the CPU and figure out where to go from there.

    When implementing the sort, keep in mind that point sprites are always screen aligned (uniform z value in eye-space), so you do not have to worry about intersection (a point sprite will either be completely in-front of, behind, or parallel to any other point sprite it overlaps). This makes sorting them a lot simpler than other types of geometry, which may have to be split at points of intersection and drawn twice for proper ordering.