OpenGL渲染模型 || 3. opengl 将模型成渲染图片

前言

最近项目中需要使用到OpenGL对3D模型进行渲染。
已有数据为:

  • 带纹理的3D模型
  • 模型上的关键点。

需要实现的功能:

  • 读取和保存 带纹理的3D模型、读取模型的关键点
  • 对模型进行渲染,保存设定角度的渲染图片、以及关键点在相同角度的2D坐标
  • 在渲染图片中模型上任意一个2D点,反向计算到该点空间的3D位置

开发环境:

  • VS2015、三方库 OpenGL、OpenCV。

OpenGL渲染模型 || 1. OpenCV、OpenGL的环境配置
OpenGL渲染模型 || 2. 3D模型的读取与保存
OpenGL渲染模型 || 3. opengl 将模型成渲染图片

由于自己是简单应用,所以对其了解重心在实现的过程,以及OpenGL的相关原理。

1 OpenGL渲染设置流程

  • 1 初始化窗口:并设置初始窗口大小、位置、命名。
  • 2 设置光照环境:光照模式、材料属性、光源位置;并开启深度测试、开启剔除操作效果。
  • 3 设置相机参数:涉及到模型矩阵( Model Matrix)、投影矩阵(Projection Matrix)、视口矩阵(View Matrix)的设置
  • 4 清除屏幕,读取模型
  • 5 设置保存渲染窗口为图片的操作(此时,保存好彩色图、深度图、相机三个矩阵,就可以计算 空间3D点与对应的图片2D点 之间的互相映射)
  • 6 开启OpenGL渲染

这个过程跟我们实际拍照很像:

  • 1 确定拍照环境,如位置、大小、命名等;
  • 2 放置光源,确定位置、方向等;
  • 3 放置相机,设置相机内参;
  • 4 放置模型(注意先清除拍照空间里的杂物);
  • 5 给相机选择拍照模式
  • 6 按下快门

2 代码简介

下面实现的要渲染的模型,是带纹理信息的3D模型。


2.1 GLShow.h

该脚本定义了实现渲染模型的类 class GLShow
在这里需要使用到上一章节的 obj的读取中的 “ 类 ModelMesh” 的内容。

#pragma once

#include"meshcnn.h"
#include<GL/glut.h>
#include<GL/freeglut.h>
//#include <opencv2/flann.hpp>


using namespace std;

class GLShow
{
public:
	GLShow();
	~GLShow();

public:
 // OpenGL 初始化
	void glInit();

	// 渲染函数
	static void glDisplay();
	static void glDisplayOnWin();

	void glMainLoop();  // 开启渲染
	static void glStop(); // 结束渲染
	void glExit();    // 退出

	// 设置相机参数
	void glCamView();   // 设置
	void GetCamMatrix(int idx); //获取和保存相机参数

	// 将模型放入到坐标系中
	void initTexture();  
	void renderObj();
	void renderPoint();

	// 虚拟拍照
	void getImage(int idx, bool fortest);//获取彩色图
	void getDepthImage(int idx);//获取深度图
	void getMask(); // 获得渲染的掩码

	void D2toD3(int idx);  // 2D点反投影获取三D点
	void D3toD2(int idx);  // 3D点投影得到的2D点

	static void glKeyboard(unsigned char key, int x, int y);
	static void glMouseFunc(int button, int state, int x, int y);
	static void glMotionFunc(int x, int y);

	bool IsGetRenderImage = true;
	bool isShowColor;

public:

	ModelMesh mesh;
	string ImagePath; //保存渲染的图片路径
	string labelPath; //保存3D点到2D点的文本的路径

	int glWidth;
	int glHeight;
	int num_photo = 2; // 选择两个角度渲染

	vector<cv::Mat> images;
	vector<cv::Mat> images_depth;
	vector<cv::Mat> masks;

