IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> C++/OpenGL 入门(18):读取obj文件并贴图 -> 正文阅读

[游戏开发]C++/OpenGL 入门(18):读取obj文件并贴图

  1. 来源:《Computer Graphics Programming in OpenGL Using C++ 》by V Scott
    Gordon John L Clevenger
  2. 内容:程序6.3 Simple (Limited) OBJ Loader 简单的obj文件读取器,书P152页,PDF171/403

结果

生成:

在这里插入图片描述

读取:

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

笔记

obj文件的格式介绍

  • 加载外部绘图的模型:复杂的3D模型一般是由建模软件产生的。3D模型的格式 .obj, .3ds, .ply, .mesh, 其中最简单是的obj文件。
  • Obj文件比较简单,我们程序读取的时候也较为容易。Obj文件中,确定了点几何信息,纹理坐标,法向量和其他信息,当然也有一些限制,比如obj文件无法确定模型的运动。
    Obj文件中,开头是一系列的特征标签,提示存储的是什么样的数据。一些常用的标签有:
    V——顶点几何信息
    Vt——纹理坐标
    Vn——顶点法向量
    F——face 面(三角形的三个顶点)
  • 其他的标签可能存储的是对象的名字,所用的材料,曲率curves, 阴影 shadows和其他的细节。
    我们将讨论限制在以上四个标签,这足够展示一系列复杂模型。
    假设用Blender软件建立一个四面体,然后选择.obj格式导出文件包括纹理坐标和顶点法向量
  • 在obj文件用txt文件打开,头几行**“#“**是注释,编译器忽略注释的这几行。 “o“ 表示obj文件的名字,编译器也需要忽略
    s“开头的语句,说明这些面不该被平滑,编译器也会忽略该行
    v“开头的语句,表示四面体的5个点的相对于原点(0,0,0)的X,Y,Z坐标,原点在四面体的中心 以下是书中的例子给的obj文件
    在这里插入图片描述
  • vt“开头的红色行代表的是不同的纹理坐标,纹理坐标的数量比顶点数量多,是因为一个顶点可能参与多个三角形,在这些不同三角形中有不同的纹理坐标。
  • vn“绿色行,表示不同的法向量,法向量的行数通常比顶点的行数多,是因为有一些顶点同时在不同三角形中。
  • f“紫色行,表示三角形,每个面有三个元素,是用/分隔,比如下图:
  • 暗示说,第2.5.3的顶点组成一个三角形
    每一组的第二个数 : 7 8 9 是 vt中的,也就是纹理坐标组成的三角形
    每一组的第三个数 3 3 3 是vn 中的,也就是第三行,这三个点组成的面有一样的法向量
  • 如果obj文件不包括纹理坐标和法向量,格式为: f 2 5 3
    如果obj文件有纹理坐标,没有法向量,格式为: f 2/7 5/8 3/9
    如果obj文件没有纹理坐标,有法向量,格式为: f 2//3 5//3 3//3
  • 可以根据标签 v,vt,vn,f自己写一个obj文件

