Qt OpenGL加载OBJ模型_qt opengl obj-程序员宅基地

技术标签: Qt OpenGL 毕设  

在加载OBJ模型文件之前首先要对OBJ文件的内容有所了解,在3d模型网站https://free3d.com/3d-models/3d-printable-obj

随便找了一个模型

它的OBJ文件内容为:

# File exported by ZBrush version 4.4
# www.zbrush.com
#Vertex Count 20545
#UV Vertex Count 15953
#Face Count 20559
#Auto scale x=0.022208 y=0.022208 z=0.022208
#Auto offset x=-0.531189 y=-0.179415 z=0.083041
mtllib stickman2.mtl
usemtl defaultMat
v 14.23160078 -34.12469952 6.2026003
v 14.47159986 -34.17430379 6.32490063
v 14.4308002 -34.00520591 6.28000128
v 14.26249945 -33.91609966 6.17129972

...

vt 0.699 0.3513
vt 0.7029 0.352
vt 0.7295 0.3476

...

f 390/393 398/401 371/374 378/381
f 399/402 401/404 402/405 400/403
f 365/368 372/375 400/403 402/405

...

\n

注意:文件末尾会有一个\n字符。而3dsmax建模出的文件是\r\n..

其中最为关键的为开头为"v","vt","f"的几行数据,“v”代表了顶点数据,“vt”代表贴图坐标也及uv坐标数据,“f”代表了面数据,

其中f 1 2 3 4代表四边形顶点,f 1 2 3代表三角顶点,f 1/1 2/2 3/3 代表顶点索引/纹理索引,f 1//1 2//2 3//3 代表顶点索引//法线索引,f 1/1/1 2/2/2 3/3/3 代表顶点索引/纹理索引/法线索引.索引及代表这是第几个元素,如f 390/393 398/401 371/374 378/381

表示这个面有四个顶点分别是第390个顶点/393个uv ,第398个顶点/401个uv...。

先从最简单的读取单纯的顶点数据开始:

本文在Qt下使用OpenGL,选择使用QOpenGLExtraFunctions类采用OpenGL原生API进行编写。

读取数据部分:

bool ObjLoader::Load(QString fileName, QVector<float> &vPoints)
{
	if (fileName.mid(fileName.lastIndexOf('.')) != ".obj"&&fileName.mid(fileName.lastIndexOf('.')) != ".OBJ")
	{
		qDebug() << "file is not a obj file!";
		return false;
	}
	QFile objfile(fileName);
	if (!objfile.open(QIODevice::ReadOnly))
	{
		qDebug() << "open" << fileName << "failed";
		return false;
	}
	else
	{
		qDebug() << "open" << fileName << "success!";
	}
	QVector<float> vertextPoints, texturePoints;
	QVector<std::tuple< int, int>> facesIndexs;

	while (!objfile.atEnd())
	{
		QByteArray lineData = objfile.readLine();

		lineData = lineData.remove(lineData.count() - 2, 2);
		qDebug() << lineData;

		if (lineData == "")
			continue;
		QList<QByteArray> strValues = lineData.split(' ');
		strValues.removeAll("");
		QString dataType = strValues.takeFirst();
		if (dataType == "v")
		{
			for(int i=0;i<strValues.count();i++)
			{
				if (strValues[i] != "")
					vertextPoints.push_back( strValues[i].toFloat() );
			}

		}

		else if (dataType == "f")
		{
			if (strValues.size() == 4)
			{
				strValues.push_back(strValues.at(0));
				strValues.push_back(strValues.at(2));

			}
			std::transform(strValues.begin(), strValues.end(), std::back_inserter(facesIndexs), [](QByteArray &str) {
				QList<QByteArray> intStr = str.split('/');

				return std::make_tuple(intStr.first().toInt(), intStr.last().toInt());

			});

		}

	}
	if (vertextPoints.count() != 0)
	{
		qDebug() <<"vertpoints: "<< vertextPoints.count();
	}
	else
	{
		qDebug() << "none vert points";
		return false;
	}

	
	if (facesIndexs.count() != 0)
	{
		qDebug() << "facepoints: "<<facesIndexs.count();
	}
	else
	{
		qDebug() << "none faces";
		return false;
	}

	for (auto &verFaceInfo:facesIndexs)
	{
		int vIndex = std::get<0>(verFaceInfo);

		int vPointSizes = vertextPoints.count() / 3;
		//将顶点坐标放入
		vPoints << vertextPoints.at(vIndex * 3 - 3);
		vPoints << vertextPoints.at(vIndex * 3 - 2);
		vPoints << vertextPoints.at(vIndex * 3 - 1);
	}
	vertextPoints.clear();
	facesIndexs.clear();

	objfile.close();
	return true;
}

此代码改自https://blog.csdn.net/wanghualin033/article/details/84642286