	vector<vector<cv::Point3f>> Keypoints2;       ///< 2d关节点坐标(假定4个面)
	vector<vector<cv::Point3f>> Keypoints3;       ///< 3d关节点坐标(假定4个面)
	vector<cv::Point3f> finalKeypoints3;

private:

	GLuint m_textureId;
	static GLShow *gl;           /// 初始化 gl = this,用来调用非静态成员

	double *modelMatrix[2];
	double *projMatrix[2];
	int *viewport[2];

	float scale;
	int posX;                    /// 记录opengl窗口上鼠标X位置
	int posY;                    /// 记录opengl窗口上鼠标Y位置
	static int GLUTbutton[3];    /// 记录鼠标按键事件---左键、中键、右键
	static float rotation[4][4]; /// 模型的旋转矩阵
	static float translate[3];   /// 模型的平移矩阵

};

接下来的就是这些函数的具体实现

2.2 OpenGL的开始及终止

  • 完成 OpenGL渲染的所有设置,然后使用 glMainLoop() 开启OpenGL的渲染。程序就会反复的调用 OpenGL的回调函数
  • 当完成渲染任务后,使用 glStop(),离开OpenGL的 glMainLoop()
  • 想要退出OpenGL的整个环境,使用 glExit()

void GLShow::glMainLoop()
{
	glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
	// 与glutLeaveMainLoop()使用,退出glut但继续执行后面的程序
	glutMainLoop();
}


void GLShow::glStop()
{
	glutHideWindow();
	glutLeaveMainLoop();
}

void GLShow::glExit()
{
	glutExit();
}

2.3 OpenGL的核心渲染流程


2.3.1 部分变量的初始化

# include"GLShow.h"

// 初始化静态成员变量

GLShow * GLShow::gl = 0;
int GLShow::GLUTbutton[3] = { 0,0,0 };
float GLShow::translate[3] = { 0.0,0.0,0.0 };
float GLShow::rotation[4][4] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };

GLShow::GLShow()
{
	gl = this;
	glWidth = 500;
	glHeight = 500;
	ImagePath = "./";
	labelPath = "./";

	images.resize(num_photo);
	images_depth.resize(num_photo);
	masks.resize(num_photo);
	Keypoints2.resize(num_photo);
	Keypoints3.resize(num_photo);

}

GLShow::~GLShow()
{

}


2.3.2 初始化窗口、设置光照环境

【step1】 初始化窗口:并设置初始窗口大小、位置、命名。

void GLShow::glInit()
{
	int argc2 = 2;
	char **argv2 = new char *[2];
	argv2[0] = "OpenGL";
	argv2[1] = "TEST";
	glutInit(&argc2, argv2);

	glutInitWindowPosition(100, 100);   //初始化窗口位置
	glutInitWindowSize(glWidth, glHeight);  //初始化窗口大小
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE); //设定缓冲区类型、颜色模式、深度缓存
	int glWindowIdx = glutCreateWindow("OpenGL Viewer");   //创建窗口并命名

【step2】 设置光照环境:光照模式、材料属性、光源位置;并开启深度测试、开启剔除操作效果。


	// 设置光照模式
	static GLfloat LightAmbient[] = { 0.4, 0.4, 0.4, 1.0 };
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, LightAmbient);  //设置全局环境光
	//glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);  //将无限远的观察点改为局部观察点

	// 设置材料属性
	static GLfloat Matetrial[] = { 0.8, 0.8, 0.8, 0.8 };
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Matetrial);

	// 设置光源,这里只设置了一个光源
	static GLfloat light0Diffuse[] = { 0.9, 0.9, 0.9, 1.0 };
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse);
	static GLfloat light0Position[] = { 0, 0, 1.0, 0.0 };//光源的位置写在lookat之前,光源的位置和相机的位置就绑定在一起
	glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);

	glEnable(GL_DEPTH_TEST); //开启深度测试
	glEnable(GL_CULL_FACE); //开启剔除操作效果

