{{:cgfs:perspective_projection-000.png?400|}} #include #include #include #include #include "vmath.h" int const Cw = 600; int const Ch = 600; float const Vw = 1.0; float const Vh = 1.0; float const ProjectionPlaneZ = 1.0; using vec2 = vmath::vec2; using vec3 = vmath::vec3; void PutPixel(int x, int y, Color const &color) { DrawPixel(x + Cw / 2, -y + Ch / 2, color); } template requires std::output_iterator void Interpolate(int i0, float d0, int i1, float d1, Iter out) { if (i0 == i1) { *out = d0; return; } auto a = (d1 - d0) / (i1 - i0); auto d = d0; for (int i = i0; i <= i1; ++i) { *out++ = d; d += a; } } void DrawLine(vec2 const &p0, vec2 const &p1, Color const &color) { auto x0 = p0.x; auto y0 = p0.y; auto x1 = p1.x; auto y1 = p1.y; if (std::abs(x1 - x0) > std::abs(y1 - y0)) { if (x0 > x1) { std::swap(x0, x1); std::swap(y0, y1); } auto i0 = static_cast(std::floor(x0)); auto i1 = static_cast(std::floor(x1)); auto ys = std::vector(i1 - i0 + 1); Interpolate(i0, y0, i1, y1, ys.begin()); for (int i = i0; i <= i1; ++i) { PutPixel(i, ys[i - i0], color); } } else { if (y0 > y1) { std::swap(y0, y1); std::swap(x0, x1); } auto i0 = static_cast(std::floor(y0)); auto i1 = static_cast(std::floor(y1)); auto xs = std::vector(i1 - i0 + 1); Interpolate(i0, x0, i1, x1, xs.begin()); for (int i = i0; i <= i1; ++i) { PutPixel(xs[i - i0], i, color); } } } vec2 ViewportToCanvas(float x, float y) { return {x * Cw / Vw, y * Ch / Vh}; } vec2 ProjectVertex(vec3 const &v) { return ViewportToCanvas(v.x * ProjectionPlaneZ / v.z, v.y * ProjectionPlaneZ / v.z); } int main() { InitWindow(Cw, Ch, "Perspective Projection"); // front auto vAf = vec3{-1, 1, 4}; auto vBf = vec3{1, 1, 4}; auto vCf = vec3{1, -1, 4}; auto vDf = vec3{-1, -1, 4}; // back auto vAb = vec3{-1, 1, 6}; auto vBb = vec3{1, 1, 6}; auto vCb = vec3{1, -1, 6}; auto vDb = vec3{-1, -1, 6}; while (!WindowShouldClose()) { BeginDrawing(); ClearBackground(WHITE); DrawLine(ProjectVertex(vAf), ProjectVertex(vBf), BLUE); DrawLine(ProjectVertex(vBf), ProjectVertex(vCf), BLUE); DrawLine(ProjectVertex(vCf), ProjectVertex(vDf), BLUE); DrawLine(ProjectVertex(vDf), ProjectVertex(vAf), BLUE); DrawLine(ProjectVertex(vAb), ProjectVertex(vBb), RED); DrawLine(ProjectVertex(vBb), ProjectVertex(vCb), RED); DrawLine(ProjectVertex(vCb), ProjectVertex(vDb), RED); DrawLine(ProjectVertex(vDb), ProjectVertex(vAb), RED); DrawLine(ProjectVertex(vAf), ProjectVertex(vAb), GREEN); DrawLine(ProjectVertex(vBf), ProjectVertex(vBb), GREEN); DrawLine(ProjectVertex(vCf), ProjectVertex(vCb), GREEN); DrawLine(ProjectVertex(vDf), ProjectVertex(vDb), GREEN); EndDrawing(); } }