OpenGL:观察坐标系(模型视图矩阵)、投影变换

观察坐标系参数

在这里插入图片描述

说明:其中观察方向与+Zview相反。

观察平面法向量

  • 观察方向通常沿着Zview轴,因此观察平面,有时也称投影平面一般假设为与该轴垂直。这样,观察平面的方向 及正Zview轴可定义为观察平面法向量N。

  • OpenGL中不提供对观察平面的选择功能。近裁剪平面永远和观察平面重合,因为裁剪窗口永远位于观察体的近平面上。

在这里插入图片描述
在这里插入图片描述

观察向上向量

在这里插入图片描述

世界坐标系到观察坐标第的变换

  • 平移观察坐标原点到世界坐标系原点。
  • 进行旋转,分别让Xview,Yview,Zview轴对应到世界坐标的Xw,Yw,Zw轴。

在OpenGL中指定观察参数(gluLookAt)时,生成一个矩阵并和当前建模观察矩阵合并。同时,该观察矩阵和任何可能已指定的几何变换矩阵相结合。然后将该组合矩阵用于将世界坐标系的对象描述变换到观察坐标系中。

如:

gluLookAt(500, 500, 0, 500, 500, -1, 0, 1, 0);

生成的矩阵为:
在这里插入图片描述

投影变换

观察位置、观察平面(即投影平面)‘、裁剪窗口和裁剪平面都在观察坐标系中指定。

此处使用正投影变换来描述,投影方向平行于Zview轴,正投影的变换公式很简单。如图7.26,
在这里插入图片描述

由于屏幕坐标系经常指定为左手系,因此规范化观察体也…
在这里插入图片描述

二维裁剪窗口到规范化视口的映射

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三维裁剪窗口到规范化

在这里插入图片描述
在这里插入图片描述

如:

glOrtho(-2634.57, 2634.57, -2000, 2000, -2000, 2000);

生成的规范化矩阵如下
在这里插入图片描述

示例代码

#define FREEGLUT_STATIC

#include "gl/freeglut.h"
#include <cmath>
#include <memory>
#include <iostream>
#include <iomanip>
#include <vector>

//4X4矩阵
class Matrix3d
{
public:
	Matrix3d()
	{
		memset(&m_dValues, 0, sizeof(double) * 16);
		for (int i = 0; i < 4; ++i)
		{
			m_dValues[i][i] = 1.0;
		}
	}
	virtual ~Matrix3d(){}

	Matrix3d& postBy(const Matrix3d& mMat)
	{
		for (int i = 0; i < 4; ++i)
		{
			for (int j = 0; j < 4; ++j)
			{
				double dSum = 0;
				for (int c = 0; c < 4; ++c)
				{
					dSum += this->m_dValues[i][c] *
						mMat.m_dValues[c][j];
				}
				this->m_dValues[i][j] = dSum;
			}
		}

		return *this;
	}

	Matrix3d prevBy(const Matrix3d& mMat)
	{
		for (int i = 0; i < 4; ++i)
		{
			for (int j = 0; j < 4; ++j)
			{
				double dSum = 0.0;
				for (int c = 0; c < 4; ++c)
				{
					dSum += mMat.m_dValues[i][c] *
						this->m_dValues[c][j];
				}
				this->m_dValues[i][j] = dSum;
			}
		}

		return *this;
	}



public:
	double m_dValues[4][4];
};

//3维向量
class Vector3d
{
public:
	Vector3d()
	{
		m_x = 0;
		m_y = 0;
		m_z = 0;
	}

	Vector3d(double x, double y, double z)
	{
		m_x = x;
		m_y = y;
		m_z = z;
	}

	virtual ~Vector3d(){}

	//mat * vector
	Vector3d& transformBy(const Matrix3d& mMat )
	{
		m_x = mMat.m_dValues[0][0] * m_x + mMat.m_dValues[0][1] * m_y
			+ mMat.m_dValues[0][2] * m_z + mMat.m_dValues[0][3];

		m_y = mMat.m_dValues[1][0] * m_x + mMat.m_dValues[1][1] * m_y
			+ mMat.m_dValues[1][2] * m_z + mMat.m_dValues[1][3];

		m_z = mMat.m_dValues[2][0] * m_x + mMat.m_dValues[2][1] * m_y
			+ mMat.m_dValues[2][2] * m_z + mMat.m_dValues[2][3];

		return *this;
	}

public:
	double m_x;
	double m_y;
	double m_z;
};





#define MY_PI (3.1415926535f)
GLfloat g_dApectRadio = 1.0;
GLfloat g_CurScale = 1.0;


void GetOpenGLMatrix(GLenum enMatrixEnum, Matrix3d& mRtMat)
{
	float m[16] = { 0 }; //用来保存当前矩阵数据  
	glGetFloatv(enMatrixEnum, m);//观察矩阵的逆矩阵,得到视图矩阵
	for (int i = 0; i < 16; ++i)
	{
		mRtMat.m_dValues[i % 4][i / 4] = m[i];
	}
}

void DisplayMatrix(const Matrix3d& mMat)
{
	for (int i = 0; i < 4; ++i)
	{
		for (int j = 0; j < 4; ++j)
		{
			std::cout << std::setw(12) << std::left << std::setprecision(6)
				<< mMat.m_dValues[i][j];
		}
		std::cout << std::endl;
	}
	std::cout << std::endl << std::endl;
}