glutMainLoop() 使用之后,会循环运行下面的回调函数。

	// 初始化回调函数
	if (gl->IsGetRenderImage)
	{
		glutDisplayFunc(glDisplay);
	}
	else 
	{
		glutKeyboardFunc(glKeyboard);
		glutMouseFunc(glMouseFunc);
		glutMotionFunc(glMotionFunc);
		glutDisplayFunc(glDisplayOnWin);
	}

}


2.3.3 设置相机参数

【step3】设置相机参数:涉及到模型矩阵(Model Matrix)、投影矩阵(Projection Matrix)、视口矩阵(View Matrix)的设置

void GLShow::glCamView()
{
	cv::Point3f camPosition;             //相机位置
	cv::Point3f camToward(0, 0, -1);     //相机z轴负方向
	camPosition = mesh.center - camToward * 2 * mesh.radius;    //相机位置在z轴负方向,离模型中心的2*处

	//设置模型矩阵 Model Matrix==================================================
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(camPosition.x, camPosition.y, camPosition.z,
		mesh.center.x, mesh.center.y, mesh.center.z, 0.0, 1.0, 0.0); 

	// 设置模型矩阵(Model Matrix),用正交投影=====================================
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	float r = mesh.radius;
	glOrtho(-r, r, -r, r, 0.01*r, 100 * r);  // 正交投影
	//gluPerspective(60, 4 / 3, 0.01*mesh.radius, 100 * mesh.radius);  // 透视投影

	// 设置视口矩阵(View Matrix)。进行视口缩放之后才与mashlab渲染效果一致 ============
	glViewport((glWidth - glHeight) / 2, 0, glHeight, glHeight);

	//上面的显示,模型的成像不会随窗口的大小而改变;下面的方式相反 =====================
	//float s = glHeight / glWidth;
	//glOrtho(-r, r, -r*s, r*s, 0.01*r, 100 * r);
	//glViewport(0, 0, glWidth, glHeight);
}


2.3.4 读取模型

【step4】清除屏幕,读取模型在使用 ModelMesh.readObj 之后,已经将模型的信息获取到。然后需要将这些信息传入到OpenGL的api中。

读取纹理
void GLShow::initTexture()
{
	cv::Mat img = cv::imread(mesh.textureFile, 1);
	int type = img.type();
	cvtColor(img, img, CV_BGR2RGB);
	cv::flip(img, img, 0);
	glEnable(GL_TEXTURE_2D);
	glGenTextures(1, &m_textureId);
	glBindTexture(GL_TEXTURE_2D, m_textureId);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.cols, img.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, img.data);
	glBindTexture(GL_TEXTURE_2D, 0);
}

读取3D模型准备渲染
void GLShow::renderObj()
{
	if (m_textureId == 0)
	{
		initTexture();
	}
	glBindTexture(GL_TEXTURE_2D, m_textureId);
	glBegin(GL_TRIANGLES);

	int n = mesh.tris.size();

	for (int i = 0; i < n; i++) {
		auto v = mesh.tris[i];
		auto n = mesh.faceNrm[i];
		auto t = mesh.textures[i];

		glNormal3f(mesh.vtxNrm[n.x].x, mesh.vtxNrm[n.x].y, mesh.vtxNrm[n.x].z);
		glTexCoord2d(mesh.texcodNrm[t.v1].tx, mesh.texcodNrm[t.v1].ty);
		glVertex3f(mesh.vtx[v.v1].x, mesh.vtx[v.v1].y, mesh.vtx[v.v1].z);

		glNormal3f(mesh.vtxNrm[n.y].x, mesh.vtxNrm[n.y].y, mesh.vtxNrm[n.y].z);
		glTexCoord2d(mesh.texcodNrm[t.v2].tx, mesh.texcodNrm[t.v2].ty);
		glVertex3f(mesh.vtx[v.v2].x, mesh.vtx[v.v2].y, mesh.vtx[v.v2].z);

		glNormal3f(mesh.vtxNrm[n.z].x, mesh.vtxNrm[n.z].y, mesh.vtxNrm[n.z].z);
		glTexCoord2d(mesh.texcodNrm[t.v3].tx, mesh.texcodNrm[t.v3].ty);
		glVertex3f(mesh.vtx[v.v3].x, mesh.vtx[v.v3].y, mesh.vtx[v.v3].z);

	}
	glEnd();
	glBindTexture(GL_TEXTURE_2D, 0);
}

