Commit b2f9d492c13cbedae9229096d9ca703225cf3fe8

Authored by Georg Hopp
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...
  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