Obj文件的限制
① 只支持全部表面都是三角形的模型,也就是说顶点位置,纹理坐标和法向量必须全部说明,并且以这样的形式: f #/#/# #/#/# #/#/#
② 材料的标签被忽略后,纹理贴图的做法必须用第五章的方法完成
③ 只有全部是三角形网格组成的obj模型才能支持,其余复杂网格模型的obj文件不支持
④ 每一行的元素由空格分隔开

  • 索引indexing对obj有另外的限制:
    obj文件中两个不同的“face”行可能包含指向相同顶点但不同纹理坐标的索引。但是,OpenGL的索引机制只能指向一个特定的顶点及其属性。使用OpenGL索引将需要确保一个面的vvtvn值的整个组合都保存在各自的数组中。
  • 一种更简单,但效率较低的替代方法是为每个三角形面入口创建一个新顶点。尽管使用OpenGL索引具有节省空间的优势,但为了清晰起见,我们选择了这种更简单的方法。
  • ModelImporter 类中有一个函数parseOBJ(),能够逐行读取obj文件,分别处理四种情况 v, vt, vn, and f。在每一种情况中,每一行的数据都会被读取。首先用 erase() 函数跳过 v, vt, vn, and f的开头,然后用stringstream 类中的“>>” 符号提取接下来的每一个值,最后储存在float的向量vector中。当处理面 f 的时候,顶点是用之前创建的顶点位置,纹理坐标和法向量处理的。
  • ModelImporter 类是存储在ImportedModel class的类中,是通过将点存储在2维 vec2和3维vec3向量中来加载和读取obj文件的点信息。ModelImporter 类和ImportedModel类都在GLM类当中,用来存储顶点信息,纹理坐标和法向量。ImportedModel类中的处理器能够使得C++/OpenGL能够读取模型,就像第五章建立sphere和圆环torus的类一样。
  • 接下来将要使用 ModelImporter and ImportedModel类进行加载obj文件,将其中的顶点信息转入一系列的VBO中为后续渲染。

安装 Blender

在这里插入图片描述
软件界面中有新手教程自带正方体,右上角“文件”->“导出”->“obj”
在这里插入图片描述
在这里插入图片描述
在obj导出弹窗,选择存储位置和存储名称,选择包括在内的内容,包括顶点,法线和三角面
在这里插入图片描述
然后在相应位置会生成obj文件,可以右键选择用记事本打开
在这里插入图片描述

调试中的错误与解决办法

  • 当把 obj 文件包含在项目中,运行的时候会报错:LNK1107 文件无效或损坏:无法在0x3E2处读取
    解决方法是:将obj文件右键,选择“从项目中排除”,这样运行就不报错了,但是运行时无法出现四面体的窗口,只出现了终端
    在这里插入图片描述
    在这里插入图片描述
    将obj排除在项目外后,能够成功生成exe,但是并无弹窗
    在这里插入图片描述
    将6-3 cube1.obj排除在项目外后,出现错误:
    Debug Assertopm Failed!
    Expression:vector subscript out of range

    在这里插入图片描述

查找错误得方法:通过用 print(“成功运行\n xxx语句\n”)放在要检测的函数后面,运行,看弹出的终端窗口是否出现这句话,如果出现,说明被检测的函数可以正常运行,如果没有出现,就说明被检测函数有误,需要跳到函数定义中,逐句再查看。
通过逐一观察,发现:numObjVertices 这个值没有正常获取数值,还是0的状态,导致后面的遍历无法进行。同时,i < numObjVertices-1 应该改为 i < numObjVertices

完整代码

一共需要 个文件,分别是:
① 220302 6.3 OBJLoader.cpp 主程序
② 6.3 ImportedModel.cpp 一个类,用于读取obj文件
③ 6.3 ImportedModel.h 类的声明
④ 5.2 Utils.cpp 一些调用函数,比如检测错误的函数
⑤ 5.2 Utils.h 调用函数声明
⑥ 5.2 vertShader.glsl 点着色器,确定3D点坐标
⑦ 5.2 fragShader.glsl 片着色器,确定每个点的颜色
⑧ 6.3 cube1.obj obj文件,这里用的是Blender软件生成的
⑨ 6.1 earth.jpg 一张图片

① 220302 6.3 OBJLoader.cpp 主程序

#include <string>
#include <iostream>
#include <fstream>
#include <cmath>


#include "glm\glm.hpp"
#include "glm\gtc\type_ptr.hpp"
#include "glm\gtc\matrix_transform.hpp"
#include "Utils\6.3 ImportedModel.h"
#include "Utils\5.2 Utils.h"
using namespace std;
#define numVAOs 1
#define numVBOs 3
float cameraX, cameraY, cameraZ;
float pyrLocX, pyrLocY, pyrLocZ;
GLuint renderingProgram;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
GLuint brickTexture; // 纹理图片ID

// allocate variables used in display() function, 
// so that they won’t need to be allocated during rendering
GLuint mvLoc, projLoc;
int width, height;
float aspect;
glm::mat4 pMat, vMat, mMat, mvMat;

ImportedModel myModel("add/6.3 cube1.obj"); // in top-level declarations




void setupVertices(void) {
	std::vector<glm::vec3> vert = myModel.getVertices();
	std::vector<glm::vec2> tex = myModel.getTextureCoords();
	std::vector<glm::vec3> norm = myModel.getNormals();
	int numObjVertices = myModel.getNumVertices(); // 出错,这一行的值竟然是0
	std::vector<float> pvalues; // vertex positions
	std::vector<float> tvalues; // texture coordinates
	std::vector<float> nvalues; // normal vectors
	
	for (int i = 0; i < numObjVertices; i++) {
		
		pvalues.push_back((vert[i]).x);
		pvalues.push_back((vert[i]).y);
		pvalues.push_back((vert[i]).z);
		tvalues.push_back((tex[i]).s);
		tvalues.push_back((tex[i]).t);
		nvalues.push_back((norm[i]).x);
		nvalues.push_back((norm[i]).y);
		nvalues.push_back((norm[i]).z);
	}
	glGenVertexArrays(1, vao);
	glBindVertexArray(vao[0]);
	glGenBuffers(numVBOs, vbo);
	// VBO for vertex locations
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, pvalues.size() * 4, &pvalues[0], GL_STATIC_DRAW);
	// VBO for texture coordinates
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[0], GL_STATIC_DRAW);
	// VBO for normal vectors
	glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
	glBufferData(GL_ARRAY_BUFFER, nvalues.size() * 4, &nvalues[0], GL_STATIC_DRAW);
}

void init(GLFWwindow* window) {


	renderingProgram = createShaderProgram("add/5.2 vertShader.glsl", "add/5.2 fragShader.glsl");
	cameraX = -3.0f; cameraY = 4.0f; cameraZ = 15.0f;
	pyrLocX = 0.0f; pyrLocY = 0.0f; pyrLocZ = 0.0f; // shift down Y to reveal perspective
	
	setupVertices();
	
	brickTexture = loadTexture("add/6.1 earth.jpg"); // 加载纹理的图片
}

void display(GLFWwindow* window, double currentTime) {
	glClear(GL_DEPTH_BUFFER_BIT);
	glUseProgram(renderingProgram);
	// get the uniform variables for the MV and projection matrices
	mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
	projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
	// build perspective matrix
	glfwGetFramebufferSize(window, &width, &height);
	aspect = (float)width / (float)height;
	pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f); // 1.0472 radians = 60 degrees
	// build view matrix, model matrix, and model-view matrix
	vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));

	// vbo[0]
	mMat = glm::translate(glm::mat4(1.0f), glm::vec3(pyrLocX, pyrLocY, pyrLocZ));
	mvMat = vMat * mMat;
	// copy perspective and MV matrices to corresponding uniform variables
	glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
	glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
	// associate VBO with the corresponding vertex attribute in the vertex shader
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
	// 纹理
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, brickTexture);

	// vb0[2]
	glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(2);
	// adjust OpenGL settings and draw model
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glFrontFace(GL_CCW);// 锥体的三角形是逆时针的面认为是正方向

	glDrawArrays(GL_TRIANGLES, 0, myModel.getNumVertices());

}

