OpenGL & GLSL训练(八)

Exercise 8:三维模型的载入


前言

第八次作业了,胜利就在眼前!


一、ply文件

先上一下我这次作业用的ply模型的部分

ply
format ascii 1.0
comment - RG built normal additions
element vertex 35947
property float x
property float y
property float z
property float nx
property float ny
property float nz
element face 69451
property list uchar int vertex_indices
end_header
-0.037830 0.127940 0.004475 0.196148 0.972595 -0.124835
-0.044779 0.128887 0.001905 0.210776 0.951005 -0.226191
-0.068010 0.151244 0.037195 0.052961 0.802618 0.594137
-0.002287 0.130150 0.023220 -0.053027 0.970576 0.234886
...
...
3 34930 34929 34800
3 34801 34930 34800
3 35059 35058 34929
3 34930 35059 34929
3 35188 35187 35058
3 35059 35188 35058
3 22321 22225 22322
3 34780 34909 17277
3 17346 34909 17279
3 34909 17346 17277
3 17277 17346 17345

从第一行到end_header为止,都是ply文件的头部部分,其中较为重要的是element后面的部分。其中vertex 35947表示该模型一共有35947个顶点,face 69451表示该模型共有69451个面。property里的属性用于说明接下来的数据部分都含有什么数据、是什么格式,比如这里的vertex数据每6个float格式一行,前三个为顶点的位置,后三个则为对应的法线方向。face数据是对应的顶点索引,用于描述哪几个顶点绘制成一个面。下面是对应的读取文件。

void LoadPly(const char* path)
{
	fstream fopen;
	fopen.open(path, ios::in);
	string filein;
	while (!fopen.eof())
	{
		fopen >> filein;
		if (filein == "ply");
		else if (filein == "comment")
		{
			getline(fopen, filein, '\n');
		}
		else if (filein == "forment")
		{
			getline(fopen, filein, '\n');
		}
		else if (filein == "element")
		{
			fopen >> filein;
			if (filein == "vertex")
			{
				fopen >> vertexNum;
				vertexNum *= 6;
				vertex = new GLfloat[vertexNum];
				getline(fopen, filein, '\n');
			}
			else if (filein == "face")
			{
				fopen >> faceNum;
				faceNum *= 3;
				faces = new GLuint[faceNum];
				getline(fopen, filein, '\n');
			}
			else
			{
				getline(fopen, filein, '\n');
			}
		}
		else if (filein == "property")
		{
			getline(fopen, filein, '\n');
		}
		else if (filein == "end_header")
		{
			getline(fopen, filein, '\n');
			for (int i = 0,j = 0; i < vertexNum;)
			{
				fopen >> vertex[i++];
				fopen >> vertex[i++];
				fopen >> vertex[i++];
				fopen >> vertex[i++];
				fopen >> vertex[i++];
				fopen >> vertex[i++];
			}
			int count = 3;
			for (int i = 0; i < faceNum;)
			{
				fopen >> count;
				fopen >> faces[i++];
				fopen >> faces[i++];
				fopen >> faces[i++];
			}
			break;
		}
	}
	fopen.close();
}

二、Vertex Buffer Object的使用

读取到了ply文件的数据,接下来要做的就是将它绘制在屏幕上,这里采用VBO的方法绘制

1.VBO(Vertex Buffer Object)

VBO可以将顶点数组复制到一块顶点缓冲中,供OpenGL使用

glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);    //绑定该VBO,对应格式为GL_ARRAY_BUFFER
glBufferData(GL_ARRAY_BUFFER, sizeof(arr), arr, GL_STATIC_DRAW);    //将数组复制到顶点缓冲中

在完成复制之后,还要告诉OpenGL该如何应用该数组中的顶点数据

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), 0);
glNormalPointer(GL_FLOAT, 6 * sizeof(GLfloat), (GLubyte*)NULL + 3 * sizeof(GLfloat));
glEnableClientState(GL_VERTEX_ARRAY);    //启用GL_VERTEX_ARRAY
glEnableClientState(GL_NORMAL_ARRAY);    //启用GL_NORMAL_ARRAY

glVertexAttribPointer()函数告诉OpenGL数组中的顶点属性,对应参数如下:

1.指定顶点属性位置,和顶点着色器相关

2.指定顶点的属性大小,这里有xyz三个属性

3.顶点的数据类型

4.是否要进行归一化

5.顶点属性之间的间隔(如果数据为紧密分布,则可填默认值0)

6.初始数据的偏移量,这里为0

glNormalPointer()函数告诉OpenGL数组中的法线属性,对应参数如下:

1.数据类型

2.法线属性之间的间隔

3.初始数据的偏移量

2.EBO(Element Buffer Object)

EBO用于复制并储存顶点位置的索引

glGenBuffers(1, &eboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*faceNum, faces, GL_STATIC_DRAW);

3.VAO (Vertex Arrary Object)

VAO相当于多个VBO的引用,便于绘制图形时的使用

glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);

注意VAO的绑定要放在VBO前,在解除该VAO的绑定之前所绑定的VBO都视为可被该VAO引用