读取3D顶点准备渲染

void GLShow::renderPoint()
{
	glPointSize(10.0f);

	glBegin(GL_POINTS);
	for (int i = 0; i < gl->mesh.keypoint.size(); i++)
	{
		glVertex3f(gl->mesh.keypoint[i].x, gl->mesh.keypoint[i].y, gl->mesh.keypoint[i].z);
	}
	glEnd();

}

2.3.5 设置获取渲染图片模式

【step5】设置保存渲染窗口为图片的操作(此时,保存好彩色图、深度图、相机三个矩阵,就可以计算 空间3D点与对应的图片2D点 之间的互相映射)

在设置好相机参数后,保留当前的三个矩阵

void GLShow::GetCamMatrix(int idx)
{
		double* modelMatrixT = new double[16];
		double* projMatrixT = new double[16];
		int* viewportT = new int[16];
		glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrixT);
		glGetDoublev(GL_PROJECTION_MATRIX, projMatrixT);
		glGetIntegerv(GL_VIEWPORT, viewportT);
		gl->modelMatrix[idx] = modelMatrixT;
		gl->projMatrix[idx] = projMatrixT;
		gl->viewport[idx] = viewportT;
		//cout << "===============渲染" << endl;
		//for (int f = 0; f < 16; f++)
		//{
		//	cout << gl->modelMatrix[idx][f] << " ";
		//}
		//cout << endl;
}

这里的函数 glDisplay(),也就是 glutDisplayFunc() 的回调函数。
在这个函数里面,会调用 glCamView() 以及 renderObj()。由于想要模型的正面和侧面的渲染效果,所以会将旋转角度的操作留在for训练里面。


void GLShow::glDisplay()
{
	//设置相机位置和方向
	gl->glCamView();

	for (int idx = 0; idx < gl->num_photo; idx++) //投影2个面
	{
		if (idx = 0)
		{
			glMatrixMode(GL_MODELVIEW);
			glRotatef(180, 0, 1.0, 0);
		}
		// 保存相机矩阵
		gl->GetCamMatrix(idx);

		//清除屏幕
		glClearColor(0.0 / 255.0, 255.0 / 255.0, 0 / 255.0, 1.0);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		gl->renderObj();
		glutSwapBuffers();
		glutSwapBuffers();  //很奇怪,一定要使用两遍,如果不加这一行代码,总是虚拟拍照获取到上个场景的图片
		gl->getImage(idx, false);
		gl->getDepthImage(idx); // 获取深度图(保存缓存的深度数据)

		// 读取3D关节点进行渲染
		//glClearColor(0 / 255.0, 0 / 255.0, 0 / 255.0, 1.0);
		//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		//gl->renderPoint();
		//glutSwapBuffers();
		//glutSwapBuffers();  
		//gl->getImage(idx, true);

	}
	gl->glStop();  // 自己定义的停止渲染函数,后面介绍到

}

2.4 OpenGL的其他操作


2.4.1 保存图片

从缓存中保存渲染的图片


