PDA

View Full Version : Fragment program to mimic rectangular clipping plane



Sergey K.
09-13-2006, 01:00 AM
I wrote this fragment program to highlight part of a 3D model that is in SectPlane plane and falls completely into the triangles (in this plane) defined as (P1, P2, P3) and (P4, P2, P3)


varying vec4 Position; // position of the fragment (initialized as Position = gl_Vertex; in vertex program)
uniform vec4 SectPlane; // equation of the plane (in the same coord. system as Position)
uniform vec3 P1; // four points in this plane to form two triangles (P1, P2, P3) and (P4, P2, P3)
uniform vec3 P2;
uniform vec3 P3;
uniform vec3 P4;
bool SameSide( vec3 p1, vec3 p2, vec3 a, vec3 )
{
vec3 cp1 = cross( b-a, p1-a );
vec3 cp2 = cross( b-a, p2-a );
return dot(cp1, cp2) >= 0;
}
bool PointInTriangle( vec3 p, vec3 a, vec3 b, vec3 c )
{
return SameSide(p, a, b, c) && SameSide(p, b, a, c) && SameSide(p, c, a, b);
}
void main()
{
float Threshold = 0.01 + 10.0 * gl_FragCoord.z - 1.0;
float Dist = abs( dot( Position, SectPlane ) );
if ( Dist > Threshold ) { discard; }
if ( !( PointInTriangle( vec3( Position ), P1, P2, P3 ) || PointInTriangle( vec3( Position ), P4, P2, P3 ) ) ) { discard; }
gl_FragColor = vec4( 1.0, 0.0, 0.0, 0.0 );
}The problem is i have to make the thickness of this section independant from the distance to the camera. I tried to use Threshold = 0.01 + 10.0 * gl_FragCoord.z - 1.0; as a criterium, but this brings no success at all.

Please, any suggestions and improvements are welcome! :)

k_szczech
09-13-2006, 01:26 AM
Instead of using gl_FragCoord.z you could compute distance in vertex shader, and pass it as varying variable in fragment shader.

Sergey K.
09-13-2006, 02:33 AM
Have just added this computation to a vertex shader:


Depth = length( vec3(gl_ModelViewMatrix * gl_Vertex) );and changed fragment shading code to:


Threshold = 0.01 + pow( 0.0005 * (Depth - 200.0), 2 );This also dosn't seem to behave the way i want it to. I just want this section to behave like a line, i.e. be always some pixels in width regardless of the distance to the camera.

k_szczech
09-13-2006, 04:02 AM
Seems like you misunderstood me a bit. :) Try this:

Threshold = 0.01 * Depth;

Sergey K.
09-13-2006, 04:18 AM
I've tried it, also 0.01 was too big, i used 0.0003. But it looks even worse than my formula with squared depth. Thickness of the section zooms in and out with the model. I'm upset :(

k_szczech
09-13-2006, 05:08 AM
Then it must be something else. Look at the first line in your fragment shader:

varying vec4 Position; // position of the fragment (initialized as Position = gl_Vertex; in vertex program)Shouldn't position be transformed by modelview matrix?
Make sure you have all values in proper coordinate spaces.

Sergey K.
09-13-2006, 06:02 AM
Position and SectPlane are in the same coordinate space, so the calculation of dot product Position*SectPlane gives me the correct distance of the point from this plane. After that i compare it to Threshold and discard the fragment if it is outside of the tolerance. I guess i compute the Threshold totally wrong but i don't have any idea how it could be fixed.

k_szczech
09-13-2006, 08:29 AM
Position*SectPlane gives me the correct distanceTry mapping abs(dot(Position,SectPlane)) to color and see if you get what's expected. You never know for sure until you see it with your own eyes.


I guess i compute the Threshold totally wrongMy bet is still on coordinate spaces. Threshold is just too simple to compute - it's just distance to vertex rescaled by some constant. Not much can go wrong here (unless you have some fancy transformations in projection matrix, too).

If you won't be able to solve this then I'd like to know what your vertex shader looks like. Also, fragments of code where you set shader uniforms and matrices would be helpful.

Sergey K.
09-13-2006, 09:00 AM
I'm out of the office, so code fragments will be only 'morrow in the morning. I swear this dot product is correct since the section itself is rendered exactly as it was expected, so distance from a plane is computed the right way, so is PointInTriangle() - i could see it with my own eyes ;) . Just the width goes wrong.

