直线方程

假设在屏幕上面有两个点,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

这个方程就是大家熟悉的直线方程了。

画线
  1. 第一种画线方法
// 引用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;
}

结果
光栅化渲染器:画线_#include
其实这个方法画出来的线是无法画垂直的线的,因为求a的时候P1.x-P0.x会等于零,除数是不能为零的(因为是浮点数所以可能并不会报错说进行了除零操作)。

  1. 第二种画线方法

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;
}

结果
光栅化渲染器:画线_#include_02
第二种画线方法比第一种要快一点,不过和第一种一样也不能画垂线。

  1. 第三种画线
    前面两种都是只能从左到右画线,而不能从右到左。要想摆脱这种限制也很简单,只需要交换一下顺序就可以了。
// 引用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;
}

结果
光栅化渲染器:画线_斜率_03

  1. 第四种画线方法
    其实如果想画垂线的话,也很容易,只需要把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;
}

结果
光栅化渲染器:画线_#include_04

  1. 第五种画线方法
    要想画任何斜率的线其实只需要把上面的结合在一起就可以了。
// 引用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;
}

结果
光栅化渲染器:画线_ios_05

  1. 第六种画线方法
    这次我们在上一次的基础上添加一个插值函数。
// 引用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;
}

结果
光栅化渲染器:画线_斜率_06

结尾

其实画线的方法有很多,大家有兴趣的话可以去网上找相应的画线算法进行测试。咱们现在已经可以画线了,那么接下来就可以画三角形啦。

如果你遇到了问题可以在评论区告诉我哦
光栅化渲染器:画线欢迎去我的个人网站留言