|
Post by Jamis on Mar 13, 2019 2:30:08 GMT
A new bonus chapter is available: Texture Mapping! You will learn how to: - map 2D patterns onto spheres, planes, cylinders, and cubes
- parse PPM files and load them into a canvas
- use a canvas as a data source for 2D patterns, so you can map images onto shapes, and
- build skyboxes (and sky spheres) to provide beautiful photographic environments for your scenes.
Give it a look and tell me what you think! >> Read the bonus chapter at www.raytracerchallenge.com/bonus/texture-mapping.html
|
|
|
Post by sbehnke on Jul 15, 2019 4:12:49 GMT
I’ve started this chapter but got stuck immediately with the Spherical map pseudo code implementation. My first entry with Point 0,0,-1 came back with 0.5,0.5 for u,v. Two other of the test cases were also coming back incorrectly. Not sure what’s wrong. It’s a pretty simple example.
|
|
|
Post by Jamis on Jul 15, 2019 13:41:27 GMT
sbehnke, can you share your implementation, or paste your "spherical_map()" implementation here? I'll take a look and see if anything jumps out at me.
|
|
|
Post by sbehnke on Jul 15, 2019 19:37:51 GMT
sbehnke, can you share your implementation, or paste your "spherical_map()" implementation here? I'll take a look and see if anything jumps out at me. I will certainly post more when I get home, but I may have found some of the issue. I was including W in magnitude and that was causing some of the V values to be off. When I removed that, I’m left with only 3 of the U values that are incorrect. Is that a correct implementation of magnitude to ignore W?
|
|
|
Post by Jamis on Jul 15, 2019 20:17:35 GMT
sbehnke, vectors will (should?) always have w=0, so including them in or omitting them from the magnitude calculation will have no effect. They'll cause problems, though, if you ever try to take the magnitude of a point (which has w=1). However, taking the magnitude of a point would be a bug, because it's not an operation that makes sense. If you're finding yourself being tripped up on `w` being non-zero in the magnitude calculation, there may be a deeper problem. You might want to dig and see if you can figure out why you have a non-zero `w` in the first place.
|
|
|
Post by sbehnke on Jul 15, 2019 20:27:06 GMT
sbehnke, vectors will (should?) always have w=0, so including them in or omitting them from the magnitude calculation will have no effect. They'll cause problems, though, if you ever try to take the magnitude of a point (which has w=1). However, taking the magnitude of a point would be a bug, because it's not an operation that makes sense. If you're finding yourself being tripped up on `w` being non-zero in the magnitude calculation, there may be a deeper problem. You might want to dig and see if you can figure out why you have a non-zero `w` in the first place. Ahhh. Ok. So the sample data for the test Using a spherical mapping on a 3D point where it has a list of points should be interrupted as vectors with a w of 0?
|
|
|
Post by sbehnke on Jul 15, 2019 23:08:29 GMT
|
|
|
Post by sbehnke on Jul 15, 2019 23:17:13 GMT
func testSphereicalMapping() { // Scenario Outline: Using a spherical mapping on a 3D point // Given p ← <point> // When (u, v) ← spherical_map(p) // Then u = <u> // And v = <v> // // Examples: // | point | u | v | // | point(0, 0, -1) | 0.0 | 0.5 | // | point(1, 0, 0) | 0.25 | 0.5 | // | point(0, 0, 1) | 0.5 | 0.5 | // | point(-1, 0, 0) | 0.75 | 0.5 | // | point(0, 1, 0) | 0.5 | 1.0 | // | point(0, -1, 0) | 0.5 | 0.0 | // | point(√2/2, √2/2, 0) | 0.25 | 0.75 | let points: [Tuple] = [.Point(x: 0, y: 0, z: -1), .Point(x: 1, y: 0, z: 0), .Point(x: 0, y: 0, z: 1), .Point(x: -1, y: 0, z: 0), .Point(x: 0, y: 1, z: 0), .Point(x: 0, y: -1, z: 0), .Point(x: sqrt(2)/2, y: sqrt(2)/2, z: 0),] let u: [Double] = [0.0, 0.25, 0.5, 0.75, 0.5, 0.5, 0.25, ] let v: [Double] = [0.5, 0.5, 0.5, 0.5, 1.0, 0.0, 0.75, ] for index in 0..<points.count { let (uOut, vOut) = points[index].sphericalMap() print("Values: \(uOut), \(vOut)") XCTAssertEqual(uOut, u[index], "u does not match for index: \(index)") XCTAssertEqual(vOut, v[index], "v does not match for index: \(index)") } }
XCTAssertEqual failed: ("0.0") is not equal to ("0.5") - u does not match for index: 5 XCTAssertEqual failed: ("0.5") is not equal to ("0.0") - u does not match for index: 0 XCTAssertEqual failed: ("0.375") is not equal to ("0.25") - u does not match for index: 6
Here is the test implementation and the results that do not match.
|
|
|
Post by Jamis on Jul 17, 2019 18:07:24 GMT
Oh, geez. This one is my fault.
What that spherical_map() function ought to be doing is converting the given point p to a vector pointing from the sphere's origin, to the point. Which, since the sphere is centered at the world origin, is just vector(p.x, p.y, p.z). I'll fix the bonus chapter.
Let me know if that change is enough to fix your tests. If not, I'll take another look at your code.
- Jamis
EDIT: I just updated the pseudo-code for the "spherical_map()" function in the bonus chapter.
|
|
|
Post by sbehnke on Jul 17, 2019 19:12:38 GMT
I have made the change and get the same results. Here’s a screenshot from a playground that shows every calculation on the right hand side. Test data says u should be 0.0.
|
|
|
Post by Jamis on Jul 17, 2019 19:28:11 GMT
Alright, I found it. The pseudocode is wrong (again!). The arctan2() function should have z as the second argument, not y: "arctan2(p.x, p.z)".
I'll update the chapter. Sorry for the typos! I swear I went over it all with a fine-toothed comb before publishing it, but I guess the comb wasn't fine enough. :/
- Jamis
|
|
|
Post by sbehnke on Jul 17, 2019 19:46:21 GMT
For the planar mapping I was getting values less than 0 with the remainder function and had to add 1 to get the results to match the expected values if my remainder was less than 0.
Edit: so in swift fmod() is not actually modulo. It’s remainder. I implemented my own modulo and now that works as expected.
|
|
|
Post by sbehnke on Jul 17, 2019 20:35:19 GMT
Alright, I found it. The pseudocode is wrong (again!). The arctan2() function should have z as the second argument, not y: "arctan2(p.x, p.z)". I'll update the chapter. Sorry for the typos! I swear I went over it all with a fine-toothed comb before publishing it, but I guess the comb wasn't fine enough. :/ - Jamis No worries at all! Thanks for that. The test now passes.
|
|
|
Post by sbehnke on Jul 18, 2019 2:16:25 GMT
I found another issue with the chapter:
function cylindrical_map(p) # compute the azimuthal angle, same as with spherical_map() let theta ← arctan2(p.x, p.y) let raw_u ← theta / (2 * π) let u ← 1 - (raw_u + 0.5)
# let v go from 0 to 1 between whole units of y let v ← p.y mod 1
return (u, v) end function
In order for that to pass the unit test, I had to do arctan2(p.x, p.z) not p.x, p.y.
|
|
|
Post by laserbrain on Jul 29, 2019 10:01:25 GMT
Regarding Cube Mapping; isn't the X-direction of the "Up" and "Down" side of the cube reversed in the fold-out image here: www.raytracerchallenge.com/bonus/images/tmap/cube-face-mapping.pngThe U-vector should state " -1 .. x .. 1" (and not the other direction) -- just as the "Front" side. (The two tests "UV mapping the upper face of a cube" and "UV mapping the lower face of a cube" were failing for me, so I doubled and then tripled checked both my tests and my implementations for "cube_uv_up" and "cube_uv_down" and realized the image must be wrong.)
|
|