void GLShow::getImage(int idx, bool fortest)
{
	cv::Mat image = cv::Mat::zeros(glHeight, glWidth, CV_8UC3);
	unsigned char *imageData = new unsigned char[glWidth*glHeight * 3];
	glReadPixels(0, 0, glWidth, glHeight, GL_RGB, GL_UNSIGNED_BYTE, imageData);

	for (int rowGL = 0; rowGL < glHeight; rowGL++)
	{
		for (int col = 0; col < glWidth; col++)
		{
			//opencv的图像坐标的(0,0)在左上角,opengl的(0,0)在左下角
			int rowCV = glHeight - rowGL - 1;
			//颜色OpenCV是BGR,opengl是RGB;
			image.at<cv::Vec3b>(rowCV, col)[0] = imageData[3 * (rowGL*glWidth + col) + 2];
			image.at<cv::Vec3b>(rowCV, col)[1] = imageData[3 * (rowGL*glWidth + col) + 1];
			image.at<cv::Vec3b>(rowCV, col)[2] = imageData[3 * (rowGL*glWidth + col) + 0];
			//cout << float(image.at<cv::Vec3b>(rowCV, col)[2]) << endl;
		}
	}
	if (fortest)
	{
		imwrite(ImagePath + mesh.modelname + string("_") + std::to_string(idx) + string(".png"), image);
		return;
	}
	image.copyTo(images[idx]);
	return;

}

void GLShow::getDepthImage(int idx)
{
	cv::Mat image = cv::Mat::zeros(glHeight, glWidth, CV_32FC1);
	float *sceneDepthBuffer = (float *)malloc(sizeof(float)*glWidth*glHeight);
	glReadPixels(0, 0, glWidth, glHeight, GL_DEPTH_COMPONENT, GL_FLOAT, sceneDepthBuffer);

	for (int rowGL = 0; rowGL<glHeight; rowGL++)
	{
		for (int col = 0; col<glWidth; col++)
		{
			// opencv的图像坐标的(0,0)在左上角, opengl的(0,0)在左下角
			// 颜色opencv是BRG,opengl是RGB 

			int rowCV = glHeight - rowGL - 1;
			image.at<float>(rowCV, col) = sceneDepthBuffer[(rowGL*glWidth + col)];
		}
	}

	image.copyTo(images_depth[idx]);
}


2.4.2 3D到2D的转换

OpenGL的使用过程中,(1) 有时需要将空间的3D点,按照已定的相机参数,投影到2D图片上,并获得其2D坐标;(2) 有时需要对3D模型渲染的2D图上,找到keypoint,然后反算出其对应的3D坐标。

主要是OpenGL的两个api:

  • 3D–>2D:
    gluProject(object_x, object_y, object_z, modelview, projection, viewport, &winX, &winY, &winZ);
  • 2D–>3D:
    gluUnProject((GLdouble)winX, (GLdouble)winY, (GLdouble)winZ,
    modelview, projection, viewport, &object_x, &object_y, &object_z);

void GLShow::D2toD3(int idx)
{
	GLint *viewport = gl->viewport[idx];
	GLdouble *modelview = gl->modelMatrix[idx];
	GLdouble *projection = gl->projMatrix[idx];

	std::vector<cv::Point3f> keypoint_Tmp;
	std::vector<bool> valid;
	for (int j = 0; j < gl->Keypoints2[idx].size(); j++)   // size==18个关节点
	{
		if (gl->Keypoints2[idx][j].x == 0.0f || gl->Keypoints2[idx][j].y == 0.0f)
		{
			keypoint_Tmp.push_back(cv::Point3f(0.0f, 0.0f, 0.0f));
			valid.push_back(false);
			continue;
		}

		GLint winX, winY;
		GLfloat winZ;  /// 深度缓存数据

		// openpose获取的关节点的坐标系(0,0)在左上角,opengl窗口的坐标系(0,0)在左下角
		winX = floor(gl->Keypoints2[idx][j].x);
		winY = floor(glHeight - 1 - gl->Keypoints2[idx][j].y);
		//glReadPixels(winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);  //读取深度缓存值
		winZ = images_depth[idx].at <float>(gl->Keypoints2[idx][j].y, gl->Keypoints2[idx][j].x);
		cout << winZ << endl;

		// 反投影回三维模型
		double object_x, object_y, object_z;
		gluUnProject((GLdouble)winX, (GLdouble)winY, (GLdouble)winZ, 
			modelview, projection, viewport, &object_x, &object_y, &object_z);
		keypoint_Tmp.push_back(cv::Point3f(float(object_x), float(object_y), float(object_z)));
	}
	cout << keypoint_Tmp.size() << endl;
	Keypoints3[idx] = keypoint_Tmp;

}

