/
main.cpp
164 lines (138 loc) · 4.32 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//
// main.cpp
// tinyrenderer
//
// Created by skychx on 2020/11/15.
//
#include <vector>
#include <cmath>
#include "tgaimage.h"
#include "model.h"
#include "geometry.h"
const TGAColor white = TGAColor(255, 255, 255, 255);
const TGAColor red = TGAColor(255, 0, 0, 255);
Model *model = NULL;
const int width = 800;
const int height = 800;
// Bresenham’s 直线算法
// 具体实现参考 https://www.wikiwand.com/en/Bresenham%27s_line_algorithm#/All_cases
void line(int x1, int y1, int x2, int y2, TGAImage &image, TGAColor color) {
// 处理斜率的绝对值大于 1 的直线
bool steep = false;
if (std::abs(x1-x2) < std::abs(y1-y2)) {
std::swap(x1, y1);
std::swap(x2, y2);
steep = true;
}
// 交换起点终点的坐标
if (x1 > x2) {
std::swap(x1, x2);
std::swap(y1, y2);
}
int y = y1;
int eps = 0;
int dx = x2 - x1;
int dy = y2 - y1;
int yi = 1;
// 处理 [-1, 0] 范围内的斜率
if (dy < 0) {
yi = -1;
dy = -dy;
}
for (int x = x1; x <= x2; x++) {
if (steep) {
image.set(y, x, color);
} else {
image.set(x, y, color);
}
eps += dy;
// 这里用位运算 <<1 代替 *2
if((eps << 1) >= dx) {
y = y + yi;
eps -= dx;
}
}
}
// DDA 算法
// 缺点:涉及大量的浮点运算
void lineDDA(int x1, int y1, int x2, int y2, TGAImage &image, TGAColor color) {
float x = x1;
float y = y1;
int dx = x2 - x1;
int dy = y2 - y1;
float step;
float dlx, dly;
// 根据 dx 和 dy 的长度决定基准
if (std::abs(dx) >= std::abs(dy)) {
step = std::abs(dx);
} else {
step = std::abs(dy);
}
dlx = dx / step;
dly = dy / step;
for (int i=1; i<step; i++) {
image.set(x, y, color);
x = x + dlx;
y = y + dly;
}
}
void drawDDALine() {
TGAImage image(width, height, TGAImage::RGB);
lineDDA(400, 400, 800, 600, image, white);
lineDDA(400, 400, 600, 800, image, white);
lineDDA(400, 400, 200, 800, image, white);
lineDDA(400, 400, 0, 600, image, white);
lineDDA(400, 400, 0, 200, image, white);
lineDDA(400, 400, 200, 0, image, white);
lineDDA(400, 400, 600, 0, image, white);
lineDDA(400, 400, 800, 200, image, white);
lineDDA( 0, 400, 800, 400, image, white);
lineDDA(400, 0, 400, 800, image, white);
image.flip_vertically();
image.write_tga_file("output/day_02_line_dda.tga");
}
void drawBresenhamLine() {
TGAImage image(width, height, TGAImage::RGB);
line(400, 400, 800, 600, image, white);
line(400, 400, 600, 800, image, white);
line(400, 400, 200, 800, image, white);
line(400, 400, 0, 600, image, white);
line(400, 400, 0, 200, image, white);
line(400, 400, 200, 0, image, white);
line(400, 400, 600, 0, image, white);
line(400, 400, 800, 200, image, white);
line( 0, 400, 800, 400, image, white);
line(400, 0, 400, 800, image, white);
image.flip_vertically();
image.write_tga_file("output/day_02_line_bresenham.tga");
}
void drawObj() {
model = new Model("obj/african_head.obj");
TGAImage image(width, height, TGAImage::RGB);
// 循环模型里的所有三角形
for (int i = 0; i < model->nfaces(); i++) {
std::vector<int> face = model->face(i);
// 循环三角形三个顶点,每两个顶点连一条线
for (int j = 0; j < 3; j++) {
Vec3f v0 = model->vert(face[j]);
Vec3f v1 = model->vert(face[(j + 1) % 3]);
// 因为模型空间取值范围是 [-1, 1]^3,我们要把模型坐标平移到屏幕坐标中
// 下面 (point + 1) * width(height) / 2 的操作学名为视口变换(Viewport Transformation)
int x0 = (v0.x + 1.) * width / 2.;
int y0 = (v0.y + 1.) * height / 2.;
int x1 = (v1.x + 1.) * width / 2.;
int y1 = (v1.y + 1.) * height / 2.;
// 画线
line(x0, y0, x1, y1, image, white);
}
}
image.flip_vertically();
image.write_tga_file("output/day_02_line_obj.tga");
delete model;
}
int main(int argc, char** argv) {
drawDDALine();
drawBresenhamLine();
drawObj();
return 0;
}