Point antialiasing and gl_PointSize

Hi,

I try to render antialiased (round) points with a vertex shader computed point size and it doesn’t work.

GL_VERTEX_PROGRAM_POINT_SIZE is enabled.
GL_MULTISAMPLE is enabled.
GL_SAMPLE_BUFFERS=1
GL_SAMPLES=4

but my points are squared.

I tried a mix of glPointSize and glEnable(GL_POINT_SMOOTH) and the result is weird:

GL_VERTEX_PROGRAM_POINT_SIZE: on
GL_MULTISAMPLE: on
GL_POINT_SMOOTH: off
renderer point size = vertex program point size (gl_PointSize)
squared point

GL_VERTEX_PROGRAM_POINT_SIZE: on
GL_MULTISAMPLE: on
GL_POINT_SMOOTH: on
renderer point size = application point size (glPointSize(x))
round point

Here is my test program:


#include <stdio.h>

#include <GL/glew.h>

#include <GL/glut.h>

int window_width=640;
int window_height=480;

void glut_reshape(int w, int h)
{
	window_width = w;
	window_height = h;
	glutPostRedisplay();
}

bool vertexProgramPointSize = true;
bool pointSize = false;
bool multiSample = true;
bool pointSmooth = false;

void glut_keyboard(unsigned char key, int x, int y)
{
	switch (key)
	{
		case 27:
			exit(0);
			break;
		case 'v':
			vertexProgramPointSize = !vertexProgramPointSize;
			break;
		case 'p':
			pointSize = !pointSize;
			break;
		case 'm':
			multiSample = !multiSample;
			break;
		case 's':
			pointSmooth = !pointSmooth;
			break;
	}

	printf("vertexProgramPointSize : %s   pointSize: %s   multiSample: %s   pointSmooth: %s
", 
		vertexProgramPointSize? "yes":"no", 
		pointSize? "yes":"no", 
		multiSample? "yes":"no",
		pointSmooth? "yes":"no");
	
	glutPostRedisplay();
}

void glut_display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glViewport(0, 0, window_width, window_height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	if (vertexProgramPointSize)
		glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
	else
		glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);

	if (pointSize)
		glPointSize(10.0);
	else
		glPointSize(1.0);

	if (multiSample)
		glEnable(GL_MULTISAMPLE);
	else
		glDisable(GL_MULTISAMPLE);

	if (pointSmooth)
		glEnable(GL_POINT_SMOOTH);
	else
		glDisable(GL_POINT_SMOOTH);

	glBegin(GL_POINTS);
	glColor3f(1.0f, 0.0f, 0.0f);
	glVertex3f(0.0f, 0.0f, -1.0f);
	glEnd();
	
	glutSwapBuffers();
}

