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