|
1137 | 1137 | Texture Coordinates for Spheres
|
1138 | 1138 | --------------------------------
|
1139 | 1139 | <div class='together'>
|
1140 |
| -For spheres, this is usually based on some form of longitude and latitude, _i.e._, spherical |
1141 |
| -coordinates. So if we have a $(\theta,\phi)$ in spherical coordinates, we just need to scale |
1142 |
| -$\theta$ and $\phi$ to fractions. If $\theta$ is the angle down from the pole, and $\phi$ is the |
1143 |
| -angle around the axis through the poles, the normalization to $[0,1]$ would be: |
| 1140 | +For spheres, texture coordinates are usually based on some form of longitude and latitude, _i.e._, |
| 1141 | +spherical coordinates. So we compute $(\theta,\phi)$ in spherical coordinates, where $\theta$ is the |
| 1142 | +angle up from the bottom pole (that is, up from -Y), and $\phi$ is the angle around the Y-axis (from |
| 1143 | +-X to +Z to +X to -Z back to -X). |
| 1144 | + |
| 1145 | +We want to map $\theta$ and $\phi$ to texture coordinates $u$ and $v$ each in $[0,1]$, where |
| 1146 | +$(u=0,v=0)$ maps to the bottom-left corner of the texture. Thus the normalization from |
| 1147 | +$(\theta,\phi)$ to $(u,v)$ would be: |
1144 | 1148 |
|
1145 | 1149 | $$ u = \frac{\phi}{2\pi} $$
|
1146 | 1150 | $$ v = \frac{\theta}{\pi} $$
|
1147 | 1151 | </div>
|
1148 | 1152 |
|
1149 | 1153 | <div class='together'>
|
1150 |
| -To compute $\theta$ and $\phi$, for a given hitpoint, the formula for spherical coordinates of a |
1151 |
| -unit radius sphere on the origin is: |
| 1154 | +To compute $\theta$ and $\phi$ for a given point on the unit sphere centered at the origin, we start |
| 1155 | +with the equations for the corresponding Cartesian coordinates: |
1152 | 1156 |
|
1153 |
| - $$ x = \cos(\phi) \cos(\theta) $$ |
1154 |
| - $$ y = \sin(\phi) \cos(\theta) $$ |
1155 |
| - $$ z = \sin(\theta) $$ |
| 1157 | + $$ \begin{align*} |
| 1158 | + y &= -\cos(\theta) \\ |
| 1159 | + x &= -\cos(\phi) \sin(\theta) \\ |
| 1160 | + z &= \quad\sin(\phi) \sin(\theta) |
| 1161 | + \end{align*} |
| 1162 | + $$ |
1156 | 1163 | </div>
|
1157 | 1164 |
|
1158 | 1165 | <div class='together'>
|
1159 |
| -We need to invert that. Because of the lovely `<cmath>` function `atan2()` which takes any number |
1160 |
| -proportional to sine and cosine and returns the angle, we can pass in $x$ and $y$ (the |
1161 |
| -$\cos(\theta)$ cancel): |
| 1166 | +We need to invert these equations to solve for $\theta$ and $\phi$. Because of the lovely `<cmath>` |
| 1167 | +function `atan2()`, which takes any pair of numbers proportional to sine and cosine and returns the |
| 1168 | +angle, we can pass in $x$ and $z$ (the $\sin(\theta)$ cancel) to solve for $\phi$: |
1162 | 1169 |
|
1163 |
| - $$ \phi = \text{atan2}(y, x) $$ |
| 1170 | + $$ \phi = \text{atan2}(z, -x) $$ |
1164 | 1171 | </div>
|
1165 | 1172 |
|
1166 | 1173 | <div class='together'>
|
1167 |
| -The $atan2$ returns values in the range $-\pi$ to $\pi$, so we need to take a little care there. |
1168 |
| -The $\theta$ is more straightforward: |
| 1174 | +`atan2()` returns values in the range $-\pi$ to $\pi$, but they go from 0 to $\pi$, then flip to |
| 1175 | +$-\pi$ and proceed back to zero. While this is mathematically correct, we want $u$ to range from $0$ |
| 1176 | +to $1$, not from $0$ to $1/2$ and then from $-1/2$ to $0$. Fortunately, |
| 1177 | + |
| 1178 | + $$ \text{atan2}(a,b) = \text{atan2}(-a,-b) + \pi, $$ |
| 1179 | + |
| 1180 | +and the second formulation yields values from $0$ continuously to $2\pi$. Thus, we can compute |
| 1181 | +$\phi$ as |
| 1182 | + |
| 1183 | + $$ \phi = \text{atan2}(-z, x) + \pi $$ |
| 1184 | +</div> |
1169 | 1185 |
|
1170 |
| - $$ \theta = \text{asin}(z) $$ |
| 1186 | +<div> |
| 1187 | +The derivation for $\theta$ is more straightforward: |
1171 | 1188 |
|
1172 |
| -which returns numbers in the range $-\pi/2$ to $\pi/2$. |
| 1189 | + $$ \theta = \text{acos}(-y) $$ |
1173 | 1190 | </div>
|
1174 | 1191 |
|
1175 | 1192 | <div class='together'>
|
1176 |
| -So for a sphere, the $(u,v)$ coord computation is accomplished by a utility function that expects |
1177 |
| -things on the unit sphere centered at the origin: |
| 1193 | +So for a sphere, the $(u,v)$ coord computation is accomplished by a utility function that takes |
| 1194 | +points on the unit sphere centered at the origin, and computes $u$ and $v$: |
1178 | 1195 |
|
1179 | 1196 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
1180 |
| - void get_sphere_uv(const vec3& p, double& u, double& v) { |
1181 |
| - auto phi = atan2(p.z(), p.x()); |
1182 |
| - auto theta = asin(p.y()); |
1183 |
| - u = 1-(phi + pi) / (2*pi); |
1184 |
| - v = (theta + pi/2) / pi; |
1185 |
| - } |
| 1197 | + class sphere : public hittable { |
| 1198 | + ... |
| 1199 | + private: |
| 1200 | + static void get_sphere_uv(const point3& p, double& u, double& v) { |
| 1201 | + // p: a given point on the sphere of radius one, centered at the origin. |
| 1202 | + // u: returned value [0,1] of angle around the Y axis from X=-1. |
| 1203 | + // v: returned value [0,1] of angle from Y=-1 to Y=+1. |
| 1204 | + // <1 0 0> yields <0.50 0.50> <-1 0 0> yields <0.00 0.50> |
| 1205 | + // <0 1 0> yields <0.50 1.00> < 0 -1 0> yields <0.50 0.00> |
| 1206 | + // <0 0 1> yields <0.25 0.50> < 0 0 -1> yields <0.75 0.50> |
| 1207 | + |
| 1208 | + auto theta = acos(-p.y()); |
| 1209 | + auto phi = atan2(-p.z(), p.x()) + pi; |
| 1210 | + |
| 1211 | + u = phi / (2*pi); |
| 1212 | + v = theta / pi; |
| 1213 | + } |
| 1214 | + }; |
1186 | 1215 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
1187 | 1216 | [Listing [get-sphere-uv]: <kbd>[sphere.h]</kbd> get_sphere_uv function]
|
1188 | 1217 | </div>
|
|
0 commit comments