int main(int argc, char *argv[])
{
	printf("Hello world!
");

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(window_width, window_height);

	glutCreateWindow("::: V  I  E  W  E  R :::");

	glewInit();

	GLint sampleBuffers, samples;
	glGetIntegerv(GL_SAMPLE_BUFFERS, &sampleBuffers);
	glGetIntegerv(GL_SAMPLES, &samples);

	printf("sampleBuffers: %d   samples: %d
", sampleBuffers, samples);
	
	const char *vertexSrc =		"void main()
"
										"{
"
										"	gl_PointSize = 20.0;
"
										"	gl_Position = ftransform();
"
										"}
";

	const char *fragmentSrc =	"void main()
"
										"{
"
										"	gl_FragColor = vec4(1.0,0.0,0.0,1.0);
"
										"}
";

	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader, 1, &vertexSrc, 0);
	glCompileShader(vertexShader);

	GLint vertexShaderCompiled;
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &vertexShaderCompiled);
	if (! vertexShaderCompiled)
	{
		GLchar buffer[4096];
		glGetShaderInfoLog(vertexShader, 4096, 0, buffer);

		printf("======================================================
");
		printf("vertex shader
");
		printf("%s
", buffer);
		printf("======================================================
");
	}

	GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentShader, 1, &fragmentSrc, 0);
	glCompileShader(fragmentShader);

	GLint fragmentShaderCompiled;
	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentShaderCompiled);
	if (! fragmentShaderCompiled)
	{
		GLchar buffer[4096];
		glGetShaderInfoLog(fragmentShader, 4096, 0, buffer);

		printf("======================================================
");
		printf("fragment shader
");
		printf("%s
", buffer);
		printf("======================================================
");
	}

	GLuint program = glCreateProgram();
	glAttachShader(program, vertexShader);
	glAttachShader(program, fragmentShader);
	glLinkProgram(program);

	glUseProgram(program);
	
	glClearColor(0.1f, 0.2f, 0.2f, 1.0f);
	glEnable(GL_DEPTH_TEST);
	
	glutReshapeFunc(glut_reshape);
	glutKeyboardFunc(glut_keyboard);
	glutDisplayFunc(glut_display);
	
	glutMainLoop();

	return 0;
}


You will definitely need to enable GL_POINT_SMOOTH in order to get round points.
I’m not sure, but maybe vertex shader written point sizes work only with point sprites, however, I don’t know if there is such a limitation.

Another thing is that you need to enable GL_BLEND in order to get round points as I remember so you should start with enabling that first.

GL_BLEND doesn’t affect the rendering. I assume that point antialiasing uses multisampling.

According to the OpenGL specs, GL_POINT_SMOOTH should have no effect

OpenGL 2.1 specs:
3.3.3 Point Multisample Rasterization
If MULTISAMPLE is enabled, and the value of SAMPLE BUFFERS is
one, then points are rasterized using the following algorithm,
regardless of whether point antialiasing (POINT SMOOTH) is
enabled or disabled
. Point rasterization produces a fragment
for each framebuffer pixel with one or more sample points that
intersect a region centered at the point’s (xw, yw). This
region is a circle having diameter equal to the current point
width if POINT SPRITE is disabled, or a square with side equal
to the current point width if POINT SPRITE is enabled.
Coverage bits that correspond to sample points that intersect
the region are 1, other coverage bits are 0. All data
associated with each sample for the fragment are the data
associated with the point being rasterized, with the exception
of texture coordinates when POINT SPRITE is enabled; these
texture coordinates are computed as described in section 3.3.
Point size range and number of gradations are equivalent to
those supported for antialiased points when POINT SPRITE is
disabled. The set of point sizes supported is equivalent to
those for point sprites without multisample when POINT SPRITE
is enabled.

Obviously, if point sprite is enabled and the texture is round, the point will
be rendered has I want. Maybe I have to do that but I wanted to know if I’m doing something wrong.

If you need consistent behavior, you’ll be better off using point sprites or quads with a round texture.

I believe POINT_SMOOTH is for smoothing points when multisampling is disabled, and I wouldn’t count on it working reliably.

My experience is that AMD and NVIDIA both produce square points when multisampling is disabled. When multisampling is enabled (even with just one sample per pixel), NVIDIA produces round points, while AMD produces square points. It sounds like the AMD approach goes against the spec, but it is what it is. Also, both AMD and NVIDIA treat point size as if it were using samples for the units, not pixels; 4x and 16x multisampling cause the effective point size in pixels to halve and quarter, respectively (other multisample counts round to 4x or 16x’s scaling).

Using quads would be the most reliable way to get around these issues, but for my use case I just cancelled out the multisample scaling effect by scaling point size and lived with the square vs circle difference because I couldn’t afford the performance hit of using quads.

For what it’s worth, DX10 seems to have dropped support for point sizes greater than one pixel, and instead encourages using the geometry shader to expand points into quads (at a performance hit).

Thank you for the clarifications.

I have just checked that the point size is not in pixels, but in samples. It makes the sized points unusable. I can detect the number of samples per pixel using GL_SAMPLES_ARB but in the driver control panel, I can choose 8xS for “Antialiasing - Setting”. With this setting, the point size specified in the vertex shader is not used …

Pas glop.