int main(void) { // main() is unchanged from before

	

	if (!glfwInit()) { exit(EXIT_FAILURE); }

	
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	GLFWwindow* window = glfwCreateWindow(1200, 800, "Chapter 6 - program 2", NULL, NULL);
	glfwMakeContextCurrent(window);
	
	if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
	
	glfwSwapInterval(1);
	
	init(window);
	
	while (!glfwWindowShouldClose(window)) {
		display(window, glfwGetTime());
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glfwDestroyWindow(window);
	glfwTerminate();
	exit(EXIT_SUCCESS);


}

② 6.3 ImportedModel.cpp 一个类,用于读取obj文件

#include <fstream>
#include <sstream>
#include <glm\glm.hpp>
#include "6.3 ImportedModel.h"
#include <iostream> // cout 函数需要用
using namespace std;
// ------------ Imported Model class
ImportedModel::ImportedModel(const char *filePath) {
	ModelImporter modelImporter = ModelImporter();
	modelImporter.parseOBJ(filePath); // uses modelImporter to get vertex information
	numVertices = modelImporter.getNumVertices(); // 出错,这里的数值还是0
	std::vector<float> verts = modelImporter.getVertices();
	std::vector<float> tcs = modelImporter.getTextureCoordinates();
	std::vector<float> normals = modelImporter.getNormals();
	
	for (int i = 0; i < numVertices; i++) {
		vertices.push_back(glm::vec3(verts[i * 3], verts[i * 3 + 1], verts[i * 3 + 2]));
		texCoords.push_back(glm::vec2(tcs[i * 2], tcs[i * 2 + 1]));
		normalVecs.push_back(glm::vec3(normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2]));
	}
	
}

int ImportedModel::getNumVertices() {   return numVertices; } // accessors // 出错,这里的数值还是0
std::vector<glm::vec3> ImportedModel::getVertices() { return vertices; }
std::vector<glm::vec2> ImportedModel::getTextureCoords() { return texCoords; }
std::vector<glm::vec3> ImportedModel::getNormals() { return normalVecs; }
// -------------- Model Importer class
ModelImporter::ModelImporter() {}
void ModelImporter::parseOBJ(const char *filePath) {
	float x, y, z;
	string content;
	ifstream fileStream(filePath, ios::in);
	string line = "";

	int iNum = 0;//调试
	while (!fileStream.eof()) {
		
		getline(fileStream, line);
		if (line.compare(0, 2, "v ") == 0) { // vertex position ("v" case)

			stringstream ss(line.erase(0, 1));
			ss >> x; ss >> y; ss >> z; // extract the vertex position values
			
			vertVals.push_back(x);
			vertVals.push_back(y);
			vertVals.push_back(z);

			
		}
		if (line.compare(0, 2, "vt") == 0) { // texture coordinates ("vt" case)
			stringstream ss(line.erase(0, 2));
			ss >> x; ss >> y; // extract texture coordinate values
			stVals.push_back(x);
			stVals.push_back(y);

		}
		if (line.compare(0, 2, "vn") == 0) { // vertex normals ("vn" case)
			stringstream ss(line.erase(0, 2));
			ss >> x; ss >> y; ss >> z; // extract the normal vector values
			normVals.push_back(x);
			normVals.push_back(y);
			normVals.push_back(z);
			
		}
		if (line.compare(0, 2, "f ") == 0) { 
			// 注意,这里的 f 后面必须加一个空格,因为line.compare(0, 2, "f ")中表示从索引0开始的2个字符,而f不加空格只有一个字符

			string oneCorner, v, t, n;
			stringstream ss(line.erase(0, 2));

			for (int i = 0; i < 3; i++) {
				getline(ss, oneCorner, ' '); // extract triangle face references
				stringstream oneCornerSS(oneCorner);
				getline(oneCornerSS, v, '/');
				getline(oneCornerSS, t, '/');
				getline(oneCornerSS, n, '/');
				int vertRef = (stoi(v) - 1) * 3; // "stoi" converts string to integer
				int tcRef = (stoi(t) - 1) * 2;
				int normRef = (stoi(n) - 1) * 3;
				triangleVerts.push_back(vertVals[vertRef]); // build vector of vertices
				triangleVerts.push_back(vertVals[vertRef + 1]);
				triangleVerts.push_back(vertVals[vertRef + 2]);
				textureCoords.push_back(stVals[tcRef]); // build vector of texture coords
				textureCoords.push_back(stVals[tcRef + 1]);
				normals.push_back(normVals[normRef]); //… and normals
				normals.push_back(normVals[normRef + 1]);
				normals.push_back(normVals[normRef + 2]);


			}
			
		}
	}
}
int ModelImporter::getNumVertices() { return (triangleVerts.size() / 3); } // accessors  // 先注释,为了调试,等调试结束再解除注释
std::vector<float> ModelImporter::getVertices() { return triangleVerts; }
std::vector<float> ModelImporter::getTextureCoordinates() { return textureCoords; }
std::vector<float> ModelImporter::getNormals() { return normals; }