void GLShow::D3toD2(int idx)
{
	// 获取场景的模型、投影矩阵
	GLint *viewport = gl->viewport[idx];
	GLdouble *modelview = gl->modelMatrix[idx];
	GLdouble *projection = gl->projMatrix[idx];

	ofstream fout1(labelPath + mesh.modelname + string("_") + std::to_string(idx) + string(".txt"));
	for (int i = 0; i < mesh.keypoint.size(); i++)
	{
		GLdouble object_x = GLdouble(mesh.keypoint[i].x);
		GLdouble object_y = GLdouble(mesh.keypoint[i].y);
		GLdouble object_z = GLdouble(mesh.keypoint[i].z);

		GLdouble winX, winY, winZ;
		gluProject(object_x, object_y, object_z, modelview, projection, viewport, &winX, &winY, &winZ);
		//cout << winX << " " << winY << endl;

		int rowCV = (glHeight - winY - 1) + 0.5;
		int colCV = winX + 0.5;

		fout1 << colCV << " " << rowCV  << " " << endl;
	}
	fout1.close();
}

2.5 OpenGL的简单交互

渲染的模型在窗口持续显示,并能够进行交互,这样在代码里需要修改的地方是OpenGL的回调函数。
void GLShow::glInit() 中最后位置的代码。需要执行的是 else 内部的内容。需要定义 glKeyboard、glMouseFunc、glMotionFunc、glDisplayOnWin 四个函数。

	 初始化回调函数
	if (gl->IsGetRenderImage)
	{
		glutDisplayFunc(glDisplay);
	}
	else 
	{
		glutKeyboardFunc(glKeyboard);
		glutMouseFunc(glMouseFunc);
		glutMotionFunc(glMotionFunc);
		glutDisplayFunc(glDisplayOnWin);
	}

}


2.4.1 与鼠标键盘交互的回调函数


void GLShow::glKeyboard(unsigned char key, int x, int y)
{
	switch (key)
	{
	case 'o':
		gl->isShowColor = !gl->isShowColor;
		glutPostRedisplay();
		break;
	}
}

void GLShow::glMouseFunc(int button, int state, int x, int y)
{
	gl->posX = x;
	gl->posY = y;

	cout << "==" << button << endl; // 左键0、中键1、右键2、中上3、中下4
	int b = (button == GLUT_LEFT_BUTTON) ? 0 : ((button == GLUT_MIDDLE_BUTTON) ? 1 : 2);
	GLUTbutton[b] = (state == GLUT_DOWN) ? 1 : 0;
	if (state == GLUT_UP && button == 3)
	{
		gl->scale -= 0.1;
		if (gl->scale <= 0)
		{
			gl->scale = 0;
		}
		rotation[0][0] *= 1 / 1.1f; rotation[1][0] *= 1 / 1.1f; rotation[2][0] *= 1 / 1.1f; rotation[3][0] *= 1 / 1.1f;
		rotation[0][1] *= 1 / 1.1f; rotation[1][1] *= 1 / 1.1f; rotation[2][1] *= 1 / 1.1f; rotation[3][1] *= 1 / 1.1f;
		rotation[0][2] *= 1 / 1.1f; rotation[1][2] *= 1 / 1.1f; rotation[2][2] *= 1 / 1.1f; rotation[3][2] *= 1 / 1.1f;
	}
	else if (state == GLUT_UP && button == 4)
	{
		gl->scale += 0.1;
		rotation[0][0] *= 1.1f; rotation[1][0] *= 1.1f; rotation[2][0] *= 1.1f; rotation[3][0] *= 1.1f;
		rotation[0][1] *= 1.1f; rotation[1][1] *= 1.1f; rotation[2][1] *= 1.1f; rotation[3][1] *= 1.1f;
		rotation[0][2] *= 1.1f; rotation[1][2] *= 1.1f; rotation[2][2] *= 1.1f; rotation[3][2] *= 1.1f;
	}
	glutPostRedisplay();
}

