Sign in to follow this  

Generating eye rays

This topic is 4839 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I'm trying to code a ray tracer but I got stuck on generating eye rays (sorry if I'm just too n00b). I rewrote it 3 times but it's always missing some rays at the end. If the rays must be perfectly simmetrical, the first ray is supposed to have 0.5 as X component and the last ray must have -0.5 as X component. But the last ray has -0.44. I can't really "see" the solution so I'd like to ask for some help. I'd like to know what kind of fast method is there for generating the eye rays. (I chose rotation method because the sine and cosine can be precomputed with a single instruction and what's left is about 4 multiplications, 1 addition and 1 subtraction (without counting floating-point stack register movement instructions). If it helps, I'll post my asm code on request, but it's 123 lines long). Thank you, JVFF

Share this post


Link to post
Share on other sites
Instead of something like

int delta = fov/rays;
int angle;
for(angle = startangle; angle<=endangle; angle+=delta) {
// stuff
}

It's often better to do something like

int delta = fov/rays;
int angle;
for(i=0; i<rays; angle=(delta*i++)+startangle) {
// stuff
}

Share this post


Link to post
Share on other sites
The above post is a guess, but if you're iterating to get your rays, you could be suffering from precision errors. /Or/ your calculation of delta is wrong. (The one in my post probably is).

Share this post


Link to post
Share on other sites
In my code: For each pixel on a line rotate a predefined angle (fov / width). Then when that line is finished a rotate the same angle down and loop again. Is this pure stupidity? I've heard people generating rays by addition, is there such method? My solution was so I could get rid of normailzation (too much clocks wasted) and more precision. If I'm wrong, what is the most efficient method?
Thank you for your replies,

JVFF

Share this post


Link to post
Share on other sites
Basically my point was that you may have accuracy problems, if you just add to or subtract an angle for each ray. Instead, generate the ray by multiplying your change value by the number of the ray and add any starting angle.

(Accuracy problems like these are very visible with fixed point rasterisers.)

Generating rays by simple addition would probably be a case of linear interpolation of each of the three component vectors, for both across and down the set of rays. It's not very accurate, and the rays do not remain normalised, but it's fast and should work.

Share this post


Link to post
Share on other sites
Thanks again for your reply. Although it might be precision error, I still don't know why the last rays are "missing". I think I might be mistaken by my precomputation. Here's the method I coded:

Start with an initial ray pointing from the middle of the screen forward. I then rotate this ray half fov to the left. I calculate the amount to rotate vertically by multiplying fov by height and dividing it by width (fov/width = vfov/height). Then I rotate it by that angle. Then I divide the fov by the width to get a single ray angle, calculate sine and cosine then iterate rotating the ray to generate a new ray, rotate the new ray to generate another ray, etc.

Here's the code (although it only generates rays for the 1st line):


generate_eye_rays: ; Generate eye rays function
; Transform field of view into radians
mov ebx, TempRay ; Load address of temporary ray into ebx

mov [TempRay], dword FOV ; Load field of view into X of temporary ray
mov [TempRay+4], dword ANGLE_180 ; Load 180 into Y of temporary ray

fld dword [ebx] ; Push field of view into fpu
fldpi ; Push PI into fpu
fmulp st1, st0 ; Multiply field of view by pi and pop

fld dword [ebx+4] ; Push 180 into fpu
fdivp st1, st0 ; Divide field of view * pi by 180 and pop

; Calculate sine and cosine of half horizontal frustrum angle
mov ecx, BiVector ; Load address of bi-unit vector into ecx

fld dword [ecx] ; Push 2.0 into fpu
fdivp st1, st0 ; Divide field of view by 2 and pop

fst st1 ; Save half field of view to calculate half vertical frustrum angle

fsincos ; Calculate sine and cosine

; Generate initial ray rotated around Y axis (counter clockwise)
fldz ; Create initial ray Y
fxch st1 ; Swap new Y and new Z

; Calculate sine and cosine of half vertical frustrum angle
mov [TempRay+4], dword WIDTH ; Load width into Y of temporary ray
mov [TempRay+8], dword HEIGHT ; Load height into Z of temporary ray

fild dword [ebx+8] ; Push height into fpu
fmul st0, st4 ; Multiply height by half field of view

fild dword [ebx+4] ; Push width into fpu
fdivp st1, st0 ; Divide height * half field of view by width

fsincos ; Calculate sine and cosine

; Rotate initial ray about X axis
fxch st1 ; Swap cosine * Z and sine
fmul st0, st2 ; Multiply sine by Z
fchs ; Invert
fstp st3 ; Save new Y and pop

fmulp st1, st0 ; Multiply Z by cosine and pop

; Save initial ray
fst dword [ebx+8] ; Save Z value of initial ray
fincstp ; Rotate upwards
fst dword [ebx+4] ; Save Y value of initial ray
fincstp ; Rotate upwards
fst dword [ebx] ; Save X value of initial ray
fdecstp ; Rotate downwards
fdecstp ; Rotate downwards

; Calculate sine and cosine of ray angle
mov [TempRay+12], dword HALF_W ; Load half width into W of temporary ray
fild dword [ebx+12] ; Push half width into fpu

fdivr st0, st4 ; Divide half field of view by half width
ffree st4 ; Free half field of view

fchs ; Invert angle
fsincos ; Calculate sine and cosine

; Ray generation loop
mov edx, HEIGHT ; Loop for every line
mov eax, EyeRayBuffer ; Load address of eye ray buffer into eax

generate_line_of_rays: ; Generate a line of rays
mov ecx, WIDTH ; Loop for every pixel on a line

generate_ray: ; Generate a single ray
fincstp ; Rotate upwards
fincstp ; Rotate upwards

fst dword [eax+8] ; Save Z value
fincstp ; Rotate upwards
fst dword [eax+4] ; Save Y value
fincstp ; Rotate upwards
fst dword [eax] ; Save X value

fst st1 ; Create copy of X value
fmul st0, st4 ; Multiply X by cosine

fdecstp ; Rotate downwards
fdecstp ; Rotate downwards

fst st4 ; Create copy of Z value
fmul st0, st7 ; Multiply Z by sine
fadd st2, st0 ; Add X*cosine and Z*sine to form new X


fdecstp ; Rotate downwards
fmul st4, st0 ; Multiply copy of X by sine

fdecstp ; Rotate downwards
fmul st6, st0 ; Multiply copy of Z by cosine

fxch st5 ; Swap X*sine and cosine
fsubr st0, st6 ; Subtract X*sine from Z*cosine to form new Z
fstp st2 ; Store new Z and pop

fld st4 ; Push cosine into fpu
ffree st5 ; Free used register
ffree st6 ; Free used register

add eax, 12 ; Get next ray's address
sub ecx, 1 ; Decrement ecx
jnz generate_ray ; Execute loop

fincstp ; Rotate upwards
fincstp ; Rotate upwards

ffree st0 ; Free Z
ffree st1 ; Free Y
ffree st2 ; Free X

ret ; Return


Thank you again,

JVFF

Share this post


Link to post
Share on other sites
I can't read x86 assembly very well (never learnt it). This code seems quite straight forward though. (Although I can read ARM, 68k, PIC micro etc.,)

Three points:

Are you generating the correct number of rays? (You seem to be counting them so probably not a problem)

Is your value for delta correct? (angle between rays)

Are you suffering from precision errors?

If (delta*number_of_rays) gives a noticably different (within 3 dp)
to int val; for(int i=0; i<number_of_rays; i++) val+=delta; then you probably have issues.

Basically, there's a slight accuracy error when you do a floating point operation. The more you do on the same thing the more accuracy it looses. If you return to the original vector each time accuracy is greater.


int main() {
int i = 0;
float val = 0;
float delta = 320.0f/60.0f;

for(i = 0; i < 320; i++)
val+=delta;

printf("Interpolate: %f\n", val);
printf("Direct: %f\n", delta*320.0f);
}



Basically, your program is using the interpolate method. I've been suggesting trying the direct method (which is also rather useful for adaptive multisampling)

My results:
Interpolate: 1706.670288
Direct: 1706.666718

If delta is 60/320 then both give the correct answer, as 60/320 has a perfect floating point representation.

Share this post


Link to post
Share on other sites
i did mine my using the vertex shader.

//u can precompute the texcoords for the quad
half dx = pixelLocation.x / g_Display.viewport.z - 1.0;
half dy = pixelLocation.y / g_Display.viewport.w - 1.0;
half4 ray_start_view = half4(dx, dy, 1.0, 1.0);
half3 ray_start_world = mul(ray_start_view, g_WorldViewProjectionInverse).xyz;

cheers!
Edwinz

Share this post


Link to post
Share on other sites

This topic is 4839 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this