Skip to content

Commit 0cfe432

Browse files
authored
Merge pull request #518 from RayTracing/move-sphere-tex-coords
Move Texture Coordinates for Spheres from Ch 6 to Ch4 to define (u,v).
2 parents 2f12e6b + 5ee4248 commit 0cfe432

File tree

2 files changed

+73
-67
lines changed

2 files changed

+73
-67
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Change Log -- Ray Tracing in One Weekend
66
### _In One Weekend_
77
- Change: The C++ `<random>` version of `random_double()` no longer depends on `<functional>` header
88

9+
### _The Next Week_
10+
- Fix: Introduce "Texture Coordinates for Spheres" in Chapter 4 to support (u,v) coordinates in
11+
`hit_record` (#496)
12+
913

1014
----------------------------------------------------------------------------------------------------
1115
# v3.1.0 (2020-05-03)

books/RayTracingTheNextWeek.html

Lines changed: 69 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,71 @@
957957
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
958958
[Listing [hit-record-uv]: <kbd>[hittable.h]</kbd> Adding U,V coordinates to the `hit_record`]
959959

960+
We will also need to compute $(u,v)$ texture coordinates for hittables.
961+
962+
963+
Texture Coordinates for Spheres
964+
--------------------------------
965+
<div class='together'>
966+
For spheres, this is usually based on some form of longitude and latitude, _i.e._, spherical
967+
coordinates. So if we have a $(\theta,\phi)$ in spherical coordinates, we just need to scale
968+
$\theta$ and $\phi$ to fractions. If $\theta$ is the angle down from the pole, and $\phi$ is the
969+
angle around the axis through the poles, the normalization to $[0,1]$ would be:
970+
971+
$$ u = \frac{\phi}{2\pi} $$
972+
$$ v = \frac{\theta}{\pi} $$
973+
</div>
974+
975+
<div class='together'>
976+
To compute $\theta$ and $\phi$, for a given hitpoint, the formula for spherical coordinates of a
977+
unit radius sphere on the origin is:
978+
979+
$$ x = \cos(\phi) \cos(\theta) $$
980+
$$ y = \sin(\phi) \cos(\theta) $$
981+
$$ z = \sin(\theta) $$
982+
</div>
983+
984+
<div class='together'>
985+
We need to invert that. Because of the lovely `<cmath>` function `atan2()` which takes any number
986+
proportional to sine and cosine and returns the angle, we can pass in $x$ and $y$ (the
987+
$\cos(\theta)$ cancel):
988+
989+
$$ \phi = \text{atan2}(y, x) $$
990+
</div>
991+
992+
<div class='together'>
993+
The $atan2$ returns values in the range $-\pi$ to $\pi$, so we need to take a little care there.
994+
The $\theta$ is more straightforward:
995+
996+
$$ \theta = \text{asin}(z) $$
997+
998+
which returns numbers in the range $-\pi/2$ to $\pi/2$.
999+
</div>
1000+
1001+
<div class='together'>
1002+
So for a sphere, the $(u,v)$ coord computation is accomplished by a utility function that expects
1003+
things on the unit sphere centered at the origin. The call inside sphere::hit should be:
1004+
1005+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
1006+
get_sphere_uv((rec.p-center)/radius, rec.u, rec.v);
1007+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1008+
[Listing [get-sphere-uv-call]: <kbd>[sphere.h]</kbd> Sphere UV coordinates from hit]
1009+
</div>
1010+
1011+
<div class='together'>
1012+
The utility function is:
1013+
1014+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
1015+
void get_sphere_uv(const vec3& p, double& u, double& v) {
1016+
auto phi = atan2(p.z(), p.x());
1017+
auto theta = asin(p.y());
1018+
u = 1-(phi + pi) / (2*pi);
1019+
v = (theta + pi/2) / pi;
1020+
}
1021+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1022+
[Listing [get-sphere-uv]: <kbd>[sphere.h]</kbd> get_sphere_uv function]
1023+
</div>
1024+
9601025
<div class='together'>
9611026
Now we can make textured materials by replacing the `const color& a` with a texture pointer:
9621027

@@ -1604,8 +1669,9 @@
16041669
Image Texture Mapping
16051670
====================================================================================================
16061671

1607-
We used the hitpoint p before to index a procedure solid texture like marble. We can also read in an
1608-
image and use a 2D $(u,v)$ texture coordinate to index into the image.
1672+
From the hitpoint $\mathbf{P}$, we compute the surface coordinates $(u,v)$. We then use these to
1673+
index into our procedural solid texture (like marble). We can also read in an image and use the
1674+
2D $(u,v)$ texture coordinate to index into the image.
16091675

16101676
<div class='together'>
16111677
A direct way to use scaled $(u,v)$ in an image is to round the $u$ and $v$ to integers, and use that
@@ -1619,71 +1685,7 @@
16191685
$$ v = \frac{j}{N_y-1} $$
16201686
</div>
16211687

1622-
<div class='together'>
1623-
This is just a fractional position. For a hittable, we need to also return a $u$ and $v$ in the hit
1624-
record.
1625-
1626-
1627-
Texture Coordinates for Spheres
1628-
--------------------------------
1629-
For spheres, this is usually based on some form of longitude and latitude, _i.e._, spherical
1630-
coordinates. So if we have a $(\theta,\phi)$ in spherical coordinates, we just need to scale
1631-
$\theta$ and $\phi$ to fractions. If $\theta$ is the angle down from the pole, and $\phi$ is the
1632-
angle around the axis through the poles, the normalization to $[0,1]$ would be:
1633-
1634-
$$ u = \frac{\phi}{2\pi} $$
1635-
$$ v = \frac{\theta}{\pi} $$
1636-
</div>
1637-
1638-
<div class='together'>
1639-
To compute $\theta$ and $\phi$, for a given hitpoint, the formula for spherical coordinates of a
1640-
unit radius sphere on the origin is:
1641-
1642-
$$ x = \cos(\phi) \cos(\theta) $$
1643-
$$ y = \sin(\phi) \cos(\theta) $$
1644-
$$ z = \sin(\theta) $$
1645-
</div>
1646-
1647-
<div class='together'>
1648-
We need to invert that. Because of the lovely `<cmath>` function `atan2()` (which takes any number
1649-
proportional to sine and cosine and returns the angle), we can pass in $x$ and $y$ (the
1650-
$\cos(\theta)$ cancel):
1651-
1652-
$$ \phi = \text{atan2}(y, x) $$
1653-
</div>
1654-
1655-
<div class='together'>
1656-
The $atan2$ returns values in the range $-\pi$ to $\pi$, so we need to take a little care there.
1657-
The $\theta$ is more straightforward:
1658-
1659-
$$ \theta = \text{asin}(z) $$
1660-
1661-
which returns numbers in the range $-\pi/2$ to $\pi/2$.
1662-
</div>
1663-
1664-
<div class='together'>
1665-
So for a sphere, the $(u,v)$ coord computation is accomplished by a utility function that expects
1666-
things on the unit sphere centered at the origin. The call inside sphere::hit should be:
1667-
1668-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
1669-
get_sphere_uv((rec.p-center)/radius, rec.u, rec.v);
1670-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1671-
[Listing [get-sphere-uv-call]: <kbd>[sphere.h]</kbd> Sphere UV coordinates from hit]
1672-
</div>
1673-
1674-
<div class='together'>
1675-
The utility function is:
1676-
1677-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
1678-
void get_sphere_uv(const vec3& p, double& u, double& v) {
1679-
auto phi = atan2(p.z(), p.x());
1680-
auto theta = asin(p.y());
1681-
u = 1-(phi + pi) / (2*pi);
1682-
v = (theta + pi/2) / pi;
1683-
}
1684-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1685-
[Listing [get-sphere-uv]: <kbd>[sphere.h]</kbd> get_sphere_uv function]
1686-
</div>
1688+
This is just a fractional position.
16871689

16881690

16891691
Storing Texture Image Data

0 commit comments

Comments
 (0)