Point plus vector gives odd result

Hi,

I have a point ray_start_pos in world coordinates. I add the vector (reflection_dir * ray_length) to get ray_end_pos, which is then transferred to screen coordinates by multiplying the pvm matrix with ray_end_pos.

(Abstract of) original GLSL code:


mat4    pvm = proj * view * model;

vec3    reflection_dir = normalize(reflect(cam_to_ray_start_pos, ground_normal));

float   ray_length = min(1000.0, length(ray_start_pos - cam_pos));

vec3    ray_end_pos = ray_start_pos + reflection_dir * ray_length;
vec4    ray_end_pos_on_screen = pvm * vec4(ray_end_pos, 1.0);

ray_end_pos_on_screen.xyz = ray_end_pos_on_screen.xyz * 0.5 / ray_end_pos_on_screen.w + 0.5;

My PROBLEM: when ray_length is small, around 100, then ray_end_pos_on_screen.xyz is as expected. When ray_length is larger, around 1000, then ray_end_pos_on_screen.xyz seems to ‘wrap around’, in the sense that (in tests especially) ray_end_pos_on_screen.x is suddenly much too large, or has other odd values.

Any idea what could be wrong? Thanks!

What are you doing with ray_end_pos_on_screen? Anything which involves the w component is going to be wrong. What’s the purpose of the last line?

For debugging, the only action done with ray_end_pos_on_screen is to examine its values by deriving the current fragment’s color from it:


[...] (see initial post)

out_col = vec4(ray_end_pos_on_screen.x, ray_end_pos_on_screen.y, 0.0, 1.0); // set the fragment shader's output color

For a small ray_length, e.g. 100.0, the scene gets more intensively red from the screen’s left to right border, as expected. For the very same ray_start_pos and reflection_dir I get odd red/yellow/green patterns if ray_length is greater than around 200.0, e.g. if ray_length = 1000.0.

I just wanted to know if there are things to attend when involving the pvm matrix. In university we were told that “clipping in normalized coordinates will not work due to non linear transformation in z”. I wonder if this could somehow explain my odd ray_end_pos_on_screen content?!?

Bear in mind that if the result is outside of the unsigned unit cube, each component will be clamped separately, meaning that the direction (i.e. hue) won’t be preserved.

[QUOTE=JasonRay;1280817]
I just wanted to know if there are things to attend when involving the pvm matrix. In university we were told that “clipping in normalized coordinates will not work due to non linear transformation in z”. I wonder if this could somehow explain my odd ray_end_pos_on_screen content?!?[/QUOTE]
Clipping isn’t a factor here, although the lack of clipping may be. If eye-space Z is positive (behind the viewpoint), clip-space W will be negative, resulting in normalised X,Y,Z all being flipped. Clipping exists largely to prevent that situation from occurring; even without the near-plane test, the geometry which remains after clipping will be in front of the viewpoint.

Provided that ray_end_pos lies within the view frustum, ray_end_pos_on_screen.xyz is calculated correctly. If ray_end_pos is outside of the view frustum, the result will be wrong. In particular, if the reflection of ray_end_pos in the viewpoint is inside of the view frustum, ray_end_pos_on_screen.xyz will correspond to that reflection. You might want to explicitly test for ray_end_pos_on_screen.w<0 before dividing by it and discard (or set a constant colour) for that case. And also test whether any of abs(ray_end_pos_on_screen.xyz/ray_end_pos_on_screen.w) are greater than one.

Seems as this did the job, thank you!

One more question: I have a point in world space, e.g. ray_start_pos, and from there a vector that points into some arbitrary direction (in world space), e.g. reflection_dir, and the length of reflection_dir, e.g. ray_length (just as in the example of my initial post).

Is there an easy way at all to determine ray_length so that ray_end_pos = ray_start_pos + reflection_dir * ray_length is located at the (nearest) screen border? I mean is there an easy way to do correct clipping in the given case? ‘Easy way’ shall mean around 5 to 10 or so lines of shader code, in any case easier than the whole Cohen-Sutherland machinery.

[QUOTE=JasonRay;1280830]
One more question: I have a point in world space, e.g. ray_start_pos, and from there a vector that points into some arbitrary direction (in world space), e.g. reflection_dir, and the length of reflection_dir, e.g. ray_length (just as in the example of my initial post).

Is there an easy way at all to determine ray_length so that ray_end_pos = ray_start_pos + reflection_dir * ray_length is located at the (nearest) screen border? I mean is there an easy way to do correct clipping in the given case? ‘Easy way’ shall mean around 5 to 10 or so lines of shader code, in any case easier than the whole Cohen-Sutherland machinery.[/QUOTE]

It’s much simpler if you transform both ray_start_pos and reflection_dir to clip space. In that situation, and provided that ray_start_pos lies within the view frustum, then you can use:


vec3 l0 = -(ray_start_pos.xyz + ray_start_pos.w) / reflection_dir.xyz;
vec3 l1 = -(ray_start_pos.xyz - ray_start_pos.w) / reflection_dir.xyz;
vec3 l = max(l0, l1);
ray_length = min(min(ray_length, l.x), min(l.y, lz));

The vector l0 contains the lengths at which the ray will coincide with the x=-w, y=-w and z=-w planes. The vector l1 contains the lengths at which the ray will coincide with the x=w, y=w and z=w planes. You want the smallest positive value. Provided that ray_start_pos is within the view frustum, each component (x, y or z) will be negative in one vector and positive in the other, so max(l0,l1) will choose the positive value for each component.

[QUOTE=GClements;1280833]It’s much simpler if you transform both ray_start_pos and reflection_dir to clip space. In that situation, and provided that ray_start_pos lies within the view frustum, then you can use:


vec3 l0 = -(ray_start_pos.xyz + ray_start_pos.w) / reflection_dir.xyz;
vec3 l1 = -(ray_start_pos.xyz - ray_start_pos.w) / reflection_dir.xyz;
vec3 l = max(l0, l1);
ray_length = min(min(ray_length, l.x), min(l.y, lz));

[/QUOTE]

Hey that works very well! My odd artifacts I described in the initial post are gone.

Thank you very much!

I made some slight changes to your code, I use:


vec3 l0 = -(ray_start_pos_on_screen.xyz + ray_start_pos_on_screen.w) / reflection_dir.xyz;
vec3 l1 = -(ray_start_pos_on_screen.xyz - ray_start_pos_on_screen.w) / reflection_dir.xyz;
vec3 l = max(l0, l1);

ray_length = min(min(ray_length, l.x), min(l.y, l.z));

Hmm. Are my changes correct at all? I mix up screen space ray_start_pos_on_screen and world space reflection_dir?!?

You need to transform reflection_dir as well as ray_start_pos.

Do you mean I have to transform ray_start_pos AND reflection_dir TO SCREEN SPACE using the pvm matrix before applying your ray_length calculation?

Yes.

Calculations involving multiple vectors typically only make sense if all vectors are in the same coordinate system.