假设在屏幕上面有两个点,P0(x0,y0)和P1(x1,y1),那么我们如何画一条从P0到P1的线?其实很简单,在P0P1这条线上的点P都满足一个方程:
P = P0 + t(P1 - P0)
转化为:
x = x0 + t(x1 - x0)
y = y0 + t(y1 - y0)
进一步化简第一个方程,求得:
t=(x - x0) / (x1 - x0)
把t带入第二个方程,可得:
y = y0 + (x - x0) / (x1 - x0) * (y1 - y0)
因为(y1 - y0) / (x1 - x0)是一个常量,咱们取为a(其实就是线的斜率),可得:
y = y0 + a * (x - x0)
进一步化简,可得:
y = ax + (y0 -ax0)
y0 - ax0其实就是线的截距,咱们取为b,可得:
y = ax + b
这个方程就是大家熟悉的直线方程了。
画线- 第一种画线方法
// 引用EasyX图形库头文件 #include <graphics.h> #include <conio.h> //glm数学相关头文件 #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float b = P0.y - a * P0.x; for (int x=P0.x;x<P1.x;x++) { int y = a * x + b; putpixel(x, y, color); } } int main() { initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素 DrawLine1(glm::vec2(100,100), glm::vec2(400,400), RED); _getch(); // 按任意键继续 closegraph(); // 关闭绘图窗口 return 0; }
结果
其实这个方法画出来的线是无法画垂直的线的,因为求a的时候P1.x-P0.x会等于零,除数是不能为零的(因为是浮点数所以可能并不会报错说进行了除零操作)。
- 第二种画线方法
y(x) = ax + b
y(x+1) =a * (x + 1) + b
对第二个方程进行变形,可得:
y(x+1) = ax + a + b
y(x+1) = (ax+b) + a
y(x+1) = y(x) + a
// 引用EasyX图形库头文件 #include <graphics.h> #include <conio.h> //glm数学相关头文件 #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float b = P0.y - a * P0.x; for (int x=P0.x;x<P1.x;x++) { int y = a * x + b; putpixel(x, y, color); } } void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } int main() { initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素 DrawLine2(glm::vec2(100,100), glm::vec2(300,100), RED); _getch(); // 按任意键继续 closegraph(); // 关闭绘图窗口 return 0; }
结果
第二种画线方法比第一种要快一点,不过和第一种一样也不能画垂线。
- 第三种画线
前面两种都是只能从左到右画线,而不能从右到左。要想摆脱这种限制也很简单,只需要交换一下顺序就可以了。
// 引用EasyX图形库头文件 #include <graphics.h> #include <conio.h> //glm数学相关头文件 #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> #include <iostream> void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float b = P0.y - a * P0.x; for (int x=P0.x;x<P1.x;x++) { int y = a * x + b; putpixel(x, y, color); } } void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } void DrawLine3(glm::vec2 P0, glm::vec2 P1, COLORREF color) { if (P0.x > P1.x) { std::swap(P0, P1); } float a = (P1.y - P0.y) / (P1.x - P0.x); float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } int main() { initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素 DrawLine3(glm::vec2(100,100), glm::vec2(300,100), RED); DrawLine3(glm::vec2(500, 200), glm::vec2(100, 200), RED); _getch(); // 按任意键继续 closegraph(); // 关闭绘图窗口 return 0; }
结果
- 第四种画线方法
其实如果想画垂线的话,也很容易,只需要把x和y换一下,只不过又不能画水平线了。
// 引用EasyX图形库头文件 #include <graphics.h> #include <conio.h> //glm数学相关头文件 #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> #include <iostream> void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float b = P0.y - a * P0.x; for (int x=P0.x;x<P1.x;x++) { int y = a * x + b; putpixel(x, y, color); } } void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } void DrawLine3(glm::vec2 P0, glm::vec2 P1, COLORREF color) { if (P0.x > P1.x) { std::swap(P0, P1); } float a = (P1.y - P0.y) / (P1.x - P0.x); float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } void DrawLine4(glm::vec2 P0, glm::vec2 P1, COLORREF color) { if (P0.y > P1.y) { std::swap(P0, P1); } float a = (P1.x - P0.x) / (P1.y - P0.y); float x = P0.x; for (int y = P0.y;y < P1.y;y++) { putpixel(x, y, color); x = x + a; } } int main() { initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素 DrawLine3(glm::vec2(100,100), glm::vec2(500,100), RED); DrawLine4(glm::vec2(100, 100), glm::vec2(100, 500), GREEN); _getch(); // 按任意键继续 closegraph(); // 关闭绘图窗口 return 0; }
结果
- 第五种画线方法
要想画任何斜率的线其实只需要把上面的结合在一起就可以了。
// 引用EasyX图形库头文件 #include <graphics.h> #include <conio.h> //glm数学相关头文件 #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> #include <iostream> void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float b = P0.y - a * P0.x; for (int x=P0.x;x<P1.x;x++) { int y = a * x + b; putpixel(x, y, color); } } void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } void DrawLine3(glm::vec2 P0, glm::vec2 P1, COLORREF color) { if (P0.x > P1.x) { std::swap(P0, P1); } float a = (P1.y - P0.y) / (P1.x - P0.x); float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } void DrawLine4(glm::vec2 P0, glm::vec2 P1, COLORREF color) { if (P0.y > P1.y) { std::swap(P0, P1); } float a = (P1.x - P0.x) / (P1.y - P0.y); float x = P0.x; for (int y = P0.y;y < P1.y;y++) { putpixel(x, y, color); x = x + a; } } void DrawLine5(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float dx = P1.x - P0.x; float dy = P1.y - P0.y; if (glm::abs(dx) > glm::abs(dy)) { if (P0.x > P1.x) { std::swap(P0, P1); } float a = dy / dx; float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } else { if (P0.y > P1.y) { std::swap(P0, P1); } float a = dx / dy; float x = P0.x; for (int y = P0.y;y < P1.y;y++) { putpixel(x, y, color); x = x + a; } } } int main() { initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素 DrawLine5(glm::vec2(100,100), glm::vec2(500,100), RED); DrawLine5(glm::vec2(100, 100), glm::vec2(100, 500), GREEN); _getch(); // 按任意键继续 closegraph(); // 关闭绘图窗口 return 0; }
结果
- 第六种画线方法
这次我们在上一次的基础上添加一个插值函数。
// 引用EasyX图形库头文件 #include <graphics.h> #include <conio.h> //glm数学相关头文件 #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> #include <iostream> #include <vector> void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float b = P0.y - a * P0.x; for (int x=P0.x;x<P1.x;x++) { int y = a * x + b; putpixel(x, y, color); } } void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float a = (P1.y - P0.y) / (P1.x - P0.x); float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } void DrawLine3(glm::vec2 P0, glm::vec2 P1, COLORREF color) { if (P0.x > P1.x) { std::swap(P0, P1); } float a = (P1.y - P0.y) / (P1.x - P0.x); float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } void DrawLine4(glm::vec2 P0, glm::vec2 P1, COLORREF color) { if (P0.y > P1.y) { std::swap(P0, P1); } float a = (P1.x - P0.x) / (P1.y - P0.y); float x = P0.x; for (int y = P0.y;y < P1.y;y++) { putpixel(x, y, color); x = x + a; } } void DrawLine5(glm::vec2 P0, glm::vec2 P1, COLORREF color) { float dx = P1.x - P0.x; float dy = P1.y - P0.y; if (glm::abs(dx) > glm::abs(dy)) { if (P0.x > P1.x) { std::swap(P0, P1); } float a = dy / dx; float y = P0.y; for (int x = P0.x;x < P1.x;x++) { putpixel(x, y, color); y = y + a; } } else { if (P0.y > P1.y) { std::swap(P0, P1); } float a = dx / dy; float x = P0.x; for (int y = P0.y;y < P1.y;y++) { putpixel(x, y, color); x = x + a; } } } std::vector<float> Interpolate(float i0, float d0, float i1, float d1) { std::vector<float> values; if (glm::abs(i0 - i1)<=1e-6) { values.push_back(d0); return values; } float a = (d1 - d0) / (i1 - i0); float d = d0; for (int i = i0;i < i1;i++) { values.push_back(d); d = d + a; } return values; } void DrawLine6(glm::vec2 P0, glm::vec2 P1, COLORREF color) { if (glm::abs(P1.x-P0.x) > glm::abs(P1.y-P0.y)) { if (P0.x > P1.x) { std::swap(P0, P1); } std::vector<float> ys = Interpolate(P0.x,P0.y,P1.x,P1.y); for (int x = P0.x;x < P1.x;x++) { putpixel(x, ys[x-P0.x], color); } } else { if (P0.y > P1.y) { std::swap(P0, P1); } std::vector<float> xs = Interpolate(P0.y, P0.x, P1.y, P1.x); for (int y = P0.y;y < P1.y;y++) { putpixel(xs[y - P0.y], y, color); } } } int main() { initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素 DrawLine6(glm::vec2(100,100), glm::vec2(500,100), RED); DrawLine6(glm::vec2(100, 100), glm::vec2(100, 500), GREEN); _getch(); // 按任意键继续 closegraph(); // 关闭绘图窗口 return 0; }
结果
其实画线的方法有很多,大家有兴趣的话可以去网上找相应的画线算法进行测试。咱们现在已经可以画线了,那么接下来就可以画三角形啦。
如果你遇到了问题可以在评论区告诉我哦
光栅化渲染器:画线欢迎去我的个人网站留言