void GLShow::glMotionFunc(int x, int y)
{
	int dx = x - gl->posX;
	int dy = y - gl->posY;
	if (GLUTbutton[0])
	{
		float a = float(dx) / float((gl->glWidth / 4));
		float sa = sin(a);
		float ca = cos(a);
		float row0[4];
		for (int i = 0; i<4; i++) row0[i] = rotation[i][0];
		for (int i = 0; i<4; i++) rotation[i][0] = ca*row0[i] + sa*rotation[i][2];
		for (int i = 0; i<4; i++) rotation[i][2] = -sa*row0[i] + ca*rotation[i][2];

		a = float(dy) / float((gl->glHeight / 4));
		sa = sin(a);
		ca = cos(a);
		float row1[4];
		for (int i = 0; i<4; i++) row1[i] = rotation[i][1];
		for (int i = 0; i<4; i++) rotation[i][1] = ca*row1[i] - sa*rotation[i][2];
		for (int i = 0; i<4; i++) rotation[i][2] = sa*row1[i] + ca*rotation[i][2];
	}
	else if (GLUTbutton[1])
	{
		gl->translate[0] += dx;
		gl->translate[1] += dy;

		for (int i = 0; i<4; i++)     rotation[i][0] += (float(dx) / float(gl->glWidth))*rotation[i][3];
		for (int i = 0; i<4; i++)     rotation[i][1] += (float(dy) / float(gl->glHeight))*rotation[i][3];
		for (int i = 0; i<4; i++)     rotation[i][2] += 0 * rotation[i][3];
	}
	else if (GLUTbutton[2])
	{
		gl->translate[0] += dx;
		gl->translate[1] += dy;
		for (int i = 0; i<4; i++)     rotation[i][0] += (float(dx) / float(gl->glWidth))*rotation[i][3];
		for (int i = 0; i<4; i++)     rotation[i][1] += (float(dy) / float(gl->glHeight))*rotation[i][3];
		for (int i = 0; i<4; i++)     rotation[i][2] += 0 * rotation[i][3];
	}
	if (GLUTbutton[0] || GLUTbutton[1] || GLUTbutton[2]) glutPostRedisplay();
	gl->posX = x;
	gl->posY = y;

	glutPostRedisplay();
}


2.4.2 根据操作更新渲染模型

void GLShow::glDisplayOnWin()
{
	//设置相机=================================================
	gl->glCamView();

	// 获取鼠标、键盘的键入操作 转换的旋转平移矩阵===================
	// 旋转
	glPushMatrix();
	glMatrixMode(GL_MODELVIEW);
	glTranslatef(gl->mesh.center.x, gl->mesh.center.y, gl->mesh.center.z);
	glMultMatrixf(*rotation);
	glTranslatef(-gl->mesh.center.x, -gl->mesh.center.y, -gl->mesh.center.z);
	// 平移
	glMatrixMode(GL_PROJECTION);
	glViewport((gl->glWidth - gl->glHeight) / 2 + gl->translate[0], -gl->translate[1], gl->glHeight, gl->glHeight);
	glPopMatrix();

	//启用半透明效果,渲染模型=====================================
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glClearColor(221.0 / 255.0, 201.0 / 255.0, 194.0 / 255.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	gl->renderObj();

	//glDisable(GL_BLEND);
	//glDepthMask(GL_TRUE); //启用或禁用写入(更新)深度缓冲区

	glutSwapBuffers();

}
  • 11
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值