深度クリッピング < 点からはじめるラスタライザ
from 透視変換を修正するまでの道のり < 点からはじめるラスタライザ
深度クリッピング < 点からはじめるラスタライザ
さっき観察して$ w < 0だったの気になる(普通カメラの前側にあるなら$ -z)になるはず
行列演算をみると$ w値は$ zの値まんまになるから...。
どれ、perspectiveにかけられる前後のワールド座標でもみるか
code:canvas/camera.rs
snapshot!(&view * &actors0.vertices0);
snapshot!(&perspective *&view * &actors0.vertices0);
snapshot!(&view * &actors1.vertices0);
snapshot!(&perspective *&view * &actors1.vertices0);
code:rs
snapshot &view * &actors0.vertices0 = Vec4 { e: 0.0, 0.0, -5.0, 1.0 } at src/camera.rs:41
snapshot &perspective *&view * &actors0.vertices0 = Vec4 { e: 0.0, 0.0, -5.201020102010202, -5.0 } at src/camera.rs:42
snapshot &view * &actors1.vertices0 = Vec4 { e: 0.0, 1.3333333333333333, -6.666666666666667, 1.0 } at src/camera.rs:43
snapshot &perspective *&view * &actors1.vertices0 = Vec4 { e: 0.0, 0.888888888888889, -6.868020135346869, -6.666666666666667 } at src/camera.rs:44
負の値になっとるなぁappbird.icon
二つ謎があるappbird.icon
1. ビュー変換された後の座標の$ z値は正になっていて欲しい(今は全てをカメラの前側においているため)
code:rs
pub fn view_conversion(&self) -> Mat4x4 {
Mat4x4::translate(&-(&self.position))
}
いやちょっと待って。オブジェクトの置き方が変じゃないか?(カメラの裏側)
オブジェクトの位置を次のように修正
code:main.rs
Actor{
vertices: Vec4::newpoint(0., 0., 0.), Vec4::newpoint(3./3., 1./3., -2./3.), Vec4::newpoint(0./3., -3./3., -4./3.),
:
},
Actor{
vertices: Vec4::newpoint(0., 4./3., 5./3.), Vec4::newpoint(-3./3., -1./3., 2./3.), Vec4::newpoint(0., 3., -4./3.),
:
}
code:main.rs
snapshot &perspective *&view * &actors0.vertices0 = Vec4 { e: 0.0, 0.0, 2.8005800580058007, 3.0 } at src/camera.rs:41
snapshot &perspective *&view * &actors1.vertices0 = Vec4 { e: 0.0, 0.888888888888889, 1.1335800246691334, 1.3333333333333333 } at src/camera.rs:42
wで正規化された後の値を見たいところ...appbird.icon
transform_into_screenってProjectionが責任を持つべきではなかろうかappbird.icon
code:util/Vec4.rs
pub struct Vec4Project(Vec4);
impl Vec4Project {
pub fn new(v:Vec4) -> Self {
if v.w() < 1e-6 { Self(v*1e6) } else { Self(&v/v.w()) }
}
pub fn into_screen(&self, size:Point2) -> Vec4Screen {
let scale = size.y as f64 / 2.;
let v = &self.0 * scale + size.to_vec4() / 2.;
Vec4Screen(v)
}
}
ここでPoint2までここで変換しないのは、後々z-bufferをつける必要があるため(Point2まで変換するとzが捨てられてしまう)
cameraがdraw_triangleの関数を持ってるのも変なので直接繋げちゃおう
code:camera.rs
pub fn snapshot(&mut self, canvas:&mut Canvas, actors:&Vec<Actor>) {
let perspective = self.perspective_conversion();
let view = self.view_conversion();
let pv = perspective*view;
for actor in actors {
let model = actor.model_conversion();
let pvm = &pv * &model;
let projected:Point2; 3 = [
Vec4Project::new(&pvm * &actor.vertices0).into_screen(&canvas.size()).to_point2(),
Vec4Project::new(&pvm * &actor.vertices1).into_screen(&canvas.size()).to_point2(),
Vec4Project::new(&pvm * &actor.vertices2).into_screen(&canvas.size()).to_point2(),
];
canvas.draw_triangle(&projected, &actor.color);
}
}
ついでに、draw_pointとdraw_lineも消してしまうかappbird.icon cameraが責任持ってるのおかしい
code:bin/affine.rs
// ループ中
let t = stopwatch.elapsed_as_sec();
let points:Vec<_> = points.iter().map(|e| {
let m =
Mat4x4::translate(&Vec4::newvec( -0.5, -0.25, 0.))
* Mat4x4::rotation(&Vec4::newvec(0., 0., 1.), PI*t/6.)
* Mat4x4::scale(&Vec4::newvec(0.8, 0.5, 1.));
Vec4Project::new(m * e).into_screen(&canvas.size()).to_point2()
}).collect();
let white = Color::new(1., 1., 1., 1.);
for point in &points {
canvas.draw_point(point, &white);
}
for (i, j) in (0, 1), (1, 2), (2, 3), (3, 0) {
let p1 = &pointsi;
let p2 = &pointsj;
canvas.draw_line(p1, p2, &white);
}
code:bin/triangle.rs
let points: Vec4; 3 = [
Vec4::newpoint(0.5, 0.2, 0.),
Vec4::newpoint(-0.8, -0.5, 0.),
Vec4::newpoint(-0.5, 0.5, 0.)
];
let points: Point2; 3 =
points.map(|e| Vec4Project::new(e).into_screen(&canvas.size()).to_point2());
という感じに書き換えていく。
確かにコードはすごい自然になったかも!appbird.icon
カメラが本来必要ないコード(canvasに直接書き込んでいくコード)でcameraへの依存がなくなった
(ちょっと図形の配置を変えた)
https://gyazo.com/8b53bae6999168494a1503228eb95d68
そもそもの話
$ z深度が$ \lbrack-1, 1\rbrack外にある場合は描かないほうがよい
draw_triangle内で点群が[-1, 1]の外にある場合は描かないようにする。
Point2を渡している現状をVec4Screenに基づいて計算するようにしたい?
その都合上、bound_xで打つピクセルを絞るコードがなくなったが、まあそれほど実行速度には影響しない.....はず。
code:rs
fn area(p0:&Vec4, p1:&Vec4, p2:&Vec4) -> f64 {
let dx = p1 - p0;
let dy = p2 - p0;
dx.cross2d(&dy) // cross2dはよしなに定義
}
pub fn draw_triangle(
&mut self,
points: Vec4Screen; 3,
color: &Color; 3
) {
// y基準でソート
let mut points = points.map(|p| p.0);
points.sort_by(|a, b| a.y().partial_cmp(&b.y()).unwrap_or(std::cmp::Ordering::Equal));
let bound_x = ClosedInterval::between(0,(self.width-1) as i32);
let bound_y = ClosedInterval::between(0,(self.height-1) as i32);
let bound_z = ClosedInterval::between(0.,1.);
let x_segment = ClosedInterval::range(points.iter().map(|p| p.x() as i32).collect::<Vec<_>>());
let y_segment = ClosedInterval::range(points.iter().map(|p| p.y() as i32).collect::<Vec<_>>());
let z_segment = ClosedInterval::range(points.iter().map(|p| p.z()).collect::<Vec<_>>());
if z_segment.and(&bound_z).is_empty() { return; } // 三角形のz値域と0, 1が重なっていなければ描画しない
let bound_w = ClosedInterval::between(0.,1.);
let inv_abc = 1./area(&points0, &points1, &points2);
for y in &y_segment.and(&bound_y) {
for x in &x_segment.and(&bound_x) {
let p = Vec4::newpixel(x, y); // (x + 0.5, y + 0.5, 0, 0)に相当
let w0 = area(&points1, &points2, &p) * inv_abc;
let w1 = area(&points2, &points0, &p) * inv_abc;
let w2 = area(&points0, &points1, &p) * inv_abc;
if !bound_w.includes(w0) || !bound_w.includes(w1) || !bound_w.includes(w2) { continue; } // includesもよしなに定義
self.draw_pixel(&p.to_point2(),&(w0*&color0 + w1 * &color1 + w2 * &color2));
}
}
}
しかし めのまえがまっくらになった!
code:rs
snapshot &pvm * &actor.vertices0 = Vec4 { e: 0.0, 0.0, 9.801980198019804, 10.0 } at src/camera.rs:44
snapshot &pvm * &actor.vertices1 = Vec4 { e: 1.5000000000000004, 0.6666666666666667, 7.801580158015803, 8.0 } at src/camera.rs:45
snapshot &pvm * &actor.vertices2 = Vec4 { e: 0.0, -2.0, 5.8011801180118026, 6.0 } at src/camera.rs:46
snapshot projected = [Vec4Screen(Vec4 { e: 320.0, 240.0, 0.9801980198019804, 1.0 }), Vec4Screen(Vec4 { e: 365.0, 285.0, 0.9751975197519753, 1.0 }), Vec4Screen(Vec4 { e: 320.0, 240.0, 0.9668633530019671, 1.0 })] at src/camera.rs:52
二つの点が同じ場所に来るのはなんか変ではないか
Vec4 { e: [0.0, 0.0, 9.801980198019804, 10.0] } --> Vec4Screen(Vec4 { e: [320.0, 240.0, 0.9801980198019804, 1.0] }
Vec4 { e: [0.0, -2.0, 5.8011801180118026, 6.0] } ---> Vec4Screen(Vec4 { e: [320.0, 240.0, 0.9668633530019671, 1.0] }
はい
code:canvas/vec.rs
Vec4 { e: self.x() * x, self.x() * y, self.z(), self.w() }
^
y
はぁ〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
https://gyazo.com/8433e9e5ed71b08b988880235c24c258
物体は$ z \in (-6, 6)のどこかに存在する。
カメラを視点$ (0, 0, 8)から$ (0, 0, 1): z+方向に向けると
https://gyazo.com/1900f8b29e11bf8bf78196b75c320efa
カメラを視点$ (0, 0, -8)から$ (0, 0, 1): z+方向に向けると
https://gyazo.com/6d6a6bc0e3df9ede467dae403ba0f425