10

I was trying to set point sprites in OpenGL to change size with distance just as a billboarded sprite would, but I can't get the values in GL_POINT_DISTANCE_ATTENUATION_ARB to do anything useful. Is there a correlation of values to this that would match a given projection? Is what I'm trying to do even possible?

Render code being used:

glPointParameterfARB  = (PFNGLPOINTPARAMETERFARBPROC)wglGetProcAddress("glPointParameterfARB");
glPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC)wglGetProcAddress("glPointParameterfvARB");

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluPerspective(100.0, 800.0/600.0, 0.1, 10.0);

float quadratic[] =  { 5.0f, 0.1f, 10.0f };
glPointParameterfvARB( GL_POINT_DISTANCE_ATTENUATION_ARB, quadratic );

float maxSize = 0.0f;
glGetFloatv( GL_POINT_SIZE_MAX_ARB, &maxSize );
if( maxSize > 100.0f )  maxSize = 100.0f;
glPointSize( maxSize );

glPointParameterfARB( GL_POINT_FADE_THRESHOLD_SIZE_ARB, 0.1f );
glPointParameterfARB( GL_POINT_SIZE_MIN_ARB, 0.1f );
glPointParameterfARB( GL_POINT_SIZE_MAX_ARB, maxSize );

glTexEnvf( GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE );

glEnable( GL_POINT_SPRITE_ARB );

glScalef(0.75,1,1);
glTranslatef(0.00,0.0,-1.0);
glScalef(0.5,0.5,0.5);
glRotatef(counter*0.1+0.5,1.0,1.0,0.0);

glBegin( GL_POINTS );

for( int i = 0; i < 100; ++i )
{
    glColor4f( i%10*0.1, i/10*0.1, 0.5, 1.0f );

    glVertex3f( i%10*0.2-1.0,i/10*0.2-1.0,
    ((i%10-5)*(i%10-5)+(i/10-5)*(i/10-5))*0.01 );
}

glEnd();

glDisable( GL_POINT_SPRITE_ARB );
John Conde
  • 217,595
  • 99
  • 455
  • 496
zaratustra
  • 8,148
  • 8
  • 36
  • 42

4 Answers4

6

Here's how I make my poor man's approach to scaling the point size:

void render() {
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_POINT_SPRITE);
    glActiveTexture(GL_TEXTURE0);
    glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);

    /* Activate shader program here */
    /* Send pointSize to shader program */

    glBegin(GL_POINTS);
        /* Render points here */
        glVertex3f(...);
    glEnd(GL_POINTS);
}

Vertex shader:

uniform float pointSize;
void main() {
    gl_Position = ftransform();
    gl_PointSize = pointSize / gl_Position.w;
}

You can do whatever you want in the fragment shader, but you'll have to compute the color, lighting and texturing yourself.

Eric
  • 6,364
  • 1
  • 32
  • 49
2

GLSL aside, doing what you want is pretty simple with distance attenuation. Seeing as how the projected size of things decreases quadratically with their distance in perspective projections, you only need to use the quadratic factor.

If you want to use the point size you manually set at a distance of, say, 150 units from the eye, just use 1/(150^2) as the quadratic factor (and zero for the constant and linear factors -- if anything, you may want to use some small number like 0.01 for the constant factor just to avoid potential divisions by zero).

Dolda2000
  • 25,216
  • 4
  • 51
  • 92
1

In my experience point size attenuation isn't worth the trouble. You're much better off writing a very simple GLSL vertex shader that sets the point size manually according to some calculation you perform on your own. It took me about half a day to learn from scratch all the GLSL I needed to make this happen.

The GLSL code may be as simple as these few lines:

attribute float psize;

void main()
{   
    gl_FrontColor = gl_Color;
    gl_PointSize = psize;
    gl_Position = ftransform();
}

Where psize is the point size parameter the user chooses.

shoosh
  • 76,898
  • 55
  • 205
  • 325
0

Just have a look in pmviewer.sourceforge.net the code is using point sprites and each point has a own color and size to simulate volume rendering: The vertex shader is:

vertexShader

  // with ATI hardware, uniform variable MUST be used by output
    // variables. That's why win_height is used by gl_FrontColor
    attribute float a_hsml1;
    uniform float win_height;
    uniform vec4 cameralocin;
    void main()
    {
    vec4 position=gl_ModelViewMatrix*gl_Vertex;
    vec4 cameraloc=gl_ModelViewMatrix*cameralocin;
    float d=distance(vec3(cameraloc),vec3(position));
    float a_hsml=gl_Normal.x;
    float pointSize=win_height*a_hsml/d; // <- point diameter in
                                        //pixels (drops like sqrt(1/r^2))
    gl_PointSize=pointSize;
    gl_TexCoord[0]=gl_MultiTexCoord0;
    gl_Position=ftransform();
    gl_FrontColor=vec4(gl_Color.r,gl_Color.g,gl_Color.b,gl_Color.a);
    }

pixelShader

uniform sampler2D splatTexture;
void main()
{
vec4 color = gl_Color * texture2D(splatTexture, gl_TexCoord[0].st);
gl_FragColor = color;\n"
}

Just to send particles to gpu:

   void PutOneArrayToGPU(unsigned int m_vbo, float *hArray, unsigned int num)
{
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER,  sizeof(float) * num, hArray, GL_STATIC_DRAW);
int size = 0;
glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
if ((unsigned)size != (sizeof(float) *num))
    {
    fprintf(stderr, "WARNING: Pixel Buffer Object allocation failed!\n");
    fprintf(stderr, "TurningOff the GPU accelerated rendering\n");
    flag_GpuRender=false;
    }
return flag_GpuRender;
}

Then render them:

  void DrawPointsByGPU()
    {
    glEnableClientState(GL_VERTEX_ARRAY);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPos);
    glVertexPointer(3, GL_FLOAT, 0, 0);

    glEnableClientState(GL_COLOR_ARRAY);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboColor);
    glColorPointer(4, GL_FLOAT, 0, 0);

    glEnableClientState(GL_NORMAL_ARRAY);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboHSML);
    glNormalPointer( GL_FLOAT, 3*sizeof(float), 0);

    glDrawArrays(GL_POINTS, 0, m_numParticles);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    };
Arman
  • 4,566
  • 10
  • 45
  • 66