Commit 1375f2ad authored by Patrik Huber's avatar Patrik Huber

Added rest of the helper functions for the rendering

- extended TriangleToRasterize
- process_prospective_tri()
- added plane class
- added clip_polygon_to_plane_in_4d()
parent efc90e64
......@@ -46,12 +46,78 @@ public:
cv::Vec4f position;
cv::Vec3f color;
cv::Vec2f texcrd;
class plane
{
public:
plane() {}
plane(float a, float b, float c, float d)
{
this->a = a;
this->b = b;
this->c = c;
this->d = d;
}
plane(const cv::Vec3f& normal, float d = 0.0f)
{
this->a = normal[0];
this->b = normal[1];
this->c = normal[2];
this->d = d;
}
plane(const cv::Vec3f& point, const cv::Vec3f& normal)
{
a = normal[0];
b = normal[1];
c = normal[2];
d = -(point.dot(normal));
}
plane(const cv::Vec3f& point1, const cv::Vec3f& point2, const cv::Vec3f& point3)
{
cv::Vec3f v1 = point2 - point1;
cv::Vec3f v2 = point3 - point1;
cv::Vec3f normal = (v1.cross(v2));
normal /= cv::norm(normal, cv::NORM_L2);
a = normal[0];
b = normal[1];
c = normal[2];
d = -(point1.dot(normal));
}
void normalize()
{
float length = sqrt(a*a + b*b + c*c);
a /= length;
b /= length;
c /= length;
}
float getSignedDistanceFromPoint(const cv::Vec3f& point) const
{
return a*point[0] + b*point[1] + c*point[2] + d;
}
float getSignedDistanceFromPoint(const cv::Vec4f& point) const
{
return a*point[0] + b*point[1] + c*point[2] + d;
}
public:
float a, b, c;
float d;
};
/**
* A representation for a triangle that is to be rasterised.
* Stores the enclosing bounding box of the triangle that is
* calculated during rendering and used during rasterisation.
*
* Used in render_affine and render.
*/
struct TriangleToRasterize
{
......@@ -60,6 +126,26 @@ struct TriangleToRasterize
int max_x;
int min_y;
int max_y;
// Everything below is only used in the "normal" renderer, but not
// in render_affine.
double one_over_z0;
double one_over_z1;
double one_over_z2;
//double one_over_v0ToLine12;
//double one_over_v1ToLine20;
//double one_over_v2ToLine01;
plane alphaPlane;
plane betaPlane;
plane gammaPlane;
double one_over_alpha_c; // those are only used for texturing -> float
double one_over_beta_c;
double one_over_gamma_c;
float alpha_ffx;
float beta_ffx;
float gamma_ffx;
float alpha_ffy;
float beta_ffy;
float gamma_ffy;
};
/**
......@@ -113,6 +199,54 @@ double implicit_line(float x, float y, const cv::Vec4f& v1, const cv::Vec4f& v2)
return ((double)v1[1] - (double)v2[1])*(double)x + ((double)v2[0] - (double)v1[0])*(double)y + (double)v1[0] * (double)v2[1] - (double)v2[0] * (double)v1[1];
};
std::vector<Vertex> clip_polygon_to_plane_in_4d(const std::vector<Vertex>& vertices, const cv::Vec4f& plane_normal)
{
std::vector<Vertex> clippedVertices;
// We can have 2 cases:
// * 1 vertex visible: we make 1 new triangle out of the visible vertex plus the 2 intersection points with the near-plane
// * 2 vertices visible: we have a quad, so we have to make 2 new triangles out of it.
// See here for more info? http://math.stackexchange.com/questions/400268/equation-for-a-line-through-a-plane-in-homogeneous-coordinates
for (unsigned int i = 0; i < vertices.size(); i++)
{
int a = i; // the current vertex
int b = (i + 1) % vertices.size(); // the following vertex (wraps around 0)
float fa = vertices[a].position.dot(plane_normal); // Note: Shouldn't they be unit length?
float fb = vertices[b].position.dot(plane_normal); // < 0 means on visible side, > 0 means on invisible side?
if ((fa < 0 && fb > 0) || (fa > 0 && fb < 0)) // one vertex is on the visible side of the plane, one on the invisible? so we need to split?
{
cv::Vec4f direction = vertices[b].position - vertices[a].position;
float t = -(plane_normal.dot(vertices[a].position)) / (plane_normal.dot(direction)); // the parametric value on the line, where the line to draw intersects the plane?
// generate a new vertex at the line-plane intersection point
cv::Vec4f position = vertices[a].position + t*direction;
cv::Vec3f color = vertices[a].color + t*(vertices[b].color - vertices[a].color);
cv::Vec2f texCoord = vertices[a].texcoords + t*(vertices[b].texcoords - vertices[a].texcoords); // We could omit that if we don't render with texture.
if (fa < 0) // we keep the original vertex plus the new one
{
clippedVertices.push_back(vertices[a]);
clippedVertices.push_back(Vertex(position, color, texCoord));
}
else if (fb < 0) // we use only the new vertex
{
clippedVertices.push_back(Vertex(position, color, texCoord));
}
}
else if (fa < 0 && fb < 0) // both are visible (on the "good" side of the plane), no splitting required, use the current vertex
{
clippedVertices.push_back(vertices[a]);
}
// else, both vertices are not visible, nothing to add and draw
}
return clippedVertices;
};
// used only in tex2D_linear_mipmap_linear
// template?
float clamp(float x, float a, float b)
......@@ -210,6 +344,86 @@ cv::Vec3f tex2d_linear(const cv::Vec2f& imageTexCoord, unsigned char mipmap_inde
return color;
};
// Todo: Split this function into the general (core-part) and the texturing part.
// Then, utils::extractTexture can re-use the core-part.
// Note: Maybe a bit outdated "todo" above.
boost::optional<TriangleToRasterize> process_prospective_tri(Vertex v0, Vertex v1, Vertex v2, int viewport_width, int viewport_height, bool enable_backface_culling)
{
using cv::Vec2f;
using cv::Vec3f;
TriangleToRasterize t;
t.v0 = v0; // no memcopy I think. the transformed vertices don't get copied and exist only once. They are a local variable in runVertexProcessor(), the ref is passed here, and if we need to rasterize it, it gets push_back'ed (=copied?) to trianglesToRasterize. Perfect I think. TODO: Not anymore, no ref here
t.v1 = v1;
t.v2 = v2;
// Only for texturing or perspective texturing:
//t.texture = _texture;
t.one_over_z0 = 1.0 / (double)t.v0.position[3];
t.one_over_z1 = 1.0 / (double)t.v1.position[3];
t.one_over_z2 = 1.0 / (double)t.v2.position[3];
// divide by w
// if ortho, we can do the divide as well, it will just be a / 1.0f.
t.v0.position = t.v0.position / t.v0.position[3];
t.v1.position = t.v1.position / t.v1.position[3];
t.v2.position = t.v2.position / t.v2.position[3];
// project from 4D to 2D window position with depth value in z coordinate
// Viewport transform:
/* (a possible optimisation might be to use matrix multiplication for this as well
and do it for all triangles at once? See 'windowTransform' in:
https://github.com/elador/FeatureDetection/blob/964f0b2107ce73ef2f06dc829e5084be421de5a5/libRender/src/render/RenderDevice.cpp)
*/
Vec2f v0_screen = eos::render::clip_to_screen_space(Vec2f(t.v0.position[0], t.v0.position[1]), viewport_width, viewport_height);
t.v0.position[0] = v0_screen[0];
t.v0.position[1] = v0_screen[1];
Vec2f v1_screen = clip_to_screen_space(Vec2f(t.v1.position[0], t.v1.position[1]), viewport_width, viewport_height);
t.v1.position[0] = v1_screen[0];
t.v1.position[1] = v1_screen[1];
Vec2f v2_screen = clip_to_screen_space(Vec2f(t.v2.position[0], t.v2.position[1]), viewport_width, viewport_height);
t.v2.position[0] = v2_screen[0];
t.v2.position[1] = v2_screen[1];
if (enable_backface_culling) {
if (!are_vertices_ccw_in_screen_space(t.v0.position, t.v1.position, t.v2.position))
return boost::none;
}
// Get the bounding box of the triangle:
cv::Rect boundingBox = calculate_clipped_bounding_box(t.v0.position, t.v1.position, t.v2.position, viewport_width, viewport_height);
t.min_x = boundingBox.x;
t.max_x = boundingBox.x + boundingBox.width;
t.min_y = boundingBox.y;
t.max_y = boundingBox.y + boundingBox.height;
if (t.max_x <= t.min_x || t.max_y <= t.min_y) // Note: Can the width/height of the bbox be negative? Maybe we only need to check for equality here?
return boost::none;
// Which of these is for texturing, mipmapping, what for perspective?
// for partial derivatives computation
t.alphaPlane = plane(Vec3f(t.v0.position[0], t.v0.position[1], t.v0.texcoords[0] * t.one_over_z0),
Vec3f(t.v1.position[0], t.v1.position[1], t.v1.texcoords[0] * t.one_over_z1),
Vec3f(t.v2.position[0], t.v2.position[1], t.v2.texcoords[0] * t.one_over_z2));
t.betaPlane = plane(Vec3f(t.v0.position[0], t.v0.position[1], t.v0.texcoords[1] * t.one_over_z0),
Vec3f(t.v1.position[0], t.v1.position[1], t.v1.texcoords[1] * t.one_over_z1),
Vec3f(t.v2.position[0], t.v2.position[1], t.v2.texcoords[1] * t.one_over_z2));
t.gammaPlane = plane(Vec3f(t.v0.position[0], t.v0.position[1], t.one_over_z0),
Vec3f(t.v1.position[0], t.v1.position[1], t.one_over_z1),
Vec3f(t.v2.position[0], t.v2.position[1], t.one_over_z2));
t.one_over_alpha_c = 1.0f / t.alphaPlane.c;
t.one_over_beta_c = 1.0f / t.betaPlane.c;
t.one_over_gamma_c = 1.0f / t.gammaPlane.c;
t.alpha_ffx = -t.alphaPlane.a * t.one_over_alpha_c;
t.beta_ffx = -t.betaPlane.a * t.one_over_beta_c;
t.gamma_ffx = -t.gammaPlane.a * t.one_over_gamma_c;
t.alpha_ffy = -t.alphaPlane.b * t.one_over_alpha_c;
t.beta_ffy = -t.betaPlane.b * t.one_over_beta_c;
t.gamma_ffy = -t.gammaPlane.b * t.one_over_gamma_c;
// Use t
return boost::optional<TriangleToRasterize>(t);
};
void raster_triangle(TriangleToRasterize triangle, cv::Mat colourbuffer, cv::Mat depthbuffer, boost::optional<Texture> texture, bool enable_far_clipping)
{
using cv::Vec2f;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment