opengl模型加载

环境配置以及视角修改和着色器亮度修改

首先注意我的文件生成目录为bin目录所以要用到的dll文件必须放到bin目录文件夹下面,而不是debug下面在这里插入图片描述

  • 调整视角位置更高一点
public:
	Camera() {
		fov = 45;
		Position = glm::vec3(0.0f, 7.0f, 20.0f);
		UP = glm::vec3(0.0f, 1.0f, 0.0f);
		Front = glm::vec3(0.0f, 0.0f, -1.0f);    //移动的距离在旋转和wasd移动时都要用到,注意这里用的是-1
	}
  • 调整的亮一点,取消光源衰减
	result = (light.ambient*material.ambient
		+diffuse*light.diffuse*material.diffuse
		+specular*light.specular*material.specular)*lightColor;	

而不是

	result = (light.ambient*material.ambient
		+diffuse*light.diffuse*material.diffuse
		+specular*light.specular*material.specular)*lightColor*attenuation;	

model函数

  1. 注意构造函数里边的参数应该是:Model(const char* path),因为你输入的是一个不变的量
  2. model函数实际上构建了一个又一个的mesh,所以在输出的时候是用的还是mesh.Draw
  3. 注意mesh向量数组的构建
  4. 本身就有处理obj的纹理的能力
#pragma once
#ifndef MODEL_H
#define MODEL_H

#include <glad/glad.h> 

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "stb_image.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

#include "mesh.h"
#include "Shader.h"

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
class Model
{
public:
	/*  函数   */
	Model(const char* path)
	{
		loadModel(path);	//调用模型路径加载函数
	}
	//渲染函数
	void Draw(Shader shader)
	{
		for (unsigned int i = 0; i < meshes.size(); i++)
			meshes[i].Draw(shader);	//调用mesh的draw函数	
	}
private:
	/*  模型数据  */
	vector<Mesh> meshes;
	string directory;
	/*  加载模型函数   */
	void loadModel(string path)
	{
		// 读取文件 via ASSIMP
		Assimp::Importer importer;
		const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);	//用三角形|翻转Y轴|还有一个切线空间暂时用不到
		// check for errors
		if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
		{
			cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
			return;
		}
		// retrieve the directory path of the filepath
		directory = path.substr(0, path.find_last_of('/'));

