Rework PaintPolygon2D's triangle rasterizer so both color and uv interpolation works properly.

This commit is contained in:
Relintai 2023-08-26 11:01:03 +02:00
parent 43a2b7d6f3
commit 94f412a352
2 changed files with 174 additions and 73 deletions

View File

@ -434,10 +434,9 @@ Ref<Image> PaintPolygon2D::_get_rendered_image() {
texture_image->lock();
}
Vector2 cpoints[3];
Vector2i cpoints[3];
Vector2 cuvs[3];
Color ccolors[3];
int cinds[3];
if (colors.size() == 1) {
for (int j = 0; j < 3; ++j) {
@ -449,101 +448,86 @@ Ref<Image> PaintPolygon2D::_get_rendered_image() {
for (int index = 0; index < indices.size(); index += 3) {
// Rasterize triangle
// Based on https://www.youtube.com/watch?v=PahbNFypubE
for (int j = 0; j < 3; ++j) {
cinds[j] = indices[index + j];
for (int i = 0; i < 3; ++i) {
int cind = indices[index + i];
cpoints[j] = points[cinds[j]];
cpoints[i] = points[cind].round();
if (colors.size() > 1) {
ccolors[j] = colors[cinds[j]];
ccolors[i] = colors[cind];
}
if (use_uvs) {
cuvs[j] = uvs[cinds[j]];
cuvs[i] = uvs[cind];
}
}
// Super simple scanline rasterizer
// Same idea as presented here: https://www.youtube.com/watch?v=PahbNFypubE
//Sort them
Vector2i min_coords = Vector2i(INT_MAX, INT_MAX);
Vector2i max_coords = Vector2i(INT_MIN, INT_MIN);
for (int j = 0; j < 3; ++j) {
min_coords.x = MIN(cpoints[j].x, min_coords.x);
min_coords.y = MIN(cpoints[j].y, min_coords.y);
max_coords.x = MAX(cpoints[j].x, max_coords.x);
max_coords.y = MAX(cpoints[j].y, max_coords.y);
if (cpoints[1].y < cpoints[0].y || (cpoints[1].y == cpoints[0].y && cpoints[1].x < cpoints[0].x)) {
SWAP(cpoints[0], cpoints[1]);
SWAP(ccolors[0], ccolors[1]);
SWAP(cuvs[0], cuvs[1]);
}
Vector<Point2i> brenzenham_line_points = Geometry::brenzenham_line(cpoints[0].x, cpoints[1].x, cpoints[0].y, cpoints[1].y);
brenzenham_line_points.append_array(Geometry::brenzenham_line(cpoints[1].x, cpoints[2].x, cpoints[1].y, cpoints[2].y));
brenzenham_line_points.append_array(Geometry::brenzenham_line(cpoints[2].x, cpoints[0].x, cpoints[2].y, cpoints[0].y));
if (cpoints[2].y < cpoints[0].y || (cpoints[2].y == cpoints[0].y && cpoints[2].x < cpoints[0].x)) {
SWAP(cpoints[0], cpoints[2]);
SWAP(ccolors[0], ccolors[2]);
SWAP(cuvs[0], cuvs[2]);
}
//Rect2i tri_rect = Rect2i(min_coords, max_coords - min_coords);
if (cpoints[2].y < cpoints[1].y || (cpoints[2].y == cpoints[1].y && cpoints[2].x < cpoints[1].x)) {
SWAP(cpoints[1], cpoints[2]);
SWAP(ccolors[1], ccolors[2]);
SWAP(cuvs[1], cuvs[2]);
}
const int blp_size = brenzenham_line_points.size();
const Point2i *blp_ptr = brenzenham_line_points.ptr();
int fys = MAX(0, min_coords.y);
int fye = MIN(_size.y, max_coords.y);
for (int y = fys; y < fye; ++y) {
// Current scanline:
// . . . . psx X X X X X pse . . . .
int psx = INT_MAX;
int pex = INT_MIN;
for (int i = 0; i < blp_size; ++i) {
Point2i p = blp_ptr[i];
if (p.y != y) {
if (cpoints[0].y == cpoints[2].y) {
continue;
}
if (p.x < psx) {
psx = p.x;
}
bool shortside = (cpoints[1].y - cpoints[0].y) * (cpoints[2].x - cpoints[0].x) < (cpoints[1].x - cpoints[0].x) * (cpoints[2].y - cpoints[0].y);
if (p.x > pex) {
pex = p.x;
}
}
Slope sides[2];
if (psx == INT_MIN || pex == INT_MAX) {
continue;
}
// maybe?
// pse -= 1;
float interpy = (y - min_coords.y) / (float)(max_coords.y - min_coords.y);
Vector2 uvsy;
Vector2 uvey;
sides[!shortside].setup_position(cpoints[0], cpoints[2], cpoints[2].y - cpoints[0].y);
sides[!shortside].setup_color(ccolors[0], ccolors[2], cpoints[2].y - cpoints[0].y);
if (use_uvs) {
uvsy = cuvs[0].linear_interpolate(cuvs[1], interpy);
uvey = cuvs[0].linear_interpolate(cuvs[2], interpy);
sides[!shortside].setup_uv(cuvs[0], cuvs[2], cpoints[2].y - cpoints[0].y);
}
Color colorsy = ccolors[0].linear_interpolate(ccolors[1], interpy);
Color colorey = ccolors[0].linear_interpolate(ccolors[2], interpy);
int fxs = MAX(0, psx);
int fxe = MIN(_size.x, pex);
for (int x = fxs; x < fxe; ++x) {
//Vector2 point = Vector2(x, y);
float interpx = (x - psx) / (float)(pex - psx);
Color color = colorsy.linear_interpolate(colorey, interpx);
if (cpoints[0].y < cpoints[1].y) {
sides[shortside].setup_position(cpoints[0], cpoints[1], cpoints[1].y - cpoints[0].y);
sides[shortside].setup_color(ccolors[0], ccolors[1], cpoints[1].y - cpoints[0].y);
if (use_uvs) {
Vector2 uv = uvsy.linear_interpolate(uvey, interpx);
sides[shortside].setup_uv(cuvs[0], cuvs[1], cpoints[1].y - cpoints[0].y);
}
int starty = MAX(0, cpoints[0].y);
int endy = MIN(cpoints[1].y, _size.y);
for (int y = starty; y < endy; ++y) {
Slope s;
s.setup_color(sides[0].color_current, sides[1].color_current, sides[1].position_current.x - sides[0].position_current.x);
if (use_uvs) {
s.setup_uv(sides[0].uv_current, sides[1].uv_current, sides[1].position_current.x - sides[0].position_current.x);
}
int startx = MAX(0, sides[0].position_current.x);
int endx = MIN(sides[1].position_current.x, _size.x);
for (int x = startx; x < endx; ++x) {
Color color = s.color_current;
if (use_uvs) {
Vector2 uv = s.uv_current;
uv.x = CLAMP(uv.x, 0, 1);
uv.y = CLAMP(uv.y, 0, 1);
Vector2 imgcoord = uv * texture_image_size;
@ -556,6 +540,80 @@ Ref<Image> PaintPolygon2D::_get_rendered_image() {
}
_rendered_image->set_pixelv(Vector2(x, y), color);
s.advance_color();
if (use_uvs) {
s.advance_uv();
}
}
sides[0].advance();
sides[1].advance();
if (use_uvs) {
sides[0].advance_uv();
sides[1].advance_uv();
}
}
}
if (cpoints[1].y < cpoints[2].y) {
sides[shortside].setup_position(cpoints[1], cpoints[2], cpoints[2].y - cpoints[1].y);
sides[shortside].setup_color(ccolors[1], ccolors[2], cpoints[2].y - cpoints[1].y);
if (use_uvs) {
sides[shortside].setup_uv(cuvs[1], cuvs[2], cpoints[2].y - cpoints[1].y);
}
int starty = MAX(0, cpoints[1].y);
int endy = MIN(cpoints[2].y, _size.y);
for (int y = starty; y < endy; ++y) {
Slope s;
s.setup_color(sides[0].color_current, sides[1].color_current, sides[1].position_current.x - sides[0].position_current.x);
if (use_uvs) {
s.setup_uv(sides[0].uv_current, sides[1].uv_current, sides[1].position_current.x - sides[0].position_current.x);
}
int startx = MAX(0, sides[0].position_current.x);
int endx = MIN(sides[1].position_current.x, _size.x);
for (int x = startx; x < endx; ++x) {
Color color = s.color_current;
if (use_uvs) {
Vector2 uv = s.uv_current;
uv.x = CLAMP(uv.x, 0, 1);
uv.y = CLAMP(uv.y, 0, 1);
Vector2 imgcoord = uv * texture_image_size;
imgcoord.x = CLAMP(imgcoord.x, 0, texture_image_size.x - 1);
imgcoord.y = CLAMP(imgcoord.y, 0, texture_image_size.y - 1);
Color img_color = texture_image->get_pixelv(imgcoord);
color *= img_color;
}
_rendered_image->set_pixelv(Vector2(x, y), color);
s.advance_color();
if (use_uvs) {
s.advance_uv();
}
}
sides[0].advance();
sides[1].advance();
if (use_uvs) {
sides[0].advance_uv();
sides[1].advance_uv();
}
}
}
}

View File

@ -126,6 +126,49 @@ public:
PaintPolygon2D();
virtual ~PaintPolygon2D();
protected:
struct Slope {
Vector2 position_current;
Vector2 position_step;
Vector2 uv_current;
Vector2 uv_step;
Color color_current;
Color color_step;
_FORCE_INLINE_ void setup_position(Vector2 begin, Vector2 end, float num_steps) {
float inv_step = 1.0 / num_steps;
position_step = (end - begin) * Vector2(inv_step, inv_step);
position_current = begin;
}
_FORCE_INLINE_ void setup_color(Color begin, Color end, float num_steps) {
float inv_step = 1.0 / num_steps;
color_step = (end - begin) * Color(inv_step, inv_step, inv_step);
color_current = begin;
}
_FORCE_INLINE_ void setup_uv(Vector2 begin, Vector2 end, float num_steps) {
float inv_step = 1.0 / num_steps;
uv_step = (end - begin) * Vector2(inv_step, inv_step);
uv_current = begin;
}
_FORCE_INLINE_ void advance() {
position_current += position_step;
color_current += color_step;
}
_FORCE_INLINE_ void advance_color() {
color_current += color_step;
}
_FORCE_INLINE_ void advance_uv() {
uv_current += uv_step;
}
};
};
#endif // POLYGON_2D_H