void ShowOpenGLMatrix(GLenum enMatrixEnum)
{
	Matrix3d mMat;
	GetOpenGLMatrix(enMatrixEnum, mMat);

	if (enMatrixEnum == GL_MODELVIEW_MATRIX)
	{
		std::cout << "模型视图矩阵(OpenGL采用前乘,坐标点用列向量):" << std::endl;
	}
	else if (enMatrixEnum == GL_PROJECTION_MATRIX)
	{
		std::cout << "投影矩阵(OpenGL采用前乘,坐标点用列向量):" << std::endl;
	}

	DisplayMatrix(mMat);
}

void ShowVectorTransform()
{
	Matrix3d mModelViewMat;
	GetOpenGLMatrix(GL_MODELVIEW_MATRIX, mModelViewMat);

	Matrix3d mProjectMat;
	GetOpenGLMatrix(GL_PROJECTION_MATRIX, mProjectMat);

	std::cout << "mProjectMat * mModelViewMat:" << std::endl;
	Matrix3d mTempMat = mModelViewMat;
	mTempMat.prevBy(mProjectMat);
	DisplayMatrix(mTempMat);

	std::vector<Vector3d> mPoints;
	mPoints.push_back(Vector3d(-2000, -4000, -2000));
	mPoints.push_back(Vector3d(2000, 4000, 2000));

	mPoints.push_back(Vector3d(0, 0, 0));
	mPoints.push_back(Vector3d(1000, 0, 0));
	mPoints.push_back(Vector3d(1000, 1000, 0));
	mPoints.push_back(Vector3d(0, 1000, 0));
	for (auto it = mPoints.begin(); it != mPoints.end(); ++it)
	{
		Vector3d ptTemp = *it;
		std::cout << "point( " << ptTemp.m_x << "," << ptTemp.m_y
			<< "," << ptTemp.m_z << ")" << std::endl;

		ptTemp.transformBy(mProjectMat);
		std::cout << "\tpoint( " << ptTemp.m_x << "," << ptTemp.m_y
			<< "," << ptTemp.m_z << ")" << std::endl;
	}
}


void OnDisplay()
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	double fRange = 2000 * g_CurScale; //正交投影,通过投影矩阵来达到缩放的效果
	if (g_dApectRadio > 1.0)
	{
		glOrtho(-fRange*g_dApectRadio, fRange * g_dApectRadio, -fRange, fRange, -2000, 2000);

		std::cout << "left=" << -fRange*g_dApectRadio
			<< ", right=" << fRange*g_dApectRadio
			<< ", top=" << -fRange
			<< ", bottom=" << fRange
			<< ", zNear=-2000, zFar=2000" << std::endl;
	}
	else
	{
		glOrtho(-fRange, fRange, -fRange / g_dApectRadio, fRange / g_dApectRadio, -2000, 2000);
		std::cout << "left=" << -fRange
			<< ", right=" << fRange
			<< ", top=" << -fRange/g_dApectRadio
			<< ", bottom=" << fRange/g_dApectRadio
			<< ", zNear=-2000, zFar=2000" << std::endl;
	}

	//俯视
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(500, 500, 0, 500, 500, -1, 0, 1, 0);
//	gluLookAt(0, 0, 0, 0, 0, -1, 0, 1, 0);

	ShowOpenGLMatrix(GL_MODELVIEW_MATRIX);
	ShowOpenGLMatrix(GL_PROJECTION_MATRIX);
	ShowVectorTransform();

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glColor3d(1.0, 0.0, 0.0);
	glEnable(GL_POLYGON_SMOOTH);
	glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
	glBegin(GL_QUADS);
	glVertex3d(0, 0, 0);
	glVertex3d(1000, 0, 0);
	glVertex3d(1000, 1000, 0);
	glVertex3d(0, 1000, 0);
	glEnd();


	glFlush();
}


void OnReSize(int w, int h)
{
	glViewport(0, 0, w, h);

	std::cout << "w=" << w << ",h=" << h << std::endl;

	if (h <= 0) h = 1;
	g_dApectRadio = 1.0 * w / h;
}

void OnSpecialKey(GLint key, GLint x, GLint y)
{
	GLfloat fStep = 0.2;
	if (key == GLUT_KEY_UP)
	{
		g_CurScale *= (1.0 + fStep); //缩小

	}
	else if (key == GLUT_KEY_DOWN)
	{
		g_CurScale *= (1.0 - fStep); //放大
	}

	glutPostRedisplay();
}


int main(int argc, char** argv)
{
	glutInit(&argc, argv);

	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE);
	glutInitWindowSize(300, 600);

	double fWidths[2] = { 0 };
	glGetDoublev(GL_LINE_WIDTH_RANGE, fWidths);

	GLfloat fWidthStep = 0;
	glGetFloatv(GL_LINE_WIDTH_GRANULARITY, &fWidthStep);

	glutCreateWindow("Ortho3D");


	glutDisplayFunc(OnDisplay);
	glutReshapeFunc(OnReSize);
	glutSpecialFunc(OnSpecialKey);

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glEnable(GL_DEPTH_TEST);

	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	glDisable(GL_LIGHTING);

	const char* pszVersion = (const char*)glGetString(GL_VERSION);
	if (pszVersion)
	{
		std::cout << pszVersion << std::endl;
	}

	glutMainLoop();

	return 0;
}

版权声明:本文为s634772208原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。