PDA

View Full Version : Anti-Aliased Lines



PickleWorld
05-08-2009, 12:04 PM
So I have an application with a wireframe type draw and I was wondering if anyone has any useful tips/hints on doing really good line Anti-Aliasing with basic OpenGL?

Currently I...

1) Enable GL_MULTISAMPLE.
2) Enable GL_LINE_SMOOTH.
3) Set glLineWidth() to 0.5
4) Enable GL_BLEND (with standard blend function)
5) Set glDepthMask to FALSE
6) Set glHint to GL_NICEST

That is the basic stuff you can find in the RedBook. Anyone else have any suggestions for improving things?

overlay
05-08-2009, 01:44 PM
If you enable GL_MULTISAMPLE (and you created a context such that when you query the value of GL_SAMPLE_BUFFERS, it returns 1),
GL_LINE_SMOOTH is ignored and multisampling rasterization is used.

ref: spec 2.1 page 107, section 3.4.4 Line Multisample Rasterization.

http://www.opengl.org/registry/doc/glspec21.20061201.pdf

so 2) and 4) are useless in this case.

For multisampling, I'm not sure that you want to set depth mask to FALSE.

PickleWorld
05-08-2009, 02:12 PM
If you enable GL_MULTISAMPLE (and you created a context such that when you query the value of GL_SAMPLE_BUFFERS, it returns 1),
GL_LINE_SMOOTH is ignored and multisampling rasterization is used.


Did not know that, thanks for the reply.

remdul
05-09-2009, 05:03 AM
2) Enable GL_LINE_SMOOTH.
3) Set glLineWidth() to 0.5
4) Enable GL_BLEND (with standard blend function)
5) Set glDepthMask to FALSE
6) Set glHint to GL_NICEST

...is be enough for just anti-aliased lines.

GL_MULTISAMPLE is for full anti-aliasing including solid triangles, and you must set up a multisampled framebuffer before it works.

PickleWorld
05-11-2009, 07:32 AM
...is be enough for just anti-aliased lines.

Yes and no. The line are anti-aliased but the quality is not all that good, especially when you start drawing lines over other lines (you get bleeding and bluring).



GL_MULTISAMPLE is for full anti-aliasing including solid triangles, and you must set up a multisampled framebuffer before it works.

Yep, I have the framebuffer set up. I have it working, I was just wondering if there were things I could do to improve the quality.

Right now, it looks "ok" but not "great"

Ilian Dinev
05-11-2009, 08:21 AM
Draw wide lines (2px or 4px) with a shader, that uses gl_FragCoord.xy and writes an opacity value in gl_FragColor.a

PickleWorld
05-11-2009, 01:08 PM
Draw wide lines (2px or 4px) with a shader, that uses gl_FragCoord.xy and writes an opacity value in gl_FragColor.a

How do I use gl_FragCoord.xy?

How does the x,y window relative coordinates of a given fragment help me determine if I am near the edge of a line (which I assume I nned to know to set the alpha value?)?

Ilian Dinev
05-11-2009, 06:06 PM
search this board for it. There are two implementations, one by me that uses gl_FragCoord, and one that uses geometry shaders.

Hampel
05-12-2009, 12:54 AM
but note, that there are still issues with ATI and gl_FragCoord and FBOs...

PickleWorld
05-12-2009, 08:26 AM
So I found the following vertex shader and fragment shader on the forums but they don't seem to work?



varying vec4 posix;
void main(){
posix = ftransform();
gl_Position=posix + vec4(0,0,0,0.001);
}



varying vec4 posix;
void main(){
vec4 FinalColor = vec4(0.0,0.0,0.0,1.0);

vec2 ScreenSizeHalf = vec2(1680/2,1050/2);
vec2 posix1= ((posix.xy/posix.w)+1.0)*ScreenSizeHalf;
vec2 fpos = gl_FragCoord.xy;
float dist = distance(fpos,posix1);
FinalColor.w = 1.0-dist;
gl_FragColor = FinalColor;
}


