|
Post by auctux on Mar 18, 2021 20:17:32 GMT
So i tried to leave all the intersections as tdaines and Jamis said: and after removing the filter (t < 0) I got this , is this how it supposed to look:
|
|
|
Post by Jamis on Mar 19, 2021 2:44:21 GMT
Can you share your scene definition? It looks like the checkered plane is intersecting the spheres, instead of sitting behind them.
|
|
|
Post by auctux on Mar 19, 2021 22:09:04 GMT
Thank you Jamis I finally found out what was the issue, since you told me that the problem might be with the intersection between the sphere and the checkered Plane, I checked out the Plane and I realized that I was doing one of the mistakes that you warn us about: so basically I was doing the transform multiplication in the wrong order it was like this: rotation_x(pi/2) * translationMatrix(0, 0, 10) i changed it like this: translationMatrix(0, 0, 10) * rotation_x(pi/2) and I got this finally: I think this might be it this time. thanks again, for anyone who had the same problem as mine , just follow this 2 steps: 1. pass all the intersections even those inferior to zero in you Intersect_world function. as tdaines and Jamis mentioned 2.check your Plane transformation if the order of your multiplication is correct. that should do the work, and sorry for my English still learning
|
|
|
Post by Jamis on Mar 20, 2021 2:48:59 GMT
Fantastic! That looks great. I'm so glad you got it figured out!
|
|
|
Post by mitchel0022 on May 11, 2022 22:40:56 GMT
Adding this for anyone who got stuck because I spent way too long trying to figure this out, make sure you negate your normal vector before calculating the under_point // good if inside { normalv *= -1.0; } let under_point = point - normalv * EPSILON;
// bad let under_point = point - normalv * EPSILON; if inside { normalv *= -1.0; }
|
|
|
Post by confusedorange on Oct 20, 2022 22:57:23 GMT
Adding this for anyone who got stuck because I spent way too long trying to figure this out, make sure you negate your normal vector before calculating the under_point // good if inside { normalv *= -1.0; } let under_point = point - normalv * EPSILON;
// bad let under_point = point - normalv * EPSILON; if inside { normalv *= -1.0; } Dear mitchel0022, I registered on this forum only to tell how much I love you ❤️ Your note saved my fragile sanity
|
|
|
Post by zeothem on Dec 19, 2022 2:36:26 GMT
So I find myself stuck at the same point, but with a decidedly different problem. Hopefully people are still paying attention to this, because I am afraid I am at a loss. So far my code has been able to pass every test up through Chapter 11 Test #8 (Handling Refraction in shade_hit) but when I try to render the scene that people are discussing here, mine comes out blocky, or seemingly low resolution inside the glass sphere and I really haven't an idea why. Here is the glass marble by itself And here it is with the air bubble added to the center of it. If you zoom in, you can see that there are essentially chunks of the same color in 4-9-16 pixel sizes that are the same color and I am quite unsure of why. I suspect it has to do with my color_at function which I can repost here (or can be found in my github repo: F# RayTracer) I attempted to mimic the output on the previous page and I find that my initial call to prepare_computations (at 125,125 mimicking the scene on the previous page) give the same t values and refractive indices In precompute
Main intersection is at t = 4.028495995, object id = 3 t_val 4.028495995 obj id 3 n idx 1.5 t_val 4.568385678 obj id 4 n idx 1.0000034 t_val 5.417668679 obj id 4 n idx 1.0000034 t_val 5.957558362 obj id 3 n idx 1.5 t_val 15.02094768 obj id 2 n idx 1.0 In here obj 2 = floor, 3 = glass marble, 4 = air bubble. Once I go on to compute the color at 125, 125, I get completely different results which makes this frustrating to try and match up, I don't even seem to reach the same recursion depth, which makes me wonder if I am not doing something simple and incorrect. I do match up on Jamis' last four entries for all the terms EXCEPT the color (well except the color after the first time where we do match). What I see is that I never reach the same recursion depth, once I reach a depth of 2 (1, I could be off by one in my counting here) then the ray intersects the floor which is non transparent and we start building back up. == Remaining 2 == Reached non-transparent item id 2 true Under total internal reflection false out of recursion moves false == Remaining 3 == Object id # 3 n1 1.5 n2 1.0 eyev (0.24617184789842556, -0.24617184789842556, -0.9374427143055457, -0) normalv (0.3589807284374399, -0.3589807284374399, -0.861548416063224, -0) under point (-0.35898431824472427, 0.35898431824472427, 0.8615570315473846, 1) n ratio 1.5 cos_i 0.9843941842 sin2_t 0.06967820267 cos_t 0.9645319058 refract direction (-0.18543832601633745, 0.18543832601633745, 0.9650001318593266, 0) refracted color RGB(0.6692438880894562, 0.6692438880894562, 0.6692438880894562) == Remaining 4 == Object id # 4 n1 1.0000034 n2 1.5 eyev (0.11201785698040068, -0.11201785698040068, -0.9873722699342115, -0) normalv (0.4637181819920862, -0.4637181819920862, -0.754937676487211, -0) under point (-0.231863728177863, 0.231863728177863, 0.37747638762037033, 1) n ratio 0.6666689333 cos_i 0.8492939613 sin2_t 0.1238674056 cos_t 0.9360195481 refract direction (-0.24617184789842556, 0.24617184789842556, 0.9374427143055457, 0) refracted color RGB(1.0857648825025026, 1.0857648825025026, 1.0857648825025026) == Remaining 5 == Object id # 4 n1 1.5 n2 1.0000034 eyev (-0.02645089737072017, 0.02645089737072017, -0.9993001051018494, -0) normalv (-0.2734541100138954, 0.2734541100138954, -0.922196128506847, 0) under point (-0.13672432046584757, 0.13672432046584757, -0.4610888422921384, 1) n ratio 1.4999949 cos_i 0.9360169013 sin2_t 0.2787109157 cos_t 0.849287398 refract direction (-0.11201785698040068, 0.11201785698040068, 0.9873722699342115, 0) refracted color RGB(0.9771883942522523, 0.9771883942522523, 0.9771883942522523) == Remaining 6 == Object id # 3 n1 1.0 n2 1.5 eyev (0.03733084416743292, -0.03733084416743292, -0.9986054356689101, -0) normalv (-0.15038715621763504, 0.15038715621763504, -0.9771220018449821, 0) under point (-0.1503856523460725, 0.1503856523460725, -0.9771122306249613, 1) n ratio 0.6666666667 cos_i 0.9645311834 sin2_t 0.03096870947 cos_t 0.9843938696 refract direction (0.02645089737072017, -0.02645089737072017, 0.9993001051018494, 0) refracted color RGB(1.2710603727077006, 1.2710603727077006, 1.2710603727077006)
Here is my relevant code (with logging that isn't checked into github) hopefully someone can hit me with a clue by four and show me what I am missing. let rec shade_hit (p: PreCompute) (w: World) (d: int) (logIt: bool): Color = let mat = extract_obj p |> extract_material let in_shadow = is_shadowed w (extract_over_point p) let surface = lights w |> List.map (fun l -> lighting mat (extract_obj p) l (extract_over_point p) (extract_eyev p) (extract_normalv p) in_shadow) |> List.fold (fun acc c -> acc + c) (Color(0,0,0)) let reflect = reflected_color w p d false let refract = refracted_color w p d logIt surface + reflect + refract and color_at (w: World) (r: Ray) (d: int) (logIt: bool): Color = let its = intersect_world r w match hit its with | Some i -> shade_hit (prepare_computations i r its logIt) w d logIt | None -> Color(0,0,0) and reflected_color w p d logIt = if d < 1 then black else let reflect_val = extract_obj p |> extract_material |> reflective if reflect_val = 0.0 then black else let reflect_ray = make_ray (extract_over_point p) (extract_reflectv p) let c = color_at w reflect_ray (d-1) logIt c * reflect_val and refracted_color (w: World) (comps: PreCompute) (remaining: int) (logIt: bool): Color = let t = comps |> extract_obj |> extract_material |> transparency // gets the transparency value of the material in the precompute structure let n1 = extract_n1 comps let n2 = extract_n2 comps let o = extract_obj comps |> id let n_ratio = n1/n2 let eye_v = extract_eyev comps let normal_v = extract_normalv comps let cos_i = dot eye_v normal_v let sin2_t = n_ratio * n_ratio * (1. - cos_i*cos_i)
if t = 0.0 || sin2_t > 1.0 || remaining < 1 then let j2 = if logIt then printfn "== Remaining %A ==" remaining printfn "Reached non-transparent item id %A %A" o (t = 0.0) printfn "Under total internal reflection %A" (sin2_t > 1.0) printfn "out of recursion moves %A" (remaining < 1) 1 else 1 black else let cos_t = sqrt (1. - sin2_t) // Direction of the refracted ray let direction = normal_v * (n_ratio * cos_i - cos_t) - eye_v * n_ratio let under_point = extract_under_point comps let refract_ray = make_ray under_point direction let color_raw = color_at w refract_ray (remaining - 1) logIt let color_ans = color_raw * t let junk = if logIt then printfn "== Remaining %A ==" remaining printfn "Object id # %A" o printfn "n1 %A" n1 printfn "n2 %A" n2 printfn "eyev %A" eye_v printfn "normalv %A" normal_v printfn "under point %A" under_point printfn "n ratio %A" n_ratio printfn "cos_i %A" cos_i printfn "sin2_t %A" sin2_t printfn "cos_t %A" cos_t printfn "refract direction %A" direction printfn "refracted color %A" color_ans 1 else 1 color_ans
I very much appreciate any pointers. Thanks! EDIT: I will append my prepare_computations function to here as well so that the most critical code is in one place. let prepare_computations (i: Intersection) (r: Ray) (xs: Intersection list) (logit: bool) = let compute_n1n2 (hit: Intersection): double * double = let index_from_containers (cs: Shape list) : double option = match cs with | h::_ -> Some(extract_material h |> refractive_index) | [] -> Some(1.0) let update_containers (i: Intersection) (cs: Shape list): Shape list = if List.contains (object i) cs then let indexAt = List.findIndex (fun elem -> elem = (object i)) cs List.removeAt indexAt cs else (object i) :: cs let rec compute_aux (containers: Shape list) (intersections: Intersection list): double option * double option = match intersections with | i::is -> let opt_n1 = if i = hit then index_from_containers containers else None let mod_conts = update_containers i containers if i = hit then let opt_n2 = index_from_containers mod_conts (opt_n1, opt_n2) else compute_aux mod_conts is | _ -> failwith "Can we reach here???" match (compute_aux List.empty<Shape> xs) with | (Some(n1), Some(n2)) -> (n1, n2) | _ -> failwith "Something went wrong" let t = t_val i let o = object i let point = position r t let eyev = -(direction r) let normalv = normal_at o point let inside, adj_normal = if (dot normalv eyev) < 0 then (true, -normalv) else (false, normalv) let over_point = point + adj_normal * EPSILON let under_point = point - adj_normal * EPSILON let reflectv = reflect (direction r) normalv let junk = if logit then printfn "In precompute" printfn "Main intersection is at t = %A, object id = %A" t (id o) List.map (fun x -> printfn "\tt_val %A" (t_val x) printfn "\tobj id %A" (id (object x)) printfn "\tn idx %A" (refractive_index (extract_material (object x))) ) xs |> ignore 1 else 1
let (n1, n2) = compute_n1n2 i make_precompute t o point over_point under_point eyev adj_normal reflectv n1 n2 inside
The compute_n1n2 is a little wonky since I am trying to do this in a purely functional manner, but it passes all the tests as I would expect.
|
|
|
Post by Jamis on Dec 19, 2022 2:43:05 GMT
zeothem, this is very drive-by, sorry (I can't dig into all the code you provided, but will when I have more time). Your images actually look physically correct, but it might be that your material has something set too high, perhaps reflection, or diffuse, or some such. Could you share your scene definition, as well?
|
|
|
Post by zeothem on Dec 19, 2022 2:48:14 GMT
Jamis, sure thing, I am building them based off the YAML (or my interpretation of it from the previous page) let checkers = make_pattern (Checkers(Color(0.15,0.15,0.15), Color(0.85,0.85,0.85))) let floor_material = make_material [checkers] 0.8 0.2 0.0 0.0 0.0 0.0 1.0 let floor = make_shape Plane |> set_shape_transform (chain [rotation_x halfPi; translation 0 0 10]) |> set_shape_material floor_material
let glass_ball = make_shape Sphere |> set_shape_material (make_material (solid_pattern white) 0 0 0.9 300 0.9 0.9 1.5)
let air_bubble = make_shape Sphere |> set_shape_material (make_material (solid_pattern white) 0 0 0.9 300 0.9 0.9 1.0000034) |> set_shape_transform (scaling 0.5 0.5 0.5)
let ls = make_pointlight (make_point 2 10 -5) (Color(0.9, 0.9, 0.9)) let world = make_world [ls] [floor; glass_ball; air_bubble]//;back_wall; side_wall; middle] let camera = make_camera 300 300 0.45 |> set_camera_transform (view_transform (make_point 0 0 -5) (make_point 0 0 0) (make_vector 0 1 0))
Here, the arguments to make_material are: pattern, ambient, diffuse, specular, shininess, reflective, transparency, and refractive_index EDIT: And here is a link to a higher resolution rendering of the same scene (800x800) ( imgur link). I would have expected it to look like auctux's on the top of this page. Zooming in, and the effect of "looking through the glass make it look like a low res image" seems really clear to me as I have no better explanation for it. Not, I have NOT implemented the Fresnel effect yet, that was for tonight, but I got tripped up with the "regular" version. Thanks again
|
|
|
Post by piwakawaka on Apr 22, 2023 12:06:17 GMT
Thanks for translating the scene to YAML, chrisd ! That helps a lot. I've also looked at your code, totally , and like chrisd, I'm not seeing anything obviously out of place. For the purposes of troubleshooting this, here's the scene I used for the image in the book: - add: camera width: 300 height: 300 field-of-view: 0.45 from: [ 0, 0, -5 ] to: [ 0, 0, 0 ] up: [ 0, 1, 0 ]
- add: light intensity: [ 0.9, 0.9, 0.9 ] at: [ 2, 10, -5 ]
[snip]
Just curious as to whether the image in the book on page 159 has been horizontally flipped? With the point light positioned as per Jamis' YAML above, both my image and the other images in this thread show the specular "hot spot" on the right hand side of the image, yet, the book image shows it on the horizontally opposite side (as if it was `at: [-2, 10, -5]`).
Book image:
My render using Jamis' YAML (not no reflections - see post below):
The reason I ask is because I don't want to have made it this far only to discover that my x axis is inverted...!
|
|
|
Post by piwakawaka on Apr 22, 2023 12:09:19 GMT
EDIT: And here is a link to a higher resolution rendering of the same scene (800x800) ( imgur link). I would have expected it to look like auctux's on the top of this page. Zooming in, and the effect of "looking through the glass make it look like a low res image" seems really clear to me as I have no better explanation for it. Not, I have NOT implemented the Fresnel effect yet, that was for tonight, but I got tripped up with the "regular" version. Thanks again Hi zeothem, did you get to the bottom of this? I have a very similar problem, at the same point in the book, and I think it's with my reflection code. In shade_hit() the refraction color is working correctly, but my reflection color is way over-blown, just like yours. I'm using the same YAML that Jamis provided earlier in this thread.
By adjusting the sum in the return value of shade_hit():
Surface + reflection + refraction, depth 5:
Surface + refraction (no reflection), depth 5:
Surface + reflection only, depth 5:
These aren't a true linear decomposition of the image because the recursive nature of the algorithm feeds these back in per depth, but something is awry once reflection is added into the mix.
|
|
|
Post by piwakawaka on Apr 22, 2023 12:19:58 GMT
zeothem : At recursion depth 6, my result looks exactly like yours:
I can actually make mine look exactly like the book if I set the material's reflectivity of both spheres to zero.
Hmm, now I come to think of it, I don't really see any evidence of any surface reflections in the first book image - it looks like pure refraction to me. Was the book image really generated with the material reflection value set to 0.9?
EDIT: After implementing the Schlick approximation, it seems that this over-blown reflection disappears:
Now it looks almost identical to the second book image. So perhaps:
1. the first image in the book *was* rendered with reflective == 0.0, and, 2. the second image is rendered with reflective = 0.9, and the Schlick approximation to combine reflections and refractions.
Generally my reflections in other contexts look correct, so I think the over-blown visual was the result of simply adding the refractions and reflections and getting pixel values saturating at white. I will move on, with the assumption that my reflections are correct.
Only one question remains in my mind - in the referenced paper (https://graphics.stanford.edu/courses/cs148-10-summer/docs/2006--degreve--reflection_refraction.pdf), the equation for the refracted ray (Eq 22, page 3) has the incident vector and normal vector terms added together, yet in the Book on page 158, the terms are subtracted:
# Compute the direction of the refracted ray direction ← comps.normalv * (n_ratio * cos_i - cos_t) - comps.eyev * n_ratio I'd like to understand why this is the case, if anyone knows?
|
|