前言
最近项目中需要使用到OpenGL对3D模型进行渲染。
已有数据为:
- 带纹理的3D模型
- 模型上的关键点。
需要实现的功能:
- 读取和保存 带纹理的3D模型、读取模型的关键点
- 对模型进行渲染,保存设定角度的渲染图片、以及关键点在相同角度的2D坐标
- 在渲染图片中模型上任意一个2D点,反向计算到该点空间的3D位置
开发环境:
- VS2015、三方库 OpenGL、OpenCV。
由于自己是简单应用,所以对其了解重心在实现的过程,以及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();
}