I messed around with the code a little and it appears that the distance is always larger than 2.0? (I switched the second last line to "FinalColor.r = dist-2.0;" and the lines were still always red).

What am I doing wrong?

p.s. I am running a Nvidia Quadro FX570.
p.p.s My monitor is a Dell widescreen running at 1680x1050.

Jackis
05-12-2009, 09:45 AM
Well... at a first glance the code should work.
As for me, it seems strange, that NDC point got shifted by epsilon in W direction. In such a case you wouldn't get exact coincidence between pipeline-calculated and self-calculated points. But I don't think that's the case. Cause the worst we could get - all the lines become "more closer" that they actually are.
Then, just for the case... Are you sure you have called glViewport() with these 1680,1050 numbers??
Then, if you say you always getting oversaturated distance - try visualizing, say, "distance*0.001" and look for it's distribution. If you see radial gradient from upper-left corner - then you have troubles with "posix" varying.

Ilian Dinev
05-12-2009, 10:53 AM
if your viewport is really 1680x1050, then the code should work (have dist<=1.0). Hmm, try changing that vec4(0,0,0,0.001) to vec4(0,0,0.001,0)

Ilian Dinev
05-12-2009, 10:54 AM
but note, that there are still issues with ATI and gl_FragCoord and FBOs... no troubles, in the GLSL forum there's a solution for it - how to manually calculate gl_FragCoord.

PickleWorld
05-12-2009, 11:10 AM
Good call guys, my viewport was not really 1680x1050, I have an 11 pixel border that I wasn't considering when I hardcoded those values.

Have it working, now I just need to find that thread on how to manually calculate gl_FragCoord.

So far so good, thanks guys.

Ilian Dinev
05-12-2009, 12:30 PM
An improvement on the lines' quality could be to gamma-correct the alpha value, I think. A 1D texture LUT for the gamma curve may be necessary, as all procedural functions I tried did not produce nice results.

PickleWorld
05-20-2009, 06:41 AM
So I have the code working (everything but the gamma LUT) and I am wondering if anyone has a suggestion for overlapping lines?

I currently draw lines in blue and white over a grey background. Not a problem until a white line is drawn over a blue line (or vice versa) and the two blend to a near grey color which blends right into the background making the line(s) almost invisible.

I realize it is a direct result of using the alpha for anti-aliasing but I can't have lines just disappearing into the background.

I tried using the smoothstep() function on the dist value (to make the center of the line more opaque) but it does not work very well. I can get the line to appear clearer but I lose almost all of the anti-aliasing effect (well duh.. saw that coming... but had to try).

Any other suggestions?

ZbuffeR
05-20-2009, 09:20 AM
I have a hard time understanding how blending blue and white produces grey.
Can you post a screenshot (png if possible, to avoid jpeg artifacts) of your current method, with a number of white and blue lines crossing each other at various angles ?

Jackis
05-20-2009, 09:30 AM
I might suspect, PickleWorld means, that when he got blue (0,0,1)and white (1,1,1) blended to some sort of (0.5,0.5,1), this color looks almost like gray. And that's the problem for him.
But screenshots are highly desirable. I may be totally wrong.

Ilian Dinev
05-20-2009, 09:36 AM
If you're not going to antialias triangle edges with those AA lines, then draw those lines with a different blending func (i.e additive blending or multiplicative, etc).

PickleWorld
05-20-2009, 12:06 PM
So after some further investigation, I am not sure my code is working 100% correctly. Take a look at this image, the square on the left is drawn without my AA shader, the square on the right is drawn with the shader. In both cases you can see alias artifacts, just on the right it is blended.

http://img60.imageshack.us/img60/4502/aaexample2.png

That does not seem correct?