Komat
09-13-2006, 12:29 PM
Calculating the radial distance from camera point in vertex shader and sending it to fragment shader will not work for two reasons:
1) The perspective division, which is the thing you need to compensate if the tickness should remain the same, operates on distance from eye plane.
2) The interpolation of radial distance will offten result in values that do not correspond to correct radial distance inside the triangle.

When the perspective division is done, the vector is divided by the W coordinate. To calculate the correct threshold for some distance, you should imho multiply the base treshold value with W corresponding to the distance.

Sergey K.
09-13-2006, 11:32 PM
So, here they are:

vertex program:


varying vec4 Position;
varying float Depth;
void main()
{
Position = gl_Vertex;
Depth = length( vec3(gl_ModelViewMatrix * gl_Vertex) );
gl_Position = ftransform();
}and recent fragment program:


varying vec4 Position; // position of the fragment (initialized as Position = gl_Vertex; in vertex program)
uniform vec4 SectPlane; // equation of the plane (in the same coord. system as Position)
uniform vec3 P1; // four points in this plane to form two triangles (P1, P2, P3) and (P4, P2, P3)
uniform vec3 P2;
uniform vec3 P3;
uniform vec3 P4;
bool SameSide( vec3 p1, vec3 p2, vec3 a, vec3 ){
vec3 cp1 = cross( b-a, p1-a );
vec3 cp2 = cross( b-a, p2-a );
return dot(cp1, cp2) >= 0;
}
bool PointInTriangle( vec3 p, vec3 a, vec3 b, vec3 c )
{
return SameSide(p, a, b, c) && SameSide(p, b, a, c) && SameSide(p, c, a, b);
}
void main()
{
float Threshold = 0.0003 * Depth;
float Dist = abs( dot( Position, SectPlane ) );
if ( Dist > Threshold ) { discard; }
if ( !( PointInTriangle( vec3( Position ), P1, P2, P3 ) || PointInTriangle( vec3( Position ), P4, P2, P3 ) ) ) { discard; }
gl_FragColor = vec4( 1.0, 0.0, 0.0, 0.0 );}I guess instead of length() i should use .z coordinate, right?

How could i compensate the perspective division? Should i use gl_FragCoord.w? This doesn't seem to work either.

Komat
09-14-2006, 01:00 AM
I guess instead of length() i should use .z coordinate, right?
The .w coordinate.



How could i compensate the perspective division? Should i use gl_FragCoord.w? This doesn't seem to work either.By multiplying the treshold with .w coordinate. Did you tried multiplying by the 1/gl_FragCoord.w ?

Sergey K.
09-14-2006, 01:34 AM
I've tried .z, .w, 1/,w and also .z*.w and .z / .w - result is bad and if i use .w the thickness depends on size of the viewport.

k_szczech
09-14-2006, 03:17 AM
if i use .w the thickness depends on size of the viewportYou have to scale it by expected section width and viewport size. This will work for aspect ratios close to 1:1 since viewport can have different width and height, but I suggest you first make it work with 1:1.

Sergey K.
09-14-2006, 05:15 AM
I'm stuck with it. It doesn't want to behave correctly. The size of the section is changing if i zoom my model in and out.

Komat
09-14-2006, 06:25 AM
Can you post some screenshots?

Sergey K.
09-14-2006, 10:57 AM
Ohh... only on monday. I'm out of the office already. I will post a couple of screenshots asap.

Brolingstanz
09-14-2006, 11:19 AM
Take your time, Sergey K. Don't strain yourself.

