Games101笔记【透视矩阵】的C语言Demo

Games101中讲到渲染过程中往往会使用透视投影,产生一种“近大远小”的更符合视觉习惯的效果。这篇文章即应用透视矩阵写一个demo,加深理解。

一样的,demo还是使用C实现,使用OpenCV作为辅助以及显示

1、首先、透视矩阵的计算结果如下,具体推导过程可查看Games101系列的课程

double perspMatrix[16] = { 0 };
    double n = 300;
    double f = 800;
    perspMatrix[0] = n;
    perspMatrix[5] = n;
    perspMatrix[10] = n + f;
    perspMatrix[11] = -n * f;
    perspMatrix[14] = 1;

2、demo中画了9个排列正确的长方体,代码如下

// 定义需要展示的数据 -- 1个3D长方体
const double w = 60, l = 80, h = 500;
const double centerPos[3] = { 250, 250, 500 };
const int yCnt = 3, xCnt = 3;
double pts[8][3] =    // 定义原始的点坐标
{
	{-w / 2, -l / 2, -h / 2},
	{w / 2, -l / 2, -h / 2},
	{w / 2, l / 2, -h / 2},
	{-w / 2, l / 2, -h / 2},
	{-w / 2, -l / 2, h / 2},
	{w / 2, -l / 2, h / 2},
	{w / 2, l / 2, h / 2},
	{-w / 2, l / 2, h / 2},
	//

};
int line[12][2] =       // 定义线及点的关系
{
	{0, 1},
	{1, 2},
	{2, 3},
	{3, 0},
	{4, 5},
	{5, 6},
	{6, 7},
	{7, 4},
	{0, 4},
	{1, 5},
	{2, 6},
	{3, 7}
};
unsigned char lineColor[12][3] =
{
	{0, 0, 255},
	{0, 0, 255},
	{0, 0, 255},
	{0, 0, 255},
	{0, 255, 0},
	{0, 255, 0},
	{0, 255, 0},
	{0, 255, 0},
	{255, 0, 0},
	{255, 0, 0},
	{255, 0, 0},
	{255, 0, 0}
};
//====================================
// 定义yCnt x xCnt个长方体
double ptDatas[yCnt * xCnt][8][3];

for (int yy = 0; yy < yCnt; ++yy)
{
	for (int xx = 0; xx < xCnt; ++xx)
	{
		for (int i = 0; i < 8; ++i)
		{
			ptDatas[yy * xCnt + xx][i][0] = pts[i][0] + (-xCnt + 1 + xx * 2) * w;
			ptDatas[yy * xCnt + xx][i][1] = pts[i][1] + (-yCnt + 1 + yy * 2) * l;
			ptDatas[yy * xCnt + xx][i][2] = pts[i][2];
			// 根据旋转矩阵,重新计算点的坐标
			// TransPoint(ptDatas[yy * xCnt + xx][i], rotateM, ptDatas[yy * xCnt + xx][i]);
		}
	}
}

3、画出这9个长方体

unsigned char bkColor[3] = { 212, 148, 0};
_mainMatImg =
	cv::Mat(600, 800, CV_8UC3,
			cv::Scalar(bkColor[0], bkColor[1], bkColor[2]));
//
bool ifPerspective = true; // 是否透视投影,否则即为正交(Orthographic)投影


for (int yy = 0; yy < yCnt; ++yy)
{
	for (int xx = 0; xx < xCnt; ++xx)
	{
		if (ifPerspective)  // 如果使用透视投影
		{
			// 将长方体的8个顶点运用透视矩阵算出新的坐标
			for (int i = 0; i < 8; ++i)
			{
				TransPoint(ptDatas[yy * xCnt + xx][i], perspMatrix, ptDatas[yy * xCnt + xx][i]);
			}
		}

		for (int i = 0; i < 12; ++i)
		{
			cv::line(_mainMatImg,
					 cv::Point(ptDatas[yy * xCnt + xx][line[i][0]][0] + _mainMatImg.cols / 2, ptDatas[yy * xCnt + xx][line[i][0]][1] + _mainMatImg.rows / 2),
					 cv::Point(ptDatas[yy * xCnt + xx][line[i][1]][0] + _mainMatImg.cols / 2, ptDatas[yy * xCnt + xx][line[i][1]][1] + _mainMatImg.rows / 2),
					 cv::Scalar(lineColor[i][0], lineColor[i][1], lineColor[i][2], 255)
					);
		}
	}
}

绘制结果如下,可以看到长方体距离观察更远的平面被缩小了

Games101笔记【透视矩阵】的C语言Demo_第1张图片

以下是不是用透视投影(即平行投影)的结果,因为没有近大远小的效果,所以前后两个面互相遮挡,只能看到9个矩形(长方体的正面)

Games101笔记【透视矩阵】的C语言Demo_第2张图片

可以将其进行旋转(具体怎么对点进行旋转,后续有机会再写一篇文章),效果更为立体一点,对如下

Games101笔记【透视矩阵】的C语言Demo_第3张图片Games101笔记【透视矩阵】的C语言Demo_第4张图片

后记:

以下是将透视变换应用于更复杂的模型中的效果对比,可以看到这个人的脚远离观察者,右边就有近大远小的视觉效果,而左边的如果细看的话,会觉得脚显得有点太大,有点别扭

Games101笔记【透视矩阵】的C语言Demo_第5张图片Games101笔记【透视矩阵】的C语言Demo_第6张图片

 最后,感谢闫令琪和Games101

你可能感兴趣的:(图形学,矩阵,线性代数,opencv,图形渲染)