Commit b2f9d492c13cbedae9229096d9ca703225cf3fe8
1 parent
fb45f6cc
first very simple shading
Add a very simple shader with a direct light source. The shader modifies the color of a polygon just by the angle between the face normal and the light source. This has no physical resemblance at all but let me check if the normal calculation works so far...
Showing
5 changed files
with
184 additions
and
57 deletions
fractional/notes/math.url
0 → 100644
1 | +https://www.themathpage.com/Alg/reciprocals.htm | ... | ... |
... | ... | @@ -11,3 +11,5 @@ http://www.lysator.liu.se/~mikaelk/doc/perspectivetexture/ |
11 | 11 | |
12 | 12 | # Shader... This also describes z-Buffer... :) |
13 | 13 | https://people.ece.cornell.edu/land/OldStudentProjects/cs490-95to96/GUO/report.html |
14 | +https://software.intel.com/en-us/articles/the-basics-of-the-art-of-lighting-part-3-lighting-and-shaders/ | |
15 | +https://docs.unity3d.com/Manual/Lighting.html | ... | ... |
... | ... | @@ -27,18 +27,27 @@ use crate::transform::TMatrix; |
27 | 27 | use crate::trigonometry::Trig; |
28 | 28 | use crate::vector::Vector; |
29 | 29 | |
30 | +#[derive(Debug, Clone)] | |
31 | +pub struct Face<T> | |
32 | +where T: Add + Sub + Neg + Mul + Div + Copy + Trig { | |
33 | + corners :Vec<usize>, | |
34 | + normal :Option<Vector<T>>, | |
35 | +} | |
36 | + | |
30 | 37 | #[derive(Debug)] |
31 | 38 | pub struct Polyeder<T> |
32 | -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig { | |
33 | - points :Vec<Vector<T>>, | |
34 | - faces :Vec<Vec<usize>>, | |
35 | - normals :Vec<Vector<T>>, | |
39 | +where T: Add + Sub + Neg + Mul + Div + Copy + Trig { | |
40 | + points :Vec<Vector<T>>, | |
41 | + faces :Vec<Face<T>>, | |
36 | 42 | } |
37 | 43 | |
38 | 44 | pub trait Primitives<T> |
39 | 45 | where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> { |
40 | 46 | fn transform(&self, m :&TMatrix<T>) -> Self; |
41 | - fn project(&self, camera :&Camera<T>) -> Vec<Polygon>; | |
47 | + fn project( &self | |
48 | + , camera :&Camera<T> | |
49 | + , light :&DirectLight<T> | |
50 | + , col :u32 ) -> Vec<(Polygon, u32)>; | |
42 | 51 | } |
43 | 52 | |
44 | 53 | pub struct Camera<T> |
... | ... | @@ -48,6 +57,11 @@ where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> { |
48 | 57 | project :TMatrix<T>, |
49 | 58 | } |
50 | 59 | |
60 | +pub struct DirectLight<T> | |
61 | +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> { | |
62 | + direction: Vector<T>, | |
63 | +} | |
64 | + | |
51 | 65 | impl<T> Camera<T> |
52 | 66 | where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> |
53 | 67 | + Mul<Output = T> + Div<Output = T> |
... | ... | @@ -83,10 +97,46 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> |
83 | 97 | } |
84 | 98 | } |
85 | 99 | |
100 | +impl<T> DirectLight<T> | |
101 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
102 | + + Mul<Output = T> + Div<Output = T> | |
103 | + + Debug + Copy + Trig + From<i32> { | |
104 | + pub fn new(v :Vector<T>) -> Self { | |
105 | + DirectLight{ direction: v } | |
106 | + } | |
107 | + | |
108 | + pub fn dir(&self) -> Vector<T> { | |
109 | + self.direction | |
110 | + } | |
111 | +} | |
112 | + | |
113 | +impl<T> Face<T> | |
114 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
115 | + + Mul<Output = T> + Div<Output = T> | |
116 | + + Debug + Copy + Trig + From<i32> { | |
117 | + fn new(corners :Vec<usize>, ps :&[Vector<T>]) -> Self { | |
118 | + let mut f = Face{ corners: corners, normal: None }; | |
119 | + f.update_normal(ps); | |
120 | + f | |
121 | + } | |
122 | + | |
123 | + fn update_normal(&mut self, ps :&[Vector<T>]) { | |
124 | + let edge10 = ps[self.corners[1]] - ps[self.corners[0]]; | |
125 | + let edge12 = ps[self.corners[1]] - ps[self.corners[2]]; | |
126 | + self.normal = Some(edge10 * edge12); | |
127 | + } | |
128 | +} | |
129 | + | |
86 | 130 | impl<T> Polyeder<T> |
87 | 131 | where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> |
88 | 132 | + Mul<Output = T> + Div<Output = T> |
89 | 133 | + Debug + Copy + Trig + From<i32> { |
134 | + fn update_normals(&mut self) { | |
135 | + for f in self.faces.iter_mut() { | |
136 | + f.update_normal(&self.points); | |
137 | + } | |
138 | + } | |
139 | + | |
90 | 140 | // https://rechneronline.de/pi/tetrahedron.php |
91 | 141 | pub fn tetrahedron(a :T) -> Polyeder<T> { |
92 | 142 | let f0 :T = 0.into(); |
... | ... | @@ -106,49 +156,86 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> |
106 | 156 | // half the deeps in z |
107 | 157 | let _zh :T = T::sqrt(f3).unwrap() / f4 * a; |
108 | 158 | |
109 | - Polyeder{ points: vec!( Vector( f0, yc, f0) | |
110 | - , Vector(-ah, -yi, -zi) | |
111 | - , Vector( ah, -yi, -zi) | |
112 | - , Vector( f0, -yi, zc) ) | |
113 | - , faces: vec!( vec!(1, 2, 3) | |
114 | - , vec!(1, 0, 2) | |
115 | - , vec!(3, 0, 1) | |
116 | - , vec!(2, 0, 3) )} | |
159 | + let ps = vec!( Vector( f0, yc, f0) | |
160 | + , Vector(-ah, -yi, -zi) | |
161 | + , Vector( ah, -yi, -zi) | |
162 | + , Vector( f0, -yi, zc) ); | |
163 | + | |
164 | + let fs = vec!( Face::new(vec!(1, 2, 3), &ps) | |
165 | + , Face::new(vec!(1, 0, 2), &ps) | |
166 | + , Face::new(vec!(3, 0, 1), &ps) | |
167 | + , Face::new(vec!(2, 0, 3), &ps) ); | |
168 | + | |
169 | + Polyeder{ points: ps, faces: fs } | |
170 | + } | |
171 | + | |
172 | + pub fn triangle(a :T) -> Polyeder<T> { | |
173 | + let f0 :T = 0.into(); | |
174 | + let f3 :T = 3.into(); | |
175 | + let f6 :T = 6.into(); | |
176 | + let zi :T = T::sqrt(f3).unwrap() / f6 * a; | |
177 | + let zc :T = T::sqrt(f3).unwrap() / f3 * a; | |
178 | + let ah :T = a / 2.into(); | |
179 | + | |
180 | + let ps = vec!( Vector(-ah, f0, -zi) | |
181 | + , Vector( f0, f0, zc) | |
182 | + , Vector( ah, f0, -zi) ); | |
183 | + | |
184 | + let fs = vec!(Face::new(vec!(0, 1, 2), &ps)); | |
185 | + | |
186 | + println!("== {:?}", fs); | |
187 | + | |
188 | + Polyeder{ points: ps, faces: fs } | |
117 | 189 | } |
118 | 190 | |
119 | 191 | pub fn cube(a :T) -> Polyeder<T> { |
120 | 192 | let ah :T = a / From::<i32>::from(2); |
121 | 193 | |
122 | - Polyeder{ points: vec!( Vector(-ah, ah, -ah) // 0 => front 1 | |
123 | - , Vector(-ah, -ah, -ah) // 1 => front 2 | |
124 | - , Vector( ah, -ah, -ah) // 2 => front 3 | |
125 | - , Vector( ah, ah, -ah) // 3 => front 4 | |
126 | - , Vector(-ah, ah, ah) // 4 => back 1 | |
127 | - , Vector(-ah, -ah, ah) // 5 => back 2 | |
128 | - , Vector( ah, -ah, ah) // 6 => back 3 | |
129 | - , Vector( ah, ah, ah) ) // 7 => back 4 | |
130 | - , faces: vec!( vec!(0, 1, 2, 3) // front | |
131 | - , vec!(7, 6, 5, 4) // back | |
132 | - , vec!(1, 5, 6, 2) // top | |
133 | - , vec!(0, 3, 7, 4) // bottom | |
134 | - , vec!(0, 4, 5, 1) // left | |
135 | - , vec!(2, 6, 7, 3) )} // right | |
194 | + let ps = vec!( Vector(-ah, ah, -ah) // 0 => front 1 | |
195 | + , Vector(-ah, -ah, -ah) // 1 => front 2 | |
196 | + , Vector( ah, -ah, -ah) // 2 => front 3 | |
197 | + , Vector( ah, ah, -ah) // 3 => front 4 | |
198 | + , Vector(-ah, ah, ah) // 4 => back 1 | |
199 | + , Vector(-ah, -ah, ah) // 5 => back 2 | |
200 | + , Vector( ah, -ah, ah) // 6 => back 3 | |
201 | + , Vector( ah, ah, ah) ); // 7 => back 4 | |
202 | + | |
203 | + let fs = vec!( Face::new(vec!(0, 1, 2, 3), &ps) // front | |
204 | + , Face::new(vec!(7, 6, 5, 4), &ps) // back | |
205 | + , Face::new(vec!(1, 5, 6, 2), &ps) // top | |
206 | + , Face::new(vec!(0, 3, 7, 4), &ps) // bottom | |
207 | + , Face::new(vec!(0, 4, 5, 1), &ps) // left | |
208 | + , Face::new(vec!(2, 6, 7, 3), &ps) ); // right | |
209 | + | |
210 | + Polyeder{ points: ps, faces: fs } | |
136 | 211 | } |
137 | 212 | } |
138 | 213 | |
139 | 214 | impl<T> Primitives<T> for Polyeder<T> |
140 | 215 | where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> |
141 | 216 | + Mul<Output = T> + Div<Output = T> |
142 | - + Debug + Copy + Trig + From<i32> + From<i32> { | |
217 | + + Debug + Copy + Trig + From<i32> + PartialOrd { | |
143 | 218 | fn transform(&self, m :&TMatrix<T>) -> Self { |
144 | - Polyeder{ points: self.points.iter().map(|p| m.apply(p)).collect() | |
145 | - , faces: self.faces.to_vec() } | |
219 | + let Polyeder{ points: ps, faces: fs } = self; | |
220 | + | |
221 | + let mut p = Polyeder{ points: ps.iter().map(|p| m.apply(p)).collect() | |
222 | + , faces: fs.to_vec() }; | |
223 | + | |
224 | + // TODO alternatively we could rotate the normals too, but this cannot | |
225 | + // done with the original matrix… the question is, what is faster. | |
226 | + p.update_normals(); | |
227 | + p | |
146 | 228 | } |
147 | 229 | |
148 | 230 | // TODO for now we assume already prejected vertices (points) |
149 | 231 | // in future we need to distinguish more clear between vertex and point |
150 | 232 | // and projected_point. |
151 | - fn project(&self, _camera :&Camera<T>) -> Vec<Polygon> { | |
233 | + fn project( &self | |
234 | + , camera :&Camera<T> | |
235 | + , light :&DirectLight<T> | |
236 | + , color :u32 ) -> Vec<(Polygon, u32)> { | |
237 | + // Helper to create a Polygon from Coordinates… | |
238 | + // TODO probably there needs to be a Polygon constructor for this. | |
152 | 239 | fn polygon<I>(c :I) -> Polygon |
153 | 240 | where I: Iterator<Item = Coordinate> { |
154 | 241 | Polygon(Coordinates(c.collect())) |
... | ... | @@ -156,12 +243,34 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> |
156 | 243 | |
157 | 244 | // this one does the projection... as the projection was the last |
158 | 245 | // matrix we do not need to do it here. |
159 | - // let to_coord = |p :&usize| _camera.project(self.points[*p]); | |
160 | - let to_coord = |p :&usize| { | |
161 | - let v = self.points[*p]; | |
162 | - Coordinate(v.x().round(), v.y().round()) }; | |
163 | - let to_poly = |f :&Vec<usize>| polygon(f.iter().map(to_coord)); | |
246 | + let to_coord = |p :&usize| camera.project(self.points[*p]); | |
247 | + let to_poly = |f :&Face<T>| { | |
248 | + let pg = polygon(f.corners.iter().map(to_coord)); | |
249 | + let mut r :T = (((color >> 16) & 0xFF) as i32).into(); | |
250 | + let mut g :T = (((color >> 8) & 0xFF) as i32).into(); | |
251 | + let mut b :T = (((color ) & 0xFF) as i32).into(); | |
252 | + let lf :T = match f.normal { | |
253 | + None => 1.into(), | |
254 | + Some(n) => n.dot(light.dir()) | |
255 | + / (n.mag() * light.dir().mag()), | |
256 | + }; | |
257 | + | |
258 | + if lf < 0.into() { | |
259 | + r = r * -lf; | |
260 | + g = g * -lf; | |
261 | + b = b * -lf; | |
262 | + | |
263 | + let c :u32 = (r.round() as u32) << 16 | |
264 | + | (g.round() as u32) << 8 | |
265 | + | (b.round() as u32); | |
266 | + | |
267 | + Some((pg, c)) | |
268 | + } else { | |
269 | + None | |
270 | + }}; | |
164 | 271 | |
165 | - self.faces.iter().map(to_poly).collect() | |
272 | + let mut ps :Vec<(Polygon, u32)> = self.faces.iter().filter_map(to_poly).collect(); | |
273 | + ps.sort_by(|a, b| a.1.cmp(&b.1)); | |
274 | + ps | |
166 | 275 | } |
167 | 276 | } | ... | ... |
... | ... | @@ -39,7 +39,7 @@ use fractional::transform::{TMatrix, translate, rotate_x, rotate_y, rotate_z, ro |
39 | 39 | use fractional::xcb::XcbEasel; |
40 | 40 | use fractional::easel::Canvas; |
41 | 41 | |
42 | -use fractional::geometry::{Camera,Polyeder,Primitives}; | |
42 | +use fractional::geometry::{Camera,DirectLight,Polyeder,Primitives}; | |
43 | 43 | |
44 | 44 | fn mean(v: &Vec<i64>) -> Result<Fractional, TryFromIntError> { |
45 | 45 | let r = v.iter().fold(0, |acc, x| acc + x); |
... | ... | @@ -162,16 +162,16 @@ fn _vector<T>(v1 :Vector<T>, v2 :Vector<T>, s :T) |
162 | 162 | + Mul<Output = T> + Div<Output = T> + Trig + Copy + Display { |
163 | 163 | println!("{:>14} : {}", "Vector v1", v1); |
164 | 164 | println!("{:>14} : {}", "Vector v2", v2); |
165 | - println!("{:>14} : {}", "abs v1", v1.abs()); | |
165 | + println!("{:>14} : {}", "magnitude v1", v1.mag()); | |
166 | 166 | println!("{:>14} : {}", "-v1", -v1); |
167 | 167 | println!("{:>14} : {}", "v1 + v1", v1 + v1); |
168 | 168 | println!("{:>14} : {}", "v1 - v1", v1 - v1); |
169 | 169 | println!("{:>14} : {}", "v2 - v1", v2 - v1); |
170 | 170 | println!("{:>14} : {}", format!("v1 * {}", s), v1.mul(&s)); |
171 | 171 | println!("{:>14} : {}", "norm v1", v1.norm()); |
172 | - println!("{:>14} : {}", "abs norm v1", v1.norm().abs()); | |
173 | - println!("{:>14} : {}", "abs v1", v1.abs()); | |
174 | - println!("{:>14} : {}", "norm * abs", v1.norm().mul(&v1.abs())); | |
172 | + println!("{:>14} : {}", "magnitude norm v1", v1.norm().mag()); | |
173 | + println!("{:>14} : {}", "magnitude v1", v1.mag()); | |
174 | + println!("{:>14} : {}", "norm * magnitude", v1.norm().mul(&v1.mag())); | |
175 | 175 | println!("{:>14} : {}", "distance v1 v2", v1.distance(v2)); |
176 | 176 | println!("{:>14} : {}", "distance v2 v1", v2.distance(v1)); |
177 | 177 | println!("{:>14} : {}", "v1 dot v2", v1.dot(v2)); |
... | ... | @@ -313,15 +313,17 @@ fn _line() { |
313 | 313 | fn _democanvas<T>( xcb :&XcbEasel |
314 | 314 | , title :&'static str |
315 | 315 | , tx :mpsc::Sender<i32> |
316 | + , _triangle :Polyeder<T> | |
316 | 317 | , tetrahedron :Polyeder<T> |
317 | - , cube :Polyeder<T> ) | |
318 | + , cube :Polyeder<T> | |
319 | + , light :DirectLight<T> ) | |
318 | 320 | where T: 'static + Add<Output = T> + Sub<Output = T> + Neg<Output = T> |
319 | 321 | + Mul<Output = T> + Div<Output = T> |
320 | - + Debug + Copy + Trig + Send + From<i32> { | |
322 | + + Debug + Copy + Trig + Send + From<i32> + PartialOrd { | |
321 | 323 | |
322 | 324 | let mut canvas = xcb.canvas(151, 151).unwrap(); |
323 | - let camera = Camera::<T>::new(&canvas, 45); // the orig. view angle | |
324 | - // was 50. | |
325 | + let camera = Camera::<T>::new(&canvas, 45); // the orig. view angle | |
326 | + // was 50. | |
325 | 327 | |
326 | 328 | canvas.set_title(title); |
327 | 329 | canvas.init_events(); |
... | ... | @@ -333,24 +335,31 @@ fn _democanvas<T>( xcb :&XcbEasel |
333 | 335 | let mut last = Instant::now(); |
334 | 336 | |
335 | 337 | let t :TMatrix<T> = translate(Vector(0.into(), 0.into(), 150.into())); |
336 | - let p :TMatrix<T> = camera.get_projection(); | |
338 | + // We do not need this here… it is used within projection… | |
339 | + // let p :TMatrix<T> = camera.get_projection(); | |
337 | 340 | |
338 | 341 | loop { |
339 | 342 | let deg = ((start.elapsed() / 25).as_millis() % 360) as i32; |
340 | 343 | |
341 | 344 | let rz :TMatrix<T> = rotate_z(deg); |
342 | 345 | |
343 | - let rot1 = TMatrix::combine(vec!(rz, rotate_x(-deg*2), t, p)); | |
344 | - let rot2 = TMatrix::combine(vec!(rz, rotate_y(-deg*2), t, p)); | |
346 | + // I can not apply the projection in one turn, as I generate the | |
347 | + // normals always… and this is no longer possible after the | |
348 | + // projection… | |
349 | + // let rot1 = TMatrix::combine(vec!(rz, rotate_x(-deg*2), t, p)); | |
350 | + // let rot2 = TMatrix::combine(vec!(rz, rotate_y(-deg*2), t, p)); | |
351 | + let rot1 = TMatrix::combine(vec!(rz, rotate_x(-deg*2), t)); | |
352 | + let rot2 = TMatrix::combine(vec!(rz, rotate_y(-deg*2), t)); | |
345 | 353 | |
346 | 354 | let objects = vec!( (tetrahedron.transform(&rot1), 0xFFFF00) |
347 | 355 | , ( cube.transform(&rot2), 0x0000FF) ); |
356 | + //let objects = vec!( (triangle.transform(&rot1), 0xFFFF00) ); | |
348 | 357 | |
349 | 358 | canvas.clear(); |
350 | 359 | |
351 | 360 | for (o, color) in objects { |
352 | - for pg in o.project(&camera) { | |
353 | - canvas.draw(&pg, Coordinate(0,0), color); | |
361 | + for (pg, c) in o.project(&camera, &light, color) { | |
362 | + canvas.draw(&pg, Coordinate(0,0), c); | |
354 | 363 | } |
355 | 364 | } |
356 | 365 | |
... | ... | @@ -400,11 +409,17 @@ fn main() { |
400 | 409 | let (tx, rx) = mpsc::channel(); |
401 | 410 | |
402 | 411 | _democanvas( &xcb, "Something...(f64)", tx.clone() |
412 | + , Polyeder::triangle(60.0) | |
403 | 413 | , Polyeder::tetrahedron(60.0) |
404 | - , Polyeder::cube(60.0) ); | |
414 | + , Polyeder::cube(60.0) | |
415 | + , DirectLight::new(Vector(0.0, 0.0, 1.0)) ); | |
405 | 416 | _democanvas( &xcb, "Something...(Fractional)", tx.clone() |
417 | + , Polyeder::triangle(Fractional(60,1)) | |
406 | 418 | , Polyeder::tetrahedron(Fractional(60,1)) |
407 | - , Polyeder::cube(Fractional(60,1)) ); | |
419 | + , Polyeder::cube(Fractional(60,1)) | |
420 | + , DirectLight::new(Vector( Fractional(0,1) | |
421 | + , Fractional(0,1) | |
422 | + , Fractional(1,1) )) ); | |
408 | 423 | |
409 | 424 | for x in rx { |
410 | 425 | match x { | ... | ... |
... | ... | @@ -34,7 +34,7 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> |
34 | 34 | pub fn y(self) -> T { self.1 } |
35 | 35 | pub fn z(self) -> T { self.2 } |
36 | 36 | |
37 | - pub fn abs(self) -> T { | |
37 | + pub fn mag(self) -> T { | |
38 | 38 | let Vector(x, y, z) = self; |
39 | 39 | (x * x + y * y + z * z).sqrt().unwrap() |
40 | 40 | } |
... | ... | @@ -53,12 +53,12 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> |
53 | 53 | |
54 | 54 | pub fn norm(self) -> Self { |
55 | 55 | // TODO This can result in 0 or inf Vectors… |
56 | - // Maybe we need to handle zero and inf abs here… | |
57 | - self.mul(&self.abs()) | |
56 | + // Maybe we need to handle zero and inf magnitude here… | |
57 | + self.mul(&self.mag().recip()) | |
58 | 58 | } |
59 | 59 | |
60 | 60 | pub fn distance(self, other :Self) -> T { |
61 | - (self - other).abs() | |
61 | + (self - other).mag() | |
62 | 62 | } |
63 | 63 | } |
64 | 64 | ... | ... |
Please
register
or
login
to post a comment