Perhaps this weekend you could stop by the library and check out some math books.

Sergey K.
09-17-2006, 11:30 PM
Here are the screenshots:

http://www.linderdaum.com/Bugs/pic1.JPG
http://www.linderdaum.com/Bugs/pic2.JPG
http://www.linderdaum.com/Bugs/pic3.JPG
http://www.linderdaum.com/Bugs/pic4.JPG

You could see how red section changes it's width. I just don't want this to happen.

Komat
09-18-2006, 03:37 PM
Which treshold equation did you use for the screenshots? The zooming is done by moving the object or by changing the field of view of the camera?

Added: If you are zooming by changing field of view, you need to multiply the treshold by appropriate constant. For example, if you are using something like:

glFrustum(left * zoom, right * zoom, bottom * zoom, top * zoom, znear, zfar)then the treshold should be something like:
good_looking_constant * w_coordinate * zoomThe optical thickness of the line will still change in dependency on the mesh geometry and orientation of the plane.

Sergey K.
09-18-2006, 11:22 PM
I zoom by mooving the object closer to the camera. FOV is not affected.

Komat
09-19-2006, 12:48 PM
If the FOV is not changed, the multiplication by w coordinate should be sufficient. At least when I modified some GLSL demo to emulate that, the thickess appeared as constant (at least based on measurements in MS Paint).