③ 6.3 ImportedModel.h 类的声明

#pragma once
#include <vector>
class ImportedModel
{
private:
	int numVertices;
	std::vector<glm::vec3> vertices;
	std::vector<glm::vec2> texCoords;
	std::vector<glm::vec3> normalVecs;
public:
	ImportedModel(const char *filePath);
	int getNumVertices();
	std::vector<glm::vec3> getVertices();
	std::vector<glm::vec2> getTextureCoords();
	std::vector<glm::vec3> getNormals();
};
class ModelImporter
{
private:
	// values as read in from OBJ file
	std::vector<float> vertVals;
	std::vector<float> stVals;
	std::vector<float> normVals;
	// values stored for later use as vertex attributes
	std::vector<float> triangleVerts;
	std::vector<float> textureCoords;
	std::vector<float> normals;
public:
	ModelImporter();
	void parseOBJ(const char *filePath);
	int getNumVertices();
	std::vector<float> getVertices();
	std::vector<float> getTextureCoordinates();
	std::vector<float> getNormals();
};

④ 5.2 Utils.cpp 一些调用函数,比如检测错误的函数

#include "Utils/5.2 Utils.h"
#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include "SOIL2/SOIL2.h"

#include <iostream>
#include <string>
#include <fstream>
using namespace std;


GLuint createShaderProgram(const char* a_Path, const char* b_Path) {
	GLint vertCompiled;
	GLint fragCompiled;
	GLint linked;
	string vertShaderStr = readShaderSource(a_Path); // 文件在add文件夹中
	string fragShaderStr = readShaderSource(b_Path);
	const char *vertShaderSrc = vertShaderStr.c_str();
	const char *fragShaderSrc = fragShaderStr.c_str();
	GLuint vShader = glCreateShader(GL_VERTEX_SHADER);
	GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(vShader, 1, &vertShaderSrc, NULL);
	glShaderSource(fShader, 1, &fragShaderSrc, NULL);
	// 在编译着色器时,捕捉错误
	glCompileShader(vShader);
	checkOpenGLError();
	glGetShaderiv(vShader, GL_COMPILE_STATUS, &vertCompiled);
	if (vertCompiled != 1) {
		cout << "vertex compilation failed" << endl;
		printShaderLog(vShader);
	}
	glCompileShader(fShader);
	checkOpenGLError();
	glGetShaderiv(fShader, GL_COMPILE_STATUS, &fragCompiled);
	if (fragCompiled != 1) {
		cout << "fragment compilation failed" << endl;
		printShaderLog(fShader);
	}
	GLuint vfProgram = glCreateProgram();
	glAttachShader(vfProgram, vShader);
	glAttachShader(vfProgram, fShader);
	glLinkProgram(vfProgram);
	checkOpenGLError();
	glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked);
	if (linked != 1) {
		cout << "linking failed" << endl;
		printProgramLog(vfProgram);
	}

	return vfProgram;
}

string readShaderSource(const char *filePath) {
	string content;
	ifstream fileStream(filePath, ios::in);
	string line = "";
	while (!fileStream.eof()) {
		getline(fileStream, line);
		content.append(line + "\n");
	}
	fileStream.close();
	return content;
}
bool checkOpenGLError() {
	bool foundError = false;
	int glErr = glGetError();
	while (glErr != GL_NO_ERROR) {
		cout << "glError: " << glErr << endl;
		foundError = true;
		glErr = glGetError();
	}
	return foundError;
}

