rasterizer

c++ software renderer

clip.cpp

4.5 kB
  1#include "clip.hpp"
  2
  3#include <cmath>
  4#include <vector>
  5
  6#include "math.hpp"
  7
  8using std::vector;
  9
 10// canonical view volume
 11float plane_x_min(const vec4f &v) {
 12    return v.w + v.x;  // x >= -w
 13}
 14float plane_x_max(const vec4f &v) {
 15    return v.w - v.x;  // x <=  w
 16}
 17float plane_y_min(const vec4f &v) {
 18    return v.w + v.y;  // y >= -w
 19}
 20float plane_y_max(const vec4f &v) {
 21    return v.w - v.y;  // y <=  w
 22}
 23float plane_z_min(const vec4f &v) {
 24    return v.w + v.z;  // z >= -w
 25}
 26float plane_z_max(const vec4f &v) {
 27    return v.w - v.z;  // z <=  w
 28}
 29
 30// dead simple helpers to handle basic floating point precision on pushing
 31inline bool almost_same(const vec4f &a, const vec4f &b, float eps = 1e-6f) {
 32    return fabs(a.x - b.x) < eps &&
 33           fabs(a.y - b.y) < eps &&
 34           fabs(a.z - b.z) < eps &&
 35           fabs(a.w - b.w) < eps;
 36}
 37
 38inline void push_unique(vector<ClipVertex> &out, const ClipVertex &v) {
 39    if (out.empty() || !almost_same(out.back().pos, v.pos)) {
 40        out.push_back(v);
 41    }
 42}
 43
 44ClipVertex lerp_vert(const ClipVertex &a, const ClipVertex &b, float t) {
 45    ClipVertex result;
 46
 47    result.pos = vec4f_lerp(a.pos, b.pos, t);
 48
 49    float a_w      = a.pos.w;
 50    float b_w      = b.pos.w;
 51    float result_w = result.pos.w;
 52
 53    result.color  = vec3f_lerp(a.color, b.color, t);
 54    result.uv     = vec2f_lerp(a.uv, b.uv, t);
 55    result.normal = vec3f_lerp(a.normal, b.normal, t);
 56    return result;
 57}
 58
 59vector<ClipVertex> clip_against_plane(
 60    const vector<ClipVertex> &polygon_in, float (*plane_dist)(const vec4f &)
 61) {
 62    vector<ClipVertex> clipped_polygon;
 63    if (polygon_in.empty()) {
 64        return clipped_polygon;
 65    }
 66
 67    // prev vertex and distance to compare against
 68    ClipVertex prev_vert   = polygon_in.back();
 69    float      prev_dist   = plane_dist(prev_vert.pos);
 70    bool       prev_inside = (prev_dist >= 0.0f);
 71
 72    // loop over each vertex to compare
 73    for (size_t i = 0; i < polygon_in.size(); ++i) {
 74        const ClipVertex &curr_vert   = polygon_in[i];
 75        float             curr_dist   = plane_dist(curr_vert.pos);
 76        bool              curr_inside = (curr_dist >= 0.0f);
 77
 78        // case 1: both prev and curr are inside the plane
 79        // so the curr vertex also gets added to the new clipped polygon
 80        // notes/clipping/9
 81        if (prev_inside && curr_inside) {
 82            push_unique(clipped_polygon, curr_vert);
 83        }
 84
 85        // case 2: either vertex ends up inside while the other is outside the plane
 86        // time to create a new vert on the intersection and add it to the new polygon
 87        else if (prev_inside != curr_inside) {
 88            float      dist_difference           = prev_dist - curr_dist;
 89            float      edge_interpolation_factor = prev_dist / (dist_difference);
 90            ClipVertex intersect_vert            = lerp_vert(prev_vert, curr_vert, edge_interpolation_factor);
 91            push_unique(clipped_polygon, intersect_vert);
 92
 93            // case 3 (more like case 2.5): If the curr vertex is also already inside
 94            // safely add it to the clipped polygon
 95            // notes/clipping/10
 96            if (curr_inside) {
 97                push_unique(clipped_polygon, curr_vert);
 98            }
 99        }
100
101        // update prev_vert for the next iter
102        prev_vert   = curr_vert;
103        prev_dist   = curr_dist;
104        prev_inside = curr_inside;
105    }
106
107    return clipped_polygon;
108}
109
110// Clip space/Sutherland–Hodgman/Homogeneous clipping
111// clipping any polygon to the canonical view volume: -w <= x,y,z <= w.
112vector<ClipVertex> clip_polygon_with_attrs(
113    vec4f *clip_positions,
114    vec2f *input_uv,
115    vec3f  input_color,
116    vec3f  input_normal
117) {
118    vector<ClipVertex> clipped_vertices;
119    clipped_vertices.reserve(3);
120
121    for (int i = 0; i < 3; ++i) {
122        ClipVertex cv;
123        cv.pos    = clip_positions[i];
124        cv.uv     = input_uv[i];
125        cv.color  = input_color;
126        cv.normal = input_normal;
127        clipped_vertices.push_back(cv);
128    }
129
130    clipped_vertices = clip_against_plane(clipped_vertices, plane_x_min);
131    clipped_vertices = clip_against_plane(clipped_vertices, plane_x_max);
132
133    clipped_vertices = clip_against_plane(clipped_vertices, plane_y_min);
134    clipped_vertices = clip_against_plane(clipped_vertices, plane_y_max);
135
136    clipped_vertices = clip_against_plane(clipped_vertices, plane_z_min);
137    clipped_vertices = clip_against_plane(clipped_vertices, plane_z_max);
138
139    return clipped_vertices;  // could be empty if triangle fully outside
140}