Sergey K.
09-20-2006, 07:09 AM
Just tried this solution. So...it has failed :(
Threshold = 0.3 * gl_FragCoord.w was used.

Seems i'm realy stuck with it... and nobody knows how to help

Komat
09-20-2006, 11:12 AM
When I modified one GLSL example by adding following lines:

To VS:

MCPosition = vec3 (gl_Vertex); <-- this was actually already here
w_coordinate = gl_Position.w ;To PS:

float threshold = 0.01 * w_coordinate ;
float Dist = abs( dot( vec4( MCPosition.xyz, 1.0 ), vec4( normalize( plane.xyz ), plane_offset ) ) ) ;

if ( Dist <= threshold ) {
gl_FragColor.xyz = float3( 1.0, 0.0, 0.0 ) ;
}I got this (http://www.webpark.cz/jiridvorak/ruzne/line.html)

Sergey K.
09-20-2006, 11:35 PM
And again something is going wrong.

Finally i have this code:

VP

varying vec3 Position;
varying float WCoord;
void main()
{
Position = vec3( gl_Vertex );
gl_Position = ftransform();
WCoord = gl_Position.w;
}FP

varying vec3 Position;
varying float WCoord;
uniform vec4 SectPlane;
uniform vec3 P1;
uniform vec3 P2;
uniform vec3 P3;
uniform vec3 P4;
bool SameSide( vec3 p1, vec3 p2, vec3 a, vec3 b )
{
vec3 cp1 = cross( b-a, p1-a );
vec3 cp2 = cross( b-a, p2-a );
return dot(cp1, cp2) >= 0;
}
bool PointInTriangle( vec3 p, vec3 a, vec3 b, vec3 c )
{
return SameSide(p, a, b, c) &amp;&amp; SameSide(p, b, a, c) &amp;&amp; SameSide(p, c, a, b);
}
void main()
{
float Threshold = 0.1 * WCoord;
float Dist = abs( dot( vec4( Position.xyz, 1.0 ), SectPlane ) );
if ( Dist > Threshold ) { discard; }
if ( !( PointInTriangle( Position, P1, P2, P3 ) &amp;#0124;&amp;#0124; PointInTriangle( Position, P4, P2, P3 ) ) ) { discard; }
gl_FragColor = vec4( 1.0, 0.0, 0.0, 0.0 );
}In this case i also have an unwanted scaling of the line.

k_szczech
09-23-2006, 02:47 AM
Try mapping abs(dot(Position,SectPlane)) to color and see if you get what's expected.
I swear this dot product is correct since the section itself is rendered exactly as it was expectedI agree that section is rendered exactly WHERE expected but since it's width scales, then there is a bug somewhere, right?
Could you map that dot product to color anyway?
Scale that dot product by some constant value, so the color would change slowly over entire mesh. Then make 2 screenshots with different zoom, but make sure that object is fully visible in both screens.

Komat
09-23-2006, 06:04 AM
Whan I tried your shaders in the sample application, the scalling was not there. How do you construct various matrices (modelview & projection) used to display the object?

k_szczech
09-23-2006, 09:24 AM
He is passing plane's equation and triangles via uniforms and compares them with gl_Vertex
It probably means that his object space is the same as world space and the plane is defined in the same space (it should be if this is supposed to work without any transformations in vertex shader).
Matrix contents don't affect this shader in any way other than gl_Position = ftransform(). This causes entire geometry to zoom in and out on the screen, but using .z or .w coordinates should suffice to fix it. Unless his plane equation is wrong - normal vector not normalized or something like that. This is why I asked to map that dot product to color to see what really happens with point<->plane distance.

Sergey K.
09-23-2006, 09:59 AM
Plane equation seems to be correct and it is normalized. I will post screenshots on monday.

Komat
09-23-2006, 10:32 AM
Originally posted by k_szczech:

Matrix contents don't affect this shader in any way other than gl_Position = ftransform().Which is important thing since it calculates the w coordinate and onscreen size of the object. His shader works in my testing application which does have constant plane equation and constant scaling in modelview/projection matrices so it is reasonable to assume that one from those two things changes when the object is zoomed in his application.

Because on the screenshots the plane is always at the same position on the object, I did assume that the plane is stored in object space and does not change during the zooming so it is reasonable to assume that the change might be from the matrix.

Sergey K.
09-23-2006, 10:39 AM
Both model and plane are defined in the same object space.

Sergey K.
09-24-2006, 09:49 PM
Here are the screenshots with the visualized distance.

http://www.linderdaum.com/Bugs/bug_2_1.jpg

http://www.linderdaum.com/Bugs/bug_2_2.jpg

http://www.linderdaum.com/Bugs/bug_2_3.jpg

http://www.linderdaum.com/Bugs/bug_2_4.jpg

As you can see, it is tightly connected to the model.

k_szczech
09-25-2006, 02:09 AM
Thanks, that eliminats half of possibilities :)
So we only need to calculate the threshold properly. It should be proportional (linearily) to distance from pixel to camera measured along eye plane's normal vector. We don't care what the FOV is or how far the eye plane is - if object's distance is 2x bigger then threshold must be 2x bigger, too.
So if you have all transformations in MODELVIEW matrix and you only have perspective in PROJECTION matrix, then threshold should be multiplied by z coordinate of vertex transformed by MODELVIEW matrix:

threshold = vec4(gl_ModelViewMatrix * gl_Vertex).z * 10.0 / viewportSize;Just trmember to make sure you on't put any transformations to PROJECTION matrix except for glFrustum.
A little performance hint: note that threshold and distance to your plane will change linearily across polygon's surface and therefore can be calculated in vertex shader, but if you have lots of small polygons then it would be better to leave that calculations in fragment shader.
Also - instead of performing 2 triangle hit tests you could define 4 planes and just test dot products. These dot products will also change linearily, so only >0 test must be done in fragment shader.

Sergey K.
09-25-2006, 02:59 AM
Again i'm doing something wrong... The value of my vec4(gl_ModelViewMatrix * gl_Vertex).z is... zero :(

k_szczech
09-25-2006, 04:48 AM
How do you know it's 0? If you map it to color then perhaps it's a negative value and just appears black?

Sergey K.
09-25-2006, 05:09 AM
You are right! It was just negative! I've put an abs() to your equation and made some other changes:


Thresold = 2.0 / abs( vec4(gl_ModelViewMatrix * gl_Vertex).z / ViewportSize)And now it is all fine!

Many thanks to everybody who was spilling inks into this thread during 2 weeks. Personal respect to k_szczech. Your help is realy appriciated!

Topic is closed.