pbrt_r3/core/texture/
mapping2d.rs

1use crate::core::base::*;
2use crate::core::error::*;
3use crate::core::geometry::*;
4use crate::core::interaction::*;
5use crate::core::param_set::*;
6use crate::core::transform::*;
7
8pub trait TextureMapping2D {
9    fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f);
10}
11
12pub struct UVMapping2D {
13    pub su: Float,
14    pub sv: Float,
15    pub du: Float,
16    pub dv: Float,
17}
18
19impl UVMapping2D {
20    pub fn new(su: Float, sv: Float, du: Float, dv: Float) -> Self {
21        UVMapping2D { su, sv, du, dv }
22    }
23}
24
25impl TextureMapping2D for UVMapping2D {
26    fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f) {
27        // Compute texture differentials for 2D identity mapping
28        let dstdx = Vector2f::new(self.su * si.dudx, self.sv * si.dvdx);
29        let dstdy = Vector2f::new(self.su * si.dudy, self.sv * si.dvdy);
30        let st = Point2f::new(self.su * si.uv[0] + self.du, self.sv * si.uv[1] + self.dv);
31        return (st, dstdx, dstdy);
32    }
33}
34
35pub struct SphericalMapping2D {
36    world_to_texture: Transform,
37}
38
39impl SphericalMapping2D {
40    pub fn new(world_to_texture: &Transform) -> Self {
41        SphericalMapping2D {
42            world_to_texture: *world_to_texture,
43        }
44    }
45    fn sphere(&self, p: &Point3f) -> Point2f {
46        let vec =
47            (self.world_to_texture.transform_point(p) - Point3f::new(0.0, 0.0, 0.0)).normalize();
48        let theta = spherical_theta(&vec);
49        let phi = spherical_phi(&vec);
50        return Point2f::new(theta * INV_PI, phi * INV_2_PI);
51    }
52}
53
54impl TextureMapping2D for SphericalMapping2D {
55    fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f) {
56        let st = self.sphere(&si.p);
57        // Compute texture coordinate differentials for sphere $(u,v)$ mapping
58        const DELTA: Float = 0.1;
59        let st_delta_x = self.sphere(&(si.p + DELTA * si.dpdx));
60        let mut dstdx = (st_delta_x - st) * (1.0 / DELTA);
61        let st_delta_y = self.sphere(&(si.p + DELTA * si.dpdy));
62        let mut dstdy = (st_delta_y - st) * (1.0 / DELTA);
63
64        // Handle sphere mapping discontinuity for coordinate differentials
65        if dstdx[1] > 0.5 {
66            dstdx[1] = 1.0 - dstdx[1];
67        } else if dstdx[1] < -0.5 {
68            dstdx[1] = -(dstdx[1] + 1.0);
69        }
70
71        if dstdy[1] > 0.5 {
72            dstdy[1] = 1.0 - dstdy[1];
73        } else if dstdy[1] < -0.5 {
74            dstdy[1] = -(dstdy[1] + 1.0);
75        }
76
77        return (st, dstdx, dstdy);
78    }
79}
80
81pub struct CylindricalMapping2D {
82    world_to_texture: Transform,
83}
84
85impl CylindricalMapping2D {
86    pub fn new(world_to_texture: &Transform) -> Self {
87        CylindricalMapping2D {
88            world_to_texture: *world_to_texture,
89        }
90    }
91    fn cylinder(&self, p: &Point3f) -> Point2f {
92        let vec =
93            (self.world_to_texture.transform_point(p) - Point3f::new(0.0, 0.0, 0.0)).normalize();
94        return Point2f::new((PI + Float::atan2(vec.y, vec.x)) * INV_2_PI, vec.z);
95    }
96}
97
98impl TextureMapping2D for CylindricalMapping2D {
99    fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f) {
100        let st = self.cylinder(&si.p);
101        // Compute texture coordinate differentials for sphere $(u,v)$ mapping
102        const DELTA: Float = 0.1;
103        let st_delta_x = self.cylinder(&(si.p + DELTA * si.dpdx));
104        let mut dstdx = (st_delta_x - st) * (1.0 / DELTA);
105        let st_delta_y = self.cylinder(&(si.p + DELTA * si.dpdy));
106        let mut dstdy = (st_delta_y - st) * (1.0 / DELTA);
107
108        // Handle sphere mapping discontinuity for coordinate differentials
109        if dstdx[1] > 0.5 {
110            dstdx[1] = 1.0 - dstdx[1];
111        } else if dstdx[1] < -0.5 {
112            dstdx[1] = -(dstdx[1] + 1.0);
113        }
114
115        if dstdy[1] > 0.5 {
116            dstdy[1] = 1.0 - dstdy[1];
117        } else if dstdy[1] < -0.5 {
118            dstdy[1] = -(dstdy[1] + 1.0);
119        }
120
121        return (st, dstdx, dstdy);
122    }
123}
124
125pub struct PlanarMapping2D {
126    vs: Vector3f,
127    vt: Vector3f,
128    ds: Float,
129    dt: Float,
130}
131
132impl PlanarMapping2D {
133    pub fn new(vs: &Vector3f, vt: &Vector3f, ds: Float, dt: Float) -> Self {
134        PlanarMapping2D {
135            vs: *vs,
136            vt: *vt,
137            ds,
138            dt,
139        }
140    }
141}
142
143impl TextureMapping2D for PlanarMapping2D {
144    fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f) {
145        let vec = si.p;
146
147        let st = Vector2f::new(
148            self.ds + Vector3f::dot(&vec, &self.vs),
149            self.dt + Vector3f::dot(&vec, &self.vt),
150        );
151
152        let dstdx = Vector2f::new(
153            Vector3f::dot(&si.dpdx, &self.vs),
154            Vector3f::dot(&si.dpdx, &self.vt),
155        );
156
157        let dstdy = Vector2f::new(
158            Vector3f::dot(&si.dpdy, &self.vs),
159            Vector3f::dot(&si.dpdy, &self.vt),
160        );
161
162        return (st, dstdx, dstdy);
163    }
164}
165
166pub fn create_texture_mapping2d(
167    tex2world: &Transform,
168    tp: &TextureParams,
169) -> Result<Box<dyn TextureMapping2D>, PbrtError> {
170    let mapping = tp.find_string("mapping", "uv");
171    match mapping.as_ref() {
172        "uv" => {
173            let su = tp.find_float("uscale", 1.0);
174            let sv = tp.find_float("vscale", 1.0);
175            let du = tp.find_float("udelta", 0.0);
176            let dv = tp.find_float("vdelta", 0.0);
177            return Ok(Box::new(UVMapping2D::new(su, sv, du, dv)));
178        }
179        "spherical" => {
180            let it = tex2world.inverse();
181            return Ok(Box::new(SphericalMapping2D::new(&it)));
182        }
183        "cylindrical" => {
184            let it = tex2world.inverse();
185            return Ok(Box::new(CylindricalMapping2D::new(&it)));
186        }
187        "planar" => {
188            let v1 = tp.find_vector3f("v1", &Vector3f::new(1.0, 0.0, 0.0));
189            let v2 = tp.find_vector3f("v2", &Vector3f::new(0.0, 1.0, 0.0));
190            let du = tp.find_float("udelta", 0.0);
191            let dv = tp.find_float("vdelta", 0.0);
192            return Ok(Box::new(PlanarMapping2D::new(&v1, &v2, du, dv)));
193        }
194        _ => {
195            let msg = format!("2D texture mapping \"{}\" unknown", mapping);
196            return Err(PbrtError::error(&msg));
197        }
198    }
199}