整体思路便是 读取文件的每一行,删除掉最后的一个字符'\r\n' 并判断头个字符进行操作。先不管uv坐标.

有一个细节是此OBJ文件里的面有三个点成面的也有四个点成面的,我统一存储为三个点及把四个数据变为六个数据(顶点0 1 2 3 变为顶点 0 1 2 3 0 2)便于绘制.

将数据存储于容器中。在绘制函数中进行调用。

先初始化OpenGL绘制的环境:

void MyWidget::initializeGL()
{

	showNormal();
	setGeometry(0, 0, 800, 600);
	_program.addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, "vsrc.vert");
	_program.addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, "fsrc.frag");
	if (_program.link())
	{
		qDebug() << "link success!";
	}
	else
	{
		qDebug() << "link failed";
	}
	_objLoader.Load("C:/Users/Administrator/Desktop/obj/43n0m0fl66o0-stickman/stickman.OBJ", _vertPoints);
	qDebug() << _vertPoints.count();

	f = QOpenGLContext::currentContext()->extraFunctions();
	f->glGenVertexArrays(1, &VAO);
	f->glGenBuffers(1, &VBO);

	f->glBindBuffer(GL_ARRAY_BUFFER, VBO);
	f->glBufferData(GL_ARRAY_BUFFER, _vertPoints.size()*sizeof(float), &_vertPoints[0], GL_STATIC_DRAW);

	f->glBindVertexArray(VAO);

	f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (GLvoid*)0);
	f->glEnableVertexAttribArray(0);
	f->glBindVertexArray(0);

	vMatrix.lookAt(_cameraLocation, QVector3D(0.0, 0.0, 0.0), QVector3D(0.0, 1.0, 0));
}

此处又遇到了一个问题,    f->glBufferData(GL_ARRAY_BUFFER, _vertPoints.size()*sizeof(float), &_vertPoints[0], GL_STATIC_DRAW);其中的第二个参数 sizeof(vector)是不能返回一个vector的总数据大小的,是一个相当小的定值,以至于在想怎么把数据传进去时出了许多问题:int* arr = new int[10];

  sizeof(arr)返回的是arr[0]的大小而不是指针代表的数组的大小。

而int arr[10] 这种方式sizeof(arr)返回的就是数组的大小。

而第二种方式的数组的大小需要在编译时就确认也就是无法跟着读取数据大小的变化而改变,想了许久之后才发现,原来是sizeof(vector)这个地方出了问题,只传了一点数据进去,也就谈不上能绘制出来了。。改成_vertPoints.size()*sizeof(float)就解决了问题。

最后进行绘制:

void MyWidget::paintGL()
{
	glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
	f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	_program.bind();
	f->glBindVertexArray(VAO);
	_program.setUniformValue("uPMatrix", pMatrix);
	_program.setUniformValue("uVMatrix", vMatrix);
	_program.setUniformValue("uMMatrix", mMatrix);

	glDrawArrays(GL_TRIANGLES, 0, _vertPoints.size()/3);第三个参数是要绘制的顶点数
	f->glBindVertexArray(0);

	update();
}

加以最简单的shader:

fragshader

#version 330

out vec4 fragColor;
 
void main(void)
{
    fragColor = vec4(1.0f,1.0f,1.0f,1.0f);
}

vertexshader 

#version 330
uniform mat4 uPMatrix,uVMatrix,uMMatrix;
layout (location = 0) in vec3 aPosition;

void main(void)
{
    gl_Position = uPMatrix * uVMatrix * uMMatrix * vec4(aPosition,1);
}

出来吧:皮卡丘

感觉有点奇怪。

换种方式绘制试试:

    glDrawArrays(GL_POINTS, 0, _vertPoints.size() );

没想到就是加载个模型也是如此困难重重。。之后加上光影和uv再看看效果吧。

 

 

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_38105943/article/details/86538446

智能推荐

记录Linux安装Oracle19c_debian 安装 oracle19c-程序员宅基地

文章浏览阅读712次。最近单位要求本渣学习服务器脚本编写完成定点市级机构下发的数据库表导入项目服务器数据库,按工作顺序就先打算在自己笔记本电脑上通过虚拟机来模拟生产环境,部署虚拟环境后安装Linux版本Oracle19c数据库。经过数天研究终完成安装,记录如下。安装准备:1、虚拟软件 Oracle VM VirtualBox2、镜像 CentOS-7-x86_64-Minimal-2009.iso3、Xshell 7.04、Xftp 7.05、Xmanager Enterprise 56、LINUX._debian 安装 oracle19c

Halcon分类器之高斯混合模型分类器_训练高斯混合模型分类器-程序员宅基地

文章浏览阅读2k次,点赞2次,收藏20次。Halcon分类器示例自我理解看了很多网上的例子,总有一种纸上得来终觉浅,绝知此事要躬行的感觉。说干就干,将Halcon自带分类器例子classify_metal_parts.hdev按照自己的理解重新写一遍,示例中的分类器是MLP(多层感知机),我将它改变为GMM(高斯混合模型)。希望可以帮助刚入门的同学学习理解,大神请绕路吧,当然也喜欢各位看官帮我找出不足之处,共同进步。谢谢!分类效果如图..._训练高斯混合模型分类器

Office转PDF工具类_"officetopdf.wordtopdf(\"d:\\\\1234.doc\", \"d:\\\-程序员宅基地

文章浏览阅读819次。使用Jacob转换office文件,Jacob.dll文件需要放到jdk\bin目录下Jacob.dll文件下载地址https://download.csdn.net/download/zss0101/10546500package com.zss.util;import java.io.File;import com.jacob.activeX.ActiveXComponent;..._"officetopdf.wordtopdf(\"d:\\\\1234.doc\", \"d:\\\\1234.pdf\");"

redis实现队列_redistemplate convertandsend方法实现队列-程序员宅基地

文章浏览阅读1k次,点赞30次,收藏30次。上面的例子我们已经了一个简易的消息队列。我们继续思考一个现实的场景,假定这些是一些游戏商品,它需要添加"延迟销售"特性,在未来某个时候才可以开始处理这些游戏商品数据。那么要实现这个延迟的特性,我们需要修改现有队列的实现。在消息数据的信息中包含延迟处理消息的执行时间,如果工作进程发现消息的执行时间还没到,那么它将会在短暂的等待之后重新把消息数据推入队列中。(延迟发送消息)_redistemplate convertandsend方法实现队列

java基础-程序员宅基地

文章浏览阅读287次,点赞5次,收藏5次。java基础篇

使用gparted对linux根目录扩容(windows+linux双系统)_双系统linux扩容-程序员宅基地

文章浏览阅读298次。linux扩容根目录与/home_双系统linux扩容

随便推点

Python使用pika调用RabbitMQ_python pika 通过主机名称来访问mq-程序员宅基地

文章浏览阅读388次。定义RabbitMQ类import jsonimport osimport sysimport pikafrom Data import Datafrom MongoDB import MongoDBfrom constants import *class RabbitMQ: def __init__(self, queue_name): """ 初始化队列对象 :param queue_name: 队列名称 "_python pika 通过主机名称来访问mq

Python利用openpyxl处理excel文件_在 python 中可以通過 openpyxl 套件來很好的操作 excel 讀寫-程序员宅基地

文章浏览阅读568次。**openpyxl简介**openpyxl是一个开源项目,openpyxl模块是一个读写Excel 2010文档的Python库,如果要处理更早格式的Excel文档,需要用到其它库(如:xlrd、xlwt等),这是openpyxl比较其他模块的不足之处。openpyxl是一款比较综合的工具,不仅能够同时读取和修改Excel文档,而且可以对Excel文件内单元格进行详细设置,包括单元格样式等内容,甚至还支持图表插入、打印设置等内容,使用openpyxl可以读写xltm, xltx, xlsm, xls_在 python 中可以通過 openpyxl 套件來很好的操作 excel 讀寫

Unity判断某个物体是否在某个规定的区域_unity判断物体在范围内-程序员宅基地

文章浏览阅读1.4w次,点赞7次,收藏56次。Unity自带的两种写法:①物体的位置是否在某个区域内Vector3 pos = someRenderer.transform.position;Bounds bounds = myBoxCollider.bounds;bool rendererIsInsideTheBox = bounds.Contains(pos);②物体的矩形与区域的矩形是否交叉Bounds rendererBo..._unity判断物体在范围内

[深度学习] 使用深度学习开发的循线小车-程序员宅基地

文章浏览阅读295次,点赞6次,收藏4次。报错:Unable to find image 'openexplorer/ai_toolchain_centos_7_xj3:v2.3.3' locally。报错: ./best_line_follower_model_xy.pth cannot be opened。可以看到生成的文件 best_line_follower_model_xy.pth。报错:Module onnx is not installed!安装onox,onnxruntime。这是由于没有文件夹的写权限。

MDB-RS232测试NAYAX的VPOS刷卡器注意事项-程序员宅基地

文章浏览阅读393次,点赞10次,收藏8次。MDB-RS232测试NAYAX的VPOS非现金MDB协议刷卡器注意事项

Pytorch和Tensorflow,谁会笑到最后?-程序员宅基地

文章浏览阅读2.5k次。作者 |土豆变成泥来源 |知秋路(ID:gh_4a538bd95663)【导读】作为谷歌tensorflow某项目的Contributor,已经迅速弃坑转向Pytorch。目前Ten..._pytorch与tensorflow