Ignoring that issue, here is another example image (it is a jpg but you can see what I was talking about above), on the left I have two squares drawn in blue, on the right are the same two squares but one is drawn in white. Where the two overlap you can see how the line is very hard to pick out from the background.

http://img35.imageshack.us/img35/9154/aaexample.jpg

So yes, Jackis was correct, when I mix blue (0,0,1) with white (1,1,1) you get something close to grey (.5,.5,1) (Lines crossing one another at various angles are not a problem).

Drawing with different blending functions is an interesting thought, I might play with that a little.

PickleWorld
05-20-2009, 12:14 PM
p.s. My code looks like this :

Vertex Program :


varying vec4 color;
varying vec4 posix;
uniform vec2 viewport;

void main(void)
{
posix = ftransform();
gl_Position = posix + vec4(0.0, 0.0, 0.0, 0.001);
vec4 v4 = gl_ModelViewMatrix * gl_Vertex ;
gl_ClipVertex = v4;

color = gl_Color;
}


Fragment Program:


varying vec4 color;
varying vec4 posix;
uniform vec2 viewport;

void main( void )
{
vec2 tmp_scr1=vec2(viewport.x/2.0,viewport.y/2.0);
vec2 tmp_scr2=vec2(viewport.x/2.0+0.5,viewport.y/2.0+0.5);
vec2 fake_FragCoord = (posix.xy/posix.w)*tmp_scr1+tmp_scr2;

vec4 lineColor = color;

vec2 ScreenSizeHalf = vec2(viewport.x/2.0,viewport.y/2.0);
vec2 posix1= ((posix.xy/posix.w)+1.0)*ScreenSizeHalf;
vec2 fpos = fake_FragCoord.xy;
float dist = distance(fpos,posix1);
lineColor.a = 1.0-dist;
gl_FragColor = lineColor;
}

Ilian Dinev
05-20-2009, 02:03 PM
The basic multiplicative and additive blending will ignore the alpha value you compute (the one that makes the antialiasing effect). So, either you multiply gl_FragColor.rgb by gl_FragColor.a , or set such a blend-func that varies the multiplication/addition factor by the src-alpha component.

Long story short, when doing additive blending,
gl_FragColor.rgb *= gl_FragColor.a; // at end of frag shader

when doing multiplicative blending:
gl_FragColor.rgb = gl_FragColor.rgb*gl_FragColor.a + vec3(1.0,1.0,1.0)*(1.0 - gl_FragColor.a); // again at end of frag shader. Can be optimized.

ZbuffeR
05-20-2009, 02:20 PM
I just copied Ilian sample code (added .0 to integer values) and it worked out of the box.
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=249059#Post249059

PickleWorld
05-21-2009, 07:39 AM
I just copied Ilian sample code (added .0 to integer values) and it worked out of the box.
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=249059#Post249059

Hmm, you are right. I removed the fake fragcoord code that I added (to make things work on ATI) and the code works much better using gl_FragCoord.

So... can anyone tell me what is wrong with my fake fragCoord code? I got it from another thread here on the boards.

I have tried both of the following (and neither workds correctly) ...


vec3 fake_FragCoord;
fake_FragCoord.x = ((posix.x/posix.w)*0.5+0.5)*viewport.x;
fake_FragCoord.y = ((posix.y/posix.w)*0.5+0.5)*viewport.y;
fake_FragCoord.z = 0.5*posix.z/posix.w+0.5;




vec2 tmp_scr1=vec2(viewport.x/2.0,viewport.y/2.0);
vec2 tmp_scr2=vec2(viewport.x/2.0+0.5,viewport.y/2.0+0.5);
vec2 fake_FragCoord = (posix.xy/posix.w)*tmp_scr1+tmp_scr2;

Jackis
05-21-2009, 07:50 AM
Well, when you make like this with fake frag_coord for lines, you should always get the same points, as ftransform interpolates. You won't get any real anti-aliasing. Why? Because lines has only 2 points, and the thing is interpolation is done not between 4 points of actual quad, but between these 2 ends. A bit complicated, I have poor english, sorry.
Make quads, not war! (c) I would recommend you using simple textrued quads for this, and you wouldn't get problems with correct attribute interpolation.