4.绘制的使用

这里的绘制就非常的简便,如下所示

glBindVertexArray(vaoId);
glDrawElements(GL_TRIANGLES, faceNum, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);

总结

惯例放出这次的全部代码及生成图

#include<GL/glew.h>
#include<GL/freeglut.h>
#include<cstdio>
#include<cstdlib>
#include <fstream>
#include <string>
using namespace std;

GLint Height = 600;
GLint Width = 800;
GLfloat LightPosition0[4] = { 0,2,2,0 };

int oldX, oldY;
float rotateX, rotateY;

bool rotated = false;
GLuint shaderProgram;
GLuint vboId;
GLuint vaoId;
GLuint eboId;
int vertexNum;
int faceNum;
GLfloat* vertex;
GLfloat* normal;
GLuint* faces;
struct camer {
	float eyePos[3] = { 0,0,0.2f };
	float lookAt[3] = { 0,0,-1 };
	float upDir[3] = { 0,1,0 };
};

camer ca;
float aspect;
void Lighting();
void createVBO();
float offsetz;
void LoadShader(const char* vertName, const char* fragName, GLuint& shader);
void init()
{
	glClearColor(0.5, 0.5, 0.5, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);			//开启深度测试
	createVBO();
	Lighting();
	LoadShader("ppixel.vert", "ppixel.frag", shaderProgram);
}
const char* getFileData(const char* path)
{
	FILE* infile;
	fopen_s(&infile, path, "rb");
	if (!infile)
	{
		return NULL;
	}
	fseek(infile, 0, SEEK_END);
	int len = ftell(infile);
	fseek(infile, 0, SEEK_SET);

	char* source = new char[len+1];
	fread(source, 1, len, infile);
	source[len] = 0;
	fclose(infile);
	return source;
}
void LoadShader(const char* vertName, const char* fragName, GLuint& shader)
{
	const char* vertShaderSoucre = getFileData(vertName);
	const char* fragShaderSource = getFileData(fragName);

	GLuint vertShader = glCreateShader(GL_VERTEX_SHADER);
	GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);

	glShaderSource(vertShader, 1, &vertShaderSoucre, NULL);
	glShaderSource(fragShader, 1, &fragShaderSource, NULL);

	glCompileShader(vertShader);
	GLint ret;
	glGetShaderiv(vertShader, GL_COMPILE_STATUS, &ret);
	if (ret == 0)
	{
		GLchar log[1024];
		glGetShaderInfoLog(vertShader, sizeof(log), NULL, log);
		printf( "shader编译失败:%s\n", log);
	}
	glCompileShader(fragShader);
	glGetShaderiv(fragShader, GL_COMPILE_STATUS, &ret);
	if (ret == 0)
	{
		GLchar log[1024];
		glGetShaderInfoLog(fragShader, sizeof(log), NULL, log);
		printf("shader编译失败:%s\n", log);
	}
	shader = glCreateProgram();

	glAttachShader(shader, vertShader);
	glAttachShader(shader, fragShader);
	glLinkProgram(shader);
}
void createVBO()
{

	//创建VAO对象
	glGenVertexArrays(1, &vaoId);
	glBindVertexArray(vaoId);

	//创建VBO对象,把顶点数组复制到一个顶点缓冲中,供OpenGL使用
	glGenBuffers(1, &vboId);
	glBindBuffer(GL_ARRAY_BUFFER, vboId);
	glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*vertexNum, vertex, GL_STATIC_DRAW);
	
	//创建EBO对象	
	glGenBuffers(1, &eboId);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
	//传入EBO数据
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*faceNum, faces, GL_STATIC_DRAW);

	//解释顶点数据方式
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), 0);
	glNormalPointer(GL_FLOAT, 6 * sizeof(GLfloat), (GLubyte*)NULL + 3 * sizeof(GLfloat));
	glEnableVertexAttribArray(0);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	//解绑VAO
	glBindVertexArray(0);
	//解绑EBO
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	//解绑VBO
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void LoadPly(const char* path)
{
	fstream fopen;
	fopen.open(path, ios::in);
	string filein;
	while (!fopen.eof())
	{
		fopen >> filein;
		if (filein == "ply");
		else if (filein == "comment")
		{
			getline(fopen, filein, '\n');
		}
		else if (filein == "forment")
		{
			getline(fopen, filein, '\n');
		}
		else if (filein == "element")
		{
			fopen >> filein;
			if (filein == "vertex")
			{
				fopen >> vertexNum;
				vertexNum *= 6;
				vertex = new GLfloat[vertexNum];
				getline(fopen, filein, '\n');
			}
			else if (filein == "face")
			{
				fopen >> faceNum;
				faceNum *= 3;
				faces = new GLuint[faceNum];
				getline(fopen, filein, '\n');
			}
			else
			{
				getline(fopen, filein, '\n');
			}
		}
		else if (filein == "property")
		{
			getline(fopen, filein, '\n');
		}
		else if (filein == "end_header")
		{
			getline(fopen, filein, '\n');
			for (int i = 0,j = 0; i < vertexNum;)
			{
				fopen >> vertex[i++];
				fopen >> vertex[i++];
				fopen >> vertex[i++];
				fopen >> vertex[i++];
				fopen >> vertex[i++];
				fopen >> vertex[i++];
			}
			int count = 3;
			for (int i = 0; i < faceNum;)
			{
				fopen >> count;
				fopen >> faces[i++];
				fopen >> faces[i++];
				fopen >> faces[i++];
			}
			break;
		}
	}
	fopen.close();
}
void DrawScene()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	gluLookAt(ca.eyePos[0], ca.eyePos[1], ca.eyePos[2], ca.lookAt[0], ca.lookAt[1], ca.lookAt[2], ca.upDir[0], ca.upDir[1], ca.upDir[2]);
	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition0);
	glPushMatrix();
	glTranslatef(-0.1f, 0, -3.0f);
	glRotatef(rotateX, 0, 1, 0);
	glRotatef(rotateY, 1, 0, 0);
	glBindVertexArray(vaoId);
	glUseProgram(0);
	glDrawElements(GL_TRIANGLES, faceNum, GL_UNSIGNED_INT, 0);
	glBindVertexArray(0);
	glPopMatrix();
	glPushMatrix();
	glTranslatef(0.1f, 0, -3.0f);
	glRotatef(rotateX, 0, 1, 0);
	glRotatef(rotateY, 1, 0, 0);
	glBindVertexArray(vaoId);
	glUseProgram(shaderProgram);
	glDrawElements(GL_TRIANGLES, faceNum, GL_UNSIGNED_INT, 0);
	glBindVertexArray(0);
	glPopMatrix();
}

