-
Notifications
You must be signed in to change notification settings - Fork 1
Day 6 (190914)
- Distinguished the side of the triangles.
// main.cpp
#include <vector>
#include <cmath>
#include "tgaimage.hpp"
#include "geometry.h"
const TGAColor white = TGAColor(255, 255, 255, 255);
const TGAColor red = TGAColor(255, 0, 0, 255);
const TGAColor green = TGAColor(0, 255, 0, 255);
const int width = 200;
const int height = 200;
void line(Vec2i p0, Vec2i p1, TGAImage &image, TGAColor color) {
bool steep = false;
if (std::abs(p0.x-p1.x)<std::abs(p0.y-p1.y)) {
std::swap(p0.x, p0.y);
std::swap(p1.x, p1.y);
steep = true;
}
if (p0.x>p1.x) {
std::swap(p0, p1);
}
for (int x=p0.x; x<=p1.x; x++) {
float t = (x-p0.x)/(float)(p1.x-p0.x);
int y = p0.y*(1.-t) + p1.y*t;
if (steep) {
image.set(y, x, color);
} else {
image.set(x, y, color);
}
}
}
void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage &image, TGAColor color) {
line(t0, t1, image, color);
line(t1, t2, image, color);
line(t2, t0, image, color);
}
int main(int argc, char** argv) {
TGAImage image(width, height, TGAImage::RGB);
Vec2i t0[3] = {Vec2i(10, 70), Vec2i(50, 160), Vec2i(70, 80)};
Vec2i t1[3] = {Vec2i(180, 50), Vec2i(150, 1), Vec2i(70, 180)};
Vec2i t2[3] = {Vec2i(180, 150), Vec2i(120, 160), Vec2i(130, 180)};
triangle(t0[0], t0[1], t0[2], image, red);
triangle(t1[0], t1[1], t1[2], image, white);
triangle(t2[0], t2[1], t2[2], image, green);
image.flip_vertically(); // i want to have the origin at the left bottom corner of the image
image.write_tga_file("output.tga");
return 0;
}
The output of the code is like below.
There were a few conditions to make code of filled triangles.
- It should be (surprise!) simple and fast.
- It should be symmetrical: the picture should not depend on the order of vertices passed to the drawing function.
- If two triangles have two common vertices, there should be no holes between them because of rasterization rounding.
- We could add more requirements, but let’s do with these ones. Traditionally a line sweeping is used:
- Sort vertices of the triangle by their y-coordinates;
- Rasterize simultaneously the left and the right sides of the triangle;
- Draw a horizontal line segment between the left and the right boundary points.
The lecture says, How do I draw a triangle? Once again, if you have a better method, I’d be glad to adopt it. Let us assume that we have three points of the triangle: t0, t1, t2, they are sorted in ascending order by the y-coordinate. Then, the boundary A is between t0 and t2, boundary B is between t0 and t1, and then between t1 and t2.
To make the same output with the lecture's, I wrote the code.
void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage &image) {
if (t0.y > t1.y){
std::swap(t0.x, t1.x);
std::swap(t0.y, t1.y);
}
if (t0.y > t2.y){
std::swap(t0.x, t2.x);
std::swap(t0.y, t2.y);
}
if (t1.y > t2.y){
std::swap(t1.x, t2.x);
std::swap(t1.y, t2.y);
}
line(t0, t1, image, green);
line(t1, t2, image, green);
line(t2, t0, image, red);
}
The output of the code is like below.
I set the color of the boundary A as red, and B as green.
Then as the second condition in the number 2. above, I need to make sure whether the triangle (the boundary B) is on the left side of the boundary A, or the right. To figure it out, I add code comparing x-coods of t1 and the boundary A.
enum class eSideType
{
Left, // 0
Right, // 1
};
// ...
Vec2i abp = Vec2i(boundary_x(t0, t2, t1.y), t1.y); // (the boundary) A boundary point
eSideType side;
if (t1.x < abp.x) {
side = eSideType::Left;
} else {
side = eSideType::Right;
}
And for this, I made two functions which returns x coord when you give y, and returns y coord when you give x.
int boundary_x(Vec2i v0, Vec2i v1, int y){
float x;
if (v1.y != v0.y){
x = static_cast<float>(v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x;
}
return static_cast<int>(x);
}
int boundary_y(Vec2i v0, Vec2i v1, int x){
float y;
if (v1.x != v0.x){
y = static_cast<float>(v1.y - v0.y) / (v1.x - v0.x) * (x - v0.x) + v0.y;
}
return static_cast<int>(y);
}
I need to pass two points on the line and the y value to boundary_x, and the function returns the x value of the y value on the line created by the two points. If I pass the x value to boundary_y, it returns the y value.
Floating point exception: 8
I should've made the exception for devision by 0. So I put the conditional statement.
if (v1.x != v0.x){
y = (v1.y - v0.y) / (v1.x - v0.x) * (x - v0.x) + v0.y;
}
error: use of undeclared identifier 'x'
I should've declared the variable x out of the conditional statement.
error: invalid operands to binary expression ('std::__1::ostream' (aka 'basic_ostream<char>') and 'eSideType')
I should've cast the type to integer so that enum type can be printed as a number.
std::cout << static_cast<int>(side) << std::endl;
void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage &image) {
if (t0.y > t1.y){
std::swap(t0.x, t1.x);
std::swap(t0.y, t1.y);
}
if (t0.y > t2.y){
std::swap(t0.x, t2.x);
std::swap(t0.y, t2.y);
}
if (t1.y > t2.y){
std::swap(t1.x, t2.x);
std::swap(t1.y, t2.y);
}
Vec2i abp = Vec2i(boundary_x(t0, t2, t1.y), t1.y); // (the boundary) A boundary point
eSideType side;
std::cout << t1.x << " " << abp.x << std::endl; // *
if (t1.x < abp.x) {
side = eSideType::Left;
} else {
side = eSideType::Right;
}
line(t0, t1, image, green);
line(t1, t2, image, green);
line(t2, t0, image, red);
std::cout << static_cast<int>(side) << std::endl; // *
}
I put the cout for checking the side. But uh-oh.
They were supposed to be 1 1 0, but all of them are 1.
int boundary_x(Vec2i v0, Vec2i v1, int y){
float x;
if (v1.y != v0.x){
x = (v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x;
}
std::cout << x << std::endl; // *
return static_cast<int>(x);
}
So to figure it out, I put some couts more. This time, it tells the value of the x before casting to integer.
10, 150, and something I don't know. They are all wrong.
int boundary_x(Vec2i v0, Vec2i v1, int y){
float x;
std::cout << v1.x - v0.x << " " << v1.y - v0.y << " " << y - v0.y << " " << v0.x << std::endl; // *
if (v1.y != v0.x){
x = (v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x;
}
std::cout << x << std::endl;
return static_cast<int>(x);
}
I tried to check all terms which decide the value of x.
They are all right. But I noticed that I should cast one of the terms so that the result can have float value.
x = static_cast<float>(v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x;
Like this.
Then the results are changed. But still, the third x has a garbage value.
int boundary_x(Vec2i v0, Vec2i v1, int y){
float x;
std::cout << v1.x - v0.x << " " << v1.y - v0.y << " " << y - v0.y << " " << v0.x << std::endl;
std::cout << static_cast<float>(v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x << std::endl; // *
if (v1.y != v0.x){
x = static_cast<float>(v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x;
}
std::cout << x << std::endl;
return static_cast<int>(x);
}
}
I checked how the result is changing by adding terms one by one.
Then, something weird happened. The value at the outside of if statement is printed right, but the inside is not.
int boundary_x(Vec2i v0, Vec2i v1, int y){
float x;
std::cout << v1.x - v0.x << " " << v1.y - v0.y << " " << y - v0.y << " " << v0.x << std::endl;
std::cout << static_cast<float>(v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x << std::endl;
if (v1.y != v0.x){
x = static_cast<float>(v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x;
std::cout << x << std::endl; // *
}
std::cout << x << std::endl;
return static_cast<int>(x);
}
I put one more cout inside of the if statement
It wasn't printed. For the third triangle, the if statement doesn't run. Then I checked the condition. There was a typo.
int boundary_x(Vec2i v0, Vec2i v1, int y){
float x;
std::cout << v1.x - v0.x << " " << v1.y - v0.y << " " << y - v0.y << " " << v0.x << std::endl;
std::cout << static_cast<float>(v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x << std::endl;
if (v1.y != v0.y){ // *
x = static_cast<float>(v1.x - v0.x) / (v1.y - v0.y) * (y - v0.y) + v0.x;
std::cout << x << std::endl;
}
std::cout << x << std::endl;
return static_cast<int>(x);
}
Fortunately, the third v1.y and v0.x are the same. If they are not, I couldn't find the typo.
Now the side of the third triangle is 0 (left).