Ilian Dinev
05-21-2009, 09:31 AM
I would recommend you using simple textrued quads for this, and you wouldn't get problems with correct attribute interpolation.
Plus, the antialiasing quality will be perfect.

PickleWorld
05-21-2009, 12:34 PM
I understand why using a quad would work better for interpolation but I am confused as to how I get the vertices to send to the card?

All my lines are stored in large arrays and drawn using either glDrawElements() or glMultiDrawArrays() (I used both in different spots for different things). Now adding 2 more vertices per line to the array (to form a quad) is not a big deal... but which 2 vertices do I add?

Do the quads not need to be screen aligned and 2 pixels wide? In which case as you rotated the scene (or moved the camera in any way) would you not need to constantly update the vertex array? Won't that completely KILL performance?

Am I missing something?

Jackis
05-22-2009, 04:10 AM
Approach is rather simple.
Imagine, you have only 1 line segment (for simplicity). You want to draw it with quad in 2 pixels (independent from zooming).
You make quad, where first 2 vertices hold the same position of 1st segment vertex, last 2 holds 2nd segment vertex. But how do you make offsets?
With each of these 2 vertices you save side direction (equals to segment's normal, with different signs for left and right), and on VS you displace your position in that direction (but also in plane, perpendicular to viewer position) by distance, proportional to W coordinate of line's vertex (it is 1 for ortho and Z for perspective projection), also multiplied by some screen clarity (how may units in one pixel on near plane).
For your application, where all lines seem to be planar and you have ortho projection, such complicated path may be simplified though.
There are issues with texturing, but they be handled via perspective projection, or via barycentric coordinates (if you don't want your texture to give falsy distance appearance, which is caused by perspective transform).

PickleWorld
05-22-2009, 07:58 AM
Don't suppose you have any example code? Assuming not, let me see if I have the right idea as I am confused in some spots.

First, assume I have a line segment (0,1,0) to (0,-1,0) that I want to draw.

Ok, given that, I now have my two vertices which I will duplicate to make my quad. However, what do you mean by segment normal? It is underdefined no?

If you think of the line segement as a vector the you could consider it as the normal of an infinite plane in space. In which case then the normal to the segment would be any vector that lies in that plane correct? So which do I choose?

Once I have my normal, I would draw a quad with the following vertices and normals (note: I would really use a VBO but lets keep this simple for now)...



glBegin(GL_QUADS);
glVertex3f( 0.0, -1.0, 0.0 );
glNormal3f( ??, ??, ?? );
glVertex3f( 0.0, -1.0, 0.0 );
glNormal3f( ??, ??, ?? );

glVertex3f( 0.0, 1.0, 0.0 );
glNormal3f( ??, ??, ?? );
glVertex3f( 0.0, 1.0, 0.0 );
glNormal3f( ??, ??, ?? );
glEnd();


Then in my vertex shader, I use basically the same code I have been using but I add some code to read the vertex and normal and then project the normal into a plane perpendicular to the view position and offset the vertex along that normal by 2 pixels?

and everything else stays the same?

Jackis
05-25-2009, 01:48 AM
Hello.
Yes, if you're working in 3D, so you have to make your segment normals also lying in plane, planar to projection plane. Doing it so makes your quads to be correctly projected.
If you have general case - moving camera and perspective non-centered projection, you may end up with doing homogenous divisions in vertex shader and offseting in direction, perpendicular to screen segment's direction. You can do it totally on VS.
Good reading is nVidia example about volumetric lines. You don't need all this tricky volumetric texturing, but you can look therem how do they make quads on VS.
http://developer.download.nvidia.com/SDK/9.5/Samples/3dgraphics_samples.html#cg_VolumeLine