7
votes

I'm trying to render some particles with OpenGL 3+ using point sprites. I've just realized that I have a major issue with the points. They increase in size automatically with respect to the camera distance. Resulting in the following:

Close to the particle emitter:

colse

And when far away, everything looks blurry and bloated:

far

In older versions it seems one could adjust the point size scales with glPointParameter. The function is still available in new 3+ OpenGL, but it only supports two parameters. GL_POINT_FADE_THRESHOLD_SIZE seemed like what I need, but I've tried it with no results. I'm also using glEnable(GL_PROGRAM_POINT_SIZE);

Any other way I could avoid this automatic scaling of the points based on camera distance? I would rather not have to change to code to use standard billboards made of triangles.

Not sure if relevant at the moment, but this is vertex shader I'm using:

layout(location = 0) in vec4 a_position_size; // Point size in w coord
layout(location = 1) in vec4 a_color;         // RGBA color modulated with texture

layout(location = 0) out vec4 v_color;

uniform mat4 u_MVPMatrix;

void main()
{
    gl_Position  = u_MVPMatrix * vec4(a_position_size.xyz, 1.0);
    gl_PointSize = a_position_size.w;
    v_color      = a_color;
}
2
You'll have to scale gl_PointSize with the distance away from the camera. OpenGL doesn't (and can't reasonably) do it for you.Colonel Thirty Two
@ColonelThirtyTwo, but OpenGL is already scaling the points. Can't I disable it somehow? Undoing the scale the GL does seems a bit of a hack...glampert
It's not scaling the points. They are always gl_PointSize units on screen, regardless of their z coordinate. They only look like they're getting bigger when moving away because everything else is getting smaller.Colonel Thirty Two
@ColonelThirtyTwo, Oh, I see, now I've realized it. It is also pretty clear in the documentation, anyway: "gl_PointSize - contains size of rasterized points, in pixels". Thanks.glampert
Yes, gl_PointSize is in window-space. In other words its unit of measure is 1 pixel.Andon M. Coleman

2 Answers

14
votes

So it turns out my problem was due to my misunderstanding of gl_PointSize. As was noted in the comments and clearly stated in the documentation, gl_PointSize contains size of rasterized points, in pixels. Hence the point sprites look larger once we move away from them, but not because they are being scaled, but because they still occupy the same screen space while the rest of the 3D scene is being scaled-down according to the perspective projection.

I fixed the problem with a few adjustments to the vertex shader to actually scale the point size according to the distance from the viewer:

uniform mat4 u_MVPMatrix;
uniform vec3 u_cameraPos;

// Constants (tweakable):
const float minPointScale = 0.1;
const float maxPointScale = 0.7;
const float maxDistance   = 100.0;

void main()
{
    // Calculate point scale based on distance from the viewer
    // to compensate for the fact that gl_PointSize is the point
    // size in rasterized points / pixels.
    float cameraDist = distance(a_position_size.xyz, u_cameraPos);
    float pointScale = 1.0 - (cameraDist / maxDistance);
    pointScale = max(pointScale, minPointScale);
    pointScale = min(pointScale, maxPointScale);

    // Set GL globals and forward the color:
    gl_Position  = u_MVPMatrix * vec4(a_position_size.xyz, 1.0);
    gl_PointSize = a_position_size.w * pointScale;
    v_color      = a_color;
}
5
votes

Had the same problem while using GLSL with three.js. Solved it based on glampert's answer, but first of all three.js requires usage of certain predefined variable names:

uniform vec4 origin;

void main() {
    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    float cameraDist = distance( mvPosition, origin );
    gl_PointSize = 200.0 / cameraDist;
    gl_Position = projectionMatrix * mvPosition;
}

Secondly note that the modelViewMatrix is applied first to the particle position and then the distance is calculated to that position. Because if you apply transformations to the particle system object your particles lie in object coordinates that are not equal to global coordinates. So I calculate the distance in view coordinates (where the camera is always in the origin).