jeff
New Member
Posts: 5
|
Post by jeff on Dec 30, 2018 20:08:23 GMT
I've been wrestling with “Test #2: Determining n1 and n2” in the refraction chapter for a while and I think I must be missing something here.
The book provides this pseudo code to implement in the prepare_calculations function:
containers ← empty list
for i ← each intersection in xs if i = hit then if containers is empty comps.n1 ← 1.0 else comps.n1 ← last(containers).material.refractive_index end if end if
if containers includes i.object then remove i.object from containers else append i.object onto containers end if
if i = hit then if containers is empty comps.n2 ← 1.0 else comps.n2 ← last(containers).material.refractive_index end if
terminate loop end if end for
In the feature test, xs gets defined as:
xs ← intersections(2:A, 2.75:B, 3.25:C, 4.75:B, 5.25:C, 6:A) If I understand this correctly, this means that xs is an array of intersections ordered in increasing t-value.
Further, I understand the "hit" to be the intersection with the lowest t-value, which in this case is the first in xs.
But if that's correct, then when I pass xs into prepare_calculations, "i = hit" will always be true on the first iteration through "for i ← each intersection in xs" and the loop will terminate. That can't be right.
I must be misunderstanding something. Any help appreciated!
|
|
|
Post by ascotti on Dec 30, 2018 21:34:42 GMT
Hi, you are correct about the "hit". However, for this test we call prepare_computations() for every intersection in the list just to check that it works correctly, pretending that each intersection would be the hit.
|
|
jeff
New Member
Posts: 5
|
Post by jeff on Dec 30, 2018 22:21:46 GMT
I was able to get the tests to pass by changing "if i = hit" to "if i = intersection", where intersection is the arg I passed into prepare_computations. Maybe I'm just confused about what "hit" is supposed to be in this context. Hopefully this is correct. I'll find out by the end of the chapter. Thanks!
|
|
|
Post by Jamis on Dec 31, 2018 1:05:11 GMT
jeff -- that's it exactly. "hit" just refers to the intersection that is currently being treated as the hit, which is (in this case) the argument passed to prepare_computations. Sorry that was ambiguous!
|
|
jeff
New Member
Posts: 5
|
Post by jeff on Dec 31, 2018 3:50:26 GMT
It made sense after I went for a walk. Thanks for your response and this fantastic book -- having a ton of fun with it.
|
|
|
Post by inventordave on Nov 16, 2019 21:17:17 GMT
Hi Jamis, or anyone else,
I am having a different problem with my implementation of the pseudocode for the test(s) on p152. I am not getting the right values for n1 and n2. First I will show the source code (in JavaScript) for prepare_computations(…), including a second helper function immediately after, then I will show my test function (I am making use of the Developer Tools Debugger), then finally the results I am getting. It should be straightforward for someone like Jamis I imagine, but I have MCI, and it's late...
function prepare_computations(i, r, xs) { var comps = new compsds(); comps.t = i.t; comps.object = i.object; comps.point = _position(r, comps.t); comps.eyev = invert(r.direction); comps.normalv = normal_at(comps.object, comps.point); comps.reflectv = reflect(r.direction, comps.normalv) if (dot(comps.normalv, comps.eyev) < 0) { comps.inside = true; comps.normalv = invert(comps.normalv); } comps.over_point = add(comps.point, multiplyInt(comps.normalv, EPSILON)) // p115 // p152 var containers = [] for (var aa = 0; aa < xs.length; aa++) { if (xs[aa].object.id == i.object.id) { if (containers.length == 0) { comps.n1 = 1.0 } else { comps.n1 = containers[containers.length-1].material.refractive_index } } // if containers includes i.object then var flag = -1; for (var bb = 0; bb < containers.length; bb++) { if (containers[bb].id == i.object.id) { flag = bb; } } if (flag > -1) { containers[flag] = null; containers = removeNullEntries(containers) } else { containers.push(i.object) } if (xs[aa].object.id == i.object.id) { if (containers.length == 0) { comps.n2 = 1.0 } else { comps.n2 = containers[containers.length-1].material.refractive_index } a = xs.length } } return comps; }
function removeNullEntries(arr) { var res = []; for (var aa = 0; aa < arr.length; aa++) if (arr[aa] != null) res.push(arr)
return res }
Then the test function, which gives me access to the debugger, to update "index", and then continue execution (the first go around, index = 0)
function test152() { var A = glass_sphere() A.transform = scaling(2, 2, 2) A.material.refractive_index = 1.5 var B = glass_sphere() B.transform = translation(0,0,-0.25) B.material.refractive_index = 2.0 var C = glass_sphere() C.transform = translation(0,0,0.25) C.material.refractive_index = 2.5 var r = new ray(point(0,0,-4), vector(0,0,1)) var iA = new intersection(A, 2), iB = new intersection(B, 2.75), iC = new intersection(C, 3.25); var iB2 = new intersection(B, 4.75), iC2 = new intersection(C, 5.25), iA2 = new intersection(A, 6); var xs = intersections(iA, iB, iC, iB2, iC2, iA2) var exit = 0; var index = 0; while(!exit) { var comps = prepare_computations(xs[index], r, xs) debugger; } }
Finally, here are the values for each index I am getting:
Index | n1 | n2 0 1 1.5 1 2 1 2 1 2.5 3 2 1 4 1 2.5 5 1 2.5
Help would be much appreciated!
[/a]
|
|
|
Post by inventordave on Nov 18, 2019 5:24:24 GMT
After doing some debugging, I had the epiphany to move the creation of containers[] outside of prepare_computations(…) (in global space, for convenience). It partially improved the situation. The results I'm getting now are:
Index n1 n2 0 1 1.5 1 2 1.5 2 1.5 2.5 3 2 2.5 4 2.5 1.5 5 1.5 1.0
At least more of the values are correct. Of course, I need them all to be correct...
|
|
|
Post by Jamis on Nov 18, 2019 15:43:49 GMT
inventordave -- a fairly common reason for this test to fail is when the xs collection does not include every intersection. That is, if you've written your code to exclude intersections where t is negative (behind the eye), you'll see this test fail. It's usually a great optimization (fewer intersections means less work for the renderer to do) but it means certain features won't work right. Can you confirm that the xs collection is not being filtered anywhere to remove negative intersections?
|
|
|
Post by inventordave on Nov 19, 2019 18:27:22 GMT
Unfortunately, that is not what's happening. If you see the test function above, you see it calls intersections(…) to build the array of intersections. The code for this simple function is below. Then, the test function calls prepare_computations(…) which obviously doesn't optimize the collection either.
function intersections() {
var xs = []; for (var i = 0; i < arguments.length; i++) xs.push(arguments[i]); xs = order(xs); // sorts the array, using quicksort algorithm return xs; }
|
|
|
Post by inventordave on Nov 19, 2019 18:58:30 GMT
If it helps, here is my debugging information, following immediately after my (ever-so-slightly modified) prepare_computations(…) function....
var containers = [] function prepare_computations(i, r, xs) { var comps = new compsds(); comps.t = i.t; comps.object = i.object; comps.point = _position(r, comps.t); comps.eyev = invert(r.direction); comps.normalv = normal_at(comps.object, comps.point); comps.reflectv = reflect(r.direction, comps.normalv) if (dot(comps.normalv, comps.eyev) < 0) { comps.inside = true; comps.normalv = invert(comps.normalv); } comps.over_point = add(comps.point, multiplyInt(comps.normalv, EPSILON)) // p115 comps.under_point = subtract(comps.point, multiplyInt(comps.normalv, EPSILON)) // p155 // p152 console.log("prepare_computations() called...\n"); for (var a = 0; a < xs.length; a++) { console.log("Iteration: xs[" + a + "]\n"); if (xs[a].object.id == i.object.id) { if (containers.length == 0) { comps.n1 = 1.0 console.log("\tcontainers.length = 0, n1 = 1.0, index of xs[] = " + a + "\n") } else { comps.n1 = containers[containers.length-1].material.refractive_index console.log("\tn1 calculated. index of xs[] = " + a + "\n"); } } // if containers includes i.object then var flag = -1; var flag2 = 0; for (var b = 0; b < containers.length; b++) { if (containers[b].id == i.object.id) { console.log("\tRemoving object " + i.object.id + "\n"); console.log("\t\tfrom containers[" + b + "]\n"); flag = b; flag2++; containers[flag] = null; containers = removeNullEntries(containers) //b = containers.length } } if (flag2 > 0) { console.log ("\t(" + flag2 + " copies of object " + i.object.id + " removed.)\n"); }
if (flag == -1) { containers.push(i.object) console.log("\tInserting object " + i.object.id + ", container.length = " + containers.length + "\n"); } if (xs[a].object.id == i.object.id) { if (containers.length == 0) { comps.n2 = 1.0 } else { comps.n2 = containers[containers.length-1].material.refractive_index } a = xs.length } } console.log("n1 = " + comps.n1 + ", n2 = " + comps.n2 + "\n"); console.log("------------------------------------\n"); return comps; }
Here is the debugging information it outputs:
prepare_computations() called...
Iteration: xs[0]
container.length = 0, n1 = 1.0, index of xs[] = 0
Inserting object A, containers.length = 1
n1 = 1, n2 = 1.5
------------------------------------
prepare_computations() called...
Iteration: xs[0]
Inserting object B, containers.length = 2
Iteration: xs[1]
n1 calculated. index of xs[] = 1
Removing object B
from containers[1]
(1 copies of object B removed.)
n1 = 2, n2 = 1.5
------------------------------------
prepare_computations() called...
Iteration: xs[0]
Inserting object C, containers.length = 2
Iteration: xs[1]
Removing object C
from containers[1]
(1 copies of object C removed.)
Iteration: xs[2]
n1 calculated. index of xs[] = 2
Inserting object C, containers.length = 2
n1 = 1.5, n2 = 2.5
------------------------------------
prepare_computations() called...
Iteration: xs[0]
Inserting object B, containers.length = 3
Iteration: xs[1]
n1 calculated. index of xs[] = 1
Removing object B
from containers[2]
(1 copies of object B removed.)
n1 = 2, n2 = 2.5
------------------------------------
prepare_computations() called...
Iteration: xs[0]
Removing object C
from containers[1]
(1 copies of object C removed.)
Iteration: xs[1]
Inserting object C, containers.length = 2
Iteration: xs[2]
n1 calculated. index of xs[] = 2
Removing object C
from containers[1]
(1 copies of object C removed.)
n1 = 2.5, n2 = 1.5
------------------------------------
prepare_computations() called...
Iteration: xs[0]
n1 calculated. index of xs[] = 0
Removing object A
from containers[0]
(1 copies of object A removed.)
n1 = 1.5, n2 = 1
------------------------------------
hope this tells you exactly what is happening when the algorithm is being executed....
|
|
|
Post by inventordave on Nov 19, 2019 19:08:32 GMT
Analysing the debug info, I can immediately see a problem. During the 2nd call to prepare_computations(…) (index = 1), it appears that to get the correct result, it needs to not delete B from containers[] immediately after calculating n1, but immediately before calculating n2... I think it should have deleted A.... It should presumably add C immediately before calcing n2, also, I assume...
|
|
|
Post by inventordave on Nov 20, 2019 6:02:55 GMT
Just to wrap up, I have cleaned up the code in (my) prepare_computations(…), to include it does not need to be passed the indice of xs as an argument...
function prepare_computations(i, r, xs) { // 1st part of function, irrelevant... // ... comps.over_point = add(comps.point, multiplyInt(comps.normalv, EPSILON)) // p115 comps.under_point = subtract(comps.point, multiplyInt(comps.normalv, EPSILON)) // p155 // p152 if (xs == undefined || xs.length == 0) xs = [i]
for (var a = xs.indexOf(i); a < xs.length; a++) {
if (xs[a].object.id == i.object.id) if (containers.length == 0) comps.n1 = 1.0 else comps.n1 = containers[containers.length-1].material.refractive_index // if containers includes i.object then var indexOfObj; if ((indexOfObj = containers.indexOf(i.object)) != -1) containers.splice(indexOfObj, 1) else containers.push(i.object) if (xs[a].object.id == i.object.id) { if (containers.length == 0) comps.n2 = 1.0 else comps.n2 = containers[containers.length-1].material.refractive_index break; } }
return comps }
|
|
|
Post by Jamis on Nov 22, 2019 22:47:00 GMT
I'm glad you got it working! However, the for loop in the pseudocode that you mentioned, which loops over the 'xs' collection of intersections, should actually iterate over all of them, from 0. At least, that's what my implementations have done, and it's worked okay. The reason for this is so that the algorithm can determine what object (if any) the ray originated inside, which in turn allows the n1 value to be set appropriately.
|
|
|
Post by inventordave on Nov 22, 2019 22:59:16 GMT
Ok, lol. Perhaps my implementation of the algorithm will fail me at a later point, then...
|
|
|
Post by inventordave on Nov 25, 2019 10:27:25 GMT
Lol, after moving onto the subsequent tests, some failed, some passed. I revisited the prepare_computations() algorithm, and realised my mistake: I was confusing "i" in the algorithm on p153/154 with "i", which is passed as the 1st argument to prepare_computations. I modified it as so, and it appears to be working:
function prepare_computations(i, r, xs) { // ignore earlier part of function comps.over_point = add(comps.point, multiplyInt(comps.normalv, EPSILON)) // p115 comps.under_point = subtract(comps.point, multiplyInt(comps.normalv, EPSILON)) // p155 // p152
var containers = [] for (var a = 0; a < xs.length; a++) { var curr_i = xs[a];
if (curr_i == i) if (containers.length == 0) comps.n1 = 1.0 else comps.n1 = containers[containers.length-1].material.refractive_index // if containers includes i.object then var indexOfObj; if ((indexOfObj = containers.indexOf(curr_i.object)) != -1) containers.splice(indexOfObj, 1) else containers.push(curr_i.object) if (curr_i == i) { if (containers.length == 0) comps.n2 = 1.0 else comps.n2 = containers[containers.length-1].material.refractive_index break; } }
return comps }
Now, the test on p158 basically passes (there is a difference in the "b"/"z" component of the calc'ed color value of < EPSILON from the book's value of 0.04725, my value is 0.047242....). The test on p161 now passes, where it was failing. I just misread, then incorrectly implemented the algorithm. I haven't bothered trying to find out how I made the tests on p152 pass with incorrect code.....
|
|