void printProgramLog(int prog) {
	int len = 0;
	int chWrittn = 0;
	char *log;
	glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);
	if (len > 0) {
		log = (char *)malloc(len);
		glGetProgramInfoLog(prog, len, &chWrittn, log);
		cout << "Program Info Log: " << log << endl;
		free(log);
	}
}
void printShaderLog(GLuint shader) {
	int len = 0;
	int chWrittn = 0;
	char *log;
	glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
	if (len > 0) {
		log = (char *)malloc(len);
		glGetShaderInfoLog(shader, len, &chWrittn, log);
		cout << "Shader Info Log: " << log << endl;
		free(log);
	}
}

GLuint loadTexture(const char *texImagePath) {
	GLuint textureID;
	textureID = SOIL_load_OGL_texture(texImagePath,
		SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
	if (textureID == 0) cout << "could not find texture file" << texImagePath << endl;
	
	// mipmaps
	glBindTexture(GL_TEXTURE_2D, 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_WRAP_S, GL_MIRRORED_REPEAT);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

	 仅有一个,其余颜色为红色——多选一
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
	//float redColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
	//glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, redColor);

	// 仅有一个,其余为图片最边缘一行一列的颜色拓展——多选一
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glGenerateMipmap(GL_TEXTURE_2D);
	
	// if also anisotropic filtering 
	if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) {
		GLfloat anisoSetting = 0.0f;
		glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoSetting);
		//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoSetting);
	}

	return textureID;
}

⑤ 5.2 Utils.h 调用函数声明

#pragma once
#ifndef UTILS_H
#define UTILS_H
#include <string>
#include "GL\glew.h"
#include "GLFW\glfw3.h"
using namespace std;

GLuint createShaderProgram(const char* a_Path, const char* b_Path);
string readShaderSource(const char *filePath);
bool checkOpenGLError();
void printProgramLog(int prog);
void printShaderLog(GLuint shader);
GLuint loadTexture(const char *texImagePath);


#endif // !UTILS_H


⑥ 5.2 vertShader.glsl 点着色器,确定3D点坐标

#version 430
layout (location=0) in vec3 pos;
layout (location=1) in vec2 texCoord;
out vec2 tc; // texture coordinate output to rasterizer for interpolation
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (binding=0) uniform sampler2D samp; // not used in vertex shader 
void main(void)
{	 gl_Position = proj_matrix * mv_matrix * vec4(pos,1.0);
	tc = texCoord;
}

⑦ 5.2 fragShader.glsl 片着色器,确定每个点的颜色

#version 430
in vec2 tc; // interpolated incoming texture coordinate
out vec4 color;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (binding=0) uniform sampler2D samp;
void main(void)
{ color = texture(samp, tc);
}

⑧ 6.3 cube1.obj obj文件,这里用的是Blender软件生成的

# 以下是记事本的obj内容
# Blender v3.0.1 OBJ File: ''
# www.blender.org
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.000000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.625000 0.250000
vt 0.875000 0.750000
vt 0.625000 1.000000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
s off
f 5/1/1 3/2/1 1/3/1
f 3/2/2 8/4/2 4/5/2
f 7/6/3 6/7/3 8/8/3
f 2/9/4 8/10/4 6/11/4
f 1/3/5 4/5/5 2/9/5
f 5/12/6 2/9/6 6/7/6
f 5/1/1 7/13/1 3/2/1
f 3/2/2 7/14/2 8/4/2
f 7/6/3 5/12/3 6/7/3
f 2/9/4 4/5/4 8/10/4
f 1/3/5 3/2/5 4/5/5
f 5/12/6 1/3/6 2/9/6

⑨ 6.1 earth.jpg 一张图片

在这里插入图片描述

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-03-04 15:56:00  更:2022-03-04 15:57:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 16:45:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码