void display()
{
	DrawScene();

	glutSwapBuffers();
}


void Lighting()
{
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	GLfloat AmbientLight0[4] = { 1.0, 1.0, 1.0, 1.0 };
	glLightfv(GL_LIGHT0, GL_AMBIENT, AmbientLight0);
	GLfloat DiffuseLight0[4] = { 1.0, 1.0, 1.0, 1.0 };
	glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight0);
	GLfloat SpecularLight0[4] = { 1.0,1.0,1.0,1.0 };
	glLightfv(GL_LIGHT0, GL_SPECULAR, SpecularLight0);

	//glEnable(GL_LIGHT1);
	GLfloat AmbientLight1[4] = { 0.0, 1.0, 0.0, 1.0 };
	glLightfv(GL_LIGHT1, GL_AMBIENT, AmbientLight1);
	GLfloat DiffuseLight1[4] = { 0.0, 1.0, 0.0, 1.0 };
	glLightfv(GL_LIGHT1, GL_DIFFUSE, DiffuseLight1);
	GLfloat SpecularLight1[4] = { 0.0,1.0,0.0,1.0 };
	glLightfv(GL_LIGHT1, GL_SPECULAR, SpecularLight1);


	//glEnable(GL_LIGHT2);
	GLfloat AmbientLight2[4] = { 1.0, 0.0, 0.0, 1.0 };
	glLightfv(GL_LIGHT2, GL_AMBIENT, AmbientLight2);
	GLfloat DiffuseLight2[4] = { 1.0, 0.0, 0.0, 1.0 };
	glLightfv(GL_LIGHT2, GL_DIFFUSE, DiffuseLight2);
	GLfloat SpecularLight2[4] = { 1.0,0.0,0.0,1.0 };
	glLightfv(GL_LIGHT2, GL_SPECULAR, SpecularLight2);

}
void Idle()
{
	glutPostRedisplay();
}
void reshape(int w, int h)
{
	aspect = (float)w / ((h) ? h : 1);//平截头体的纵横比,也就是宽度除以高度,(h)?h:1意思是若h=0,则h=1
	Height = h;
	Width = w;
	glViewport(0, 0, w, h);
	//进行投影变换前调用下面两个函数,接下来的变换函数将影响投影矩阵
	//在窗口改变函数reshape中先用glMatrixMode(GL_PROJECTION)定义视锥,再用glMatrixMode(GL_MODELVIEW)改为模型变换
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(10, aspect, 1.0f, 100.0f);//
	glViewport(0, 0, w, h);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}
void MouseFunc(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		rotated = true;
		oldX = x;
		oldY = y;
	}
	else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
	{
		rotated = false;
	}

}

void MouseMove(int x, int y)
{
	if (rotated)
	{
		rotateX += x - oldX;
		rotateY += y - oldY;
		oldX = x;
		oldY = y;
	}

}

void KeyBoard(unsigned char button, int x, int y)
{

}
int main(int argc, char** argv)
{

	LoadPly("tetrahedron.ply");
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(Width, Height);
	glutCreateWindow("Test");
	glewInit(); //如果使用FBO的话,此句不能缺少
	init();

	glutDisplayFunc(display);            //窗口绘制
	glutIdleFunc(Idle);
	glutReshapeFunc(reshape);
	glutMouseFunc(MouseFunc);
	glutMotionFunc(MouseMove);
	glutKeyboardFunc(KeyBoard);

	glutMainLoop();

	return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值