		// process ASSIMP's root node recursively
		processNode(scene->mRootNode, scene);	//通过路径寻找并且赋值
	}
	//递归去获取这些网格索引,获取每个网格,处理每个网格,接着对每个节点的子节点重复这一过程。
	//结构都放进meshes里边
	void processNode(aiNode* node, const aiScene* scene)
	{
		// 处理节点所有的网格(如果有的话)
		for (unsigned int i = 0; i < node->mNumMeshes; i++)
		{
			aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
			meshes.push_back(processMesh(mesh, scene));	//函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素
		}
		// 接下来对它的子节点重复这一过程
		for (unsigned int i = 0; i < node->mNumChildren; i++)
		{
			processNode(node->mChildren[i], scene);
		}
	}
	
	//将构造mesh函数放到model类里边
	Mesh processMesh(aiMesh* mesh, const aiScene* scene)
	{
		vector<Vertex> vertices;
		vector<unsigned int> indices;
		vector<Texture> textures;

		for (unsigned int i = 0; i < mesh->mNumVertices; i++)
		{
			Vertex vertex;
			// 处理顶点位置、法线和纹理坐标
			glm::vec3 vector; // we declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.
			// positions
			vector.x = mesh->mVertices[i].x;
			vector.y = mesh->mVertices[i].y;
			vector.z = mesh->mVertices[i].z;
			vertex.Position = vector;
			// normals
			vector.x = mesh->mNormals[i].x;
			vector.y = mesh->mNormals[i].y;
			vector.z = mesh->mNormals[i].z;
			vertex.Normal = vector;
			// texture coordinates
			if (mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
			{
				glm::vec2 vec;
				// a vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't 
				// use models where a vertex can have multiple texture coordinates so we always take the first set (0).
				vec.x = mesh->mTextureCoords[0][i].x;
				vec.y = mesh->mTextureCoords[0][i].y;
				vertex.TexCoords = vec;
			}
			else
				vertex.TexCoords = glm::vec2(0.0f, 0.0f);	//没有纹理的情况

			vertices.push_back(vertex);
		}
		// 处理索引
		for (unsigned int i = 0; i < mesh->mNumFaces; i++)
		{
			aiFace face = mesh->mFaces[i];
			// retrieve all indices of the face and store them in the indices vector
			for (unsigned int j = 0; j < face.mNumIndices; j++)
				indices.push_back(face.mIndices[j]);
		}
		//处理材质
		//把所有的材质按照类型分类放到向量里边
		if (mesh->mMaterialIndex >= 0)
		{
			//注意这里是给材质对象赋值
			aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
			//分别构造两个纹理数组存放漫反射和镜面反射纹理
			vector<Texture> diffuseMaps = loadMaterialTextures(material,
				aiTextureType_DIFFUSE, "texture_diffuse");
			textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());	//填充
			vector<Texture> specularMaps = loadMaterialTextures(material,
				aiTextureType_SPECULAR, "texture_specular");
			textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
		}

		return Mesh(vertices, indices, textures);
	}

	//加载与存储纹理
	vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type,
		string typeName)
	{
		vector<Texture> textures;
		for (unsigned int i = 0; i < mat->GetTextureCount(type); i++)
		{
			aiString str;
			mat->GetTexture(type, i, &str);
			Texture texture;
			texture.id = TextureFromFile(str.C_Str(), directory);
			texture.type = typeName;
			texture.path = str.C_Str();
			textures.push_back(texture);
		}
		return textures;
	}
	//读取纹理文件
	unsigned int TextureFromFile(const char* path, const string& directory)
	{
		string filename = string(path);
		filename = directory + '/' + filename;

		unsigned int textureID;
		glGenTextures(1, &textureID);

		int width, height, nrComponents;
		unsigned char* data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);
		if (data)
		{
			GLenum format;
			if (nrComponents == 1)
				format = GL_RED;
			else if (nrComponents == 3)
				format = GL_RGB;
			else if (nrComponents == 4)
				format = GL_RGBA;

			glBindTexture(GL_TEXTURE_2D, textureID);
			glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
			glGenerateMipmap(GL_TEXTURE_2D);

			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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

			stbi_image_free(data);
		}
		else
		{
			std::cout << "Texture failed to load at path: " << path << std::endl;
			stbi_image_free(data);
		}

		return textureID;	//返回textureID
	}
};
#endif

主函数修改

  1. 添加了model的构造函数
  2. 旋转修改模型改为绕着一个Y轴旋转
  3. 我自己定义了光源,所以并没有删除mesh函数的构造
int main() {

	//初始化
	GLFWwindow* window = init(); //创建一个窗口指针,因为里边是一个空指针所有init函数必须改变类型

	//着色    
	Shader lightShader("lamp.vert", "lamp.frag");
	Shader cubeShader("cube.vert", "cube.frag");    //使用封装shader类

	glm::mat4 trans = glm::mat4(1.0f);  //单位矩阵
	glm::mat4 model = glm::mat4(1.0f);  //模型矩阵:用于物体坐标转化为世界坐标
	glm::mat4 view = glm::mat4(1.0f);

	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);   //防止窗口对鼠标进行拦截
	//渲染引擎
	glEnable(GL_DEPTH_TEST);    //打开深度缓存
    
    //生成mesh:光照用
    Mesh mesh = processCubeMesh();

    //使用model类生成model对象
    Model ourModel("D:/openGLResource/bin/nanosuit/nanosuit.obj");
	while (!glfwWindowShouldClose(window)) {    //当需要退出时候退出

		//因为每一个循环的进入时间不一样,确保推进的距离一样,要用到时间差
		currentFrame = glfwGetTime();
		deltaTime = currentFrame - lastFrame;
		lastFrame = currentFrame;

		processInput(window); //每个周期都调用键位函数

		//动态变化摄像机位置
		float radius = 10.0f;   //半径
		float camX = sin(glfwGetTime()) * radius;
		float camZ = cos(glfwGetTime()) * radius;

		//设置颜色值和透明度,需要链接opengl32库才行
		glClearColor(0.2f, 0.3f, 0.3f, 0.1f);   //背景
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   //清理颜色或者深度缓存

		//绘制立方体
		trans = glm::mat4(1.0f);
		model = glm::translate(trans, cubePositions[0]);    //只变换位置用来区分不同的立方体
		model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.0f, 3.0f, 0.0f));  //角度(逆向),绕
		view = camera.GetViewMatrix();

		glm::mat4 projection;   //投影
		projection = glm::perspective(glm::radians(camera.fov), 800.0f / 600.0f, 0.1f, 100.0f);  //透视投影:FOV,屏幕长宽比,近,远。
		cubeShader.useShader();
		//传值给传送多个矩阵让其发生位置变化
		glUniformMatrix4fv(glGetUniformLocation(cubeShader.ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
		glUniformMatrix4fv(glGetUniformLocation(cubeShader.ID, "view"), 1, GL_FALSE, glm::value_ptr(view));
		glUniformMatrix4fv(glGetUniformLocation(cubeShader.ID, "model"), 1, GL_FALSE, glm::value_ptr(model));
		glUniform4f(glGetUniformLocation(cubeShader.ID, "lightPos"), cubePositions[1].r, cubePositions[1].g, cubePositions[1].b, 1);   //注意lightposion的三个值,现在是齐次坐标
		glUniform3f(glGetUniformLocation(cubeShader.ID, "viewPos"), camera.Position.r, camera.Position.g, camera.Position.b);   //观察的方向
		glUniform3f(glGetUniformLocation(cubeShader.ID, "front"), camera.Front.r, camera.Front.g, camera.Front.b);   //相机朝向的方向
		glUniform3f(glGetUniformLocation(cubeShader.ID, "objectColor"), 1.0f, 0.5f, 0.31f);

		glUniform3f(glGetUniformLocation(cubeShader.ID, "lightColor"), 1.0f, 1.0f, 1.0f);
		//绘制mesh
		//mesh.Draw(cubeShader);
        ourModel.Draw(cubeShader);
		//绑定第二个立方体
		model = glm::translate(trans, cubePositions[2]);
		glUniformMatrix4fv(glGetUniformLocation(cubeShader.ID, "model"), 1, GL_FALSE, glm::value_ptr(model));
		//mesh.Draw(cubeShader);//画第二个立方体
        //ourModel.Draw(cubeShader);
		//绘制光源
		trans = glm::mat4(1.0f);
		model = glm::translate(trans, cubePositions[1]);
		model = glm::scale(model, glm::vec3(0.2f, 0.2f, 0.2f));
		trans = projection * view * model;
		lightShader.useShader();    //注意这是新的useShader了
		glUniform3f(glGetUniformLocation(lightShader.ID, "lightColor"), 1.0f, 1.0f, 1.0f);
		glUniformMatrix4fv(glGetUniformLocation(lightShader.ID, "transform"), 1/*个矩阵*/, GL_FALSE,
			glm::value_ptr(trans));  //设置偏移矩阵
		mesh.Draw(lightShader);
        //ourModel.Draw(lightShader);
		glfwSwapBuffers(window);
		glfwPollEvents();   //立即处理已经到位的事件,如果没有这个就会一直渲染而不触发事件       
	}
	//退出
	glfwTerminate();
	return 0;
}

结果

  • 只有纹理的漫反射而没有什么衰减等因素的影响

请添加图片描述

  • 只取消光衰减之后的样子,注意漫反射以及镜面反射请添加图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值