LearnOpenGL 模型加载—模型(二 绘制模型)

写在前面

原文链接。原文应该是github上的一个项目,本文主要用来记录一些知识点和自己遇到的问题。

和箱子模型告别

所以,让我们导入一个由真正的艺术家所创造的模型,替代我这个天才的作品(你要承认,这些箱子可能是你看过的最漂亮的立方体了),测试一下我们的实现吧。由于我不想让我占太多的功劳,我会偶尔让别的艺术家也加入我们,这次我们将会加载Crytek的游戏孤岛危机(Crysis)中的原版纳米装(Nanosuit)。这个模型被输出为一个.obj文件以及一个.mtl文件,.mtl文件包含了模型的漫反射、镜面光和法线贴图(这个会在后面学习到),你可以在这里下载到(稍微修改之后的)模型,注意所有的纹理和模型文件应该位于同一个目录下,以供加载纹理。
在这里插入图片描述
现在在代码中,声明一个Model对象,将模型的文件位置传入。接下来模型应该会自动加载并(如果没有错误的话)在渲染循环中使用它的Draw函数来绘制物体,这样就可以了。不再需要缓冲分配、属性指针和渲染指令,只需要一行代码就可以了(真爽!)。接下来如果你创建一系列着色器,其中片段着色器仅仅输出物体的漫反射纹理颜色,最终的结果看上去会是这样的:
在这里插入图片描述

你可以在这里找到完整的源码。

这里有一个很坑的问题,可能让你得不到正确结果……事实上我也费了很多时间来debug,下面来说一下。

坑点

主要是由0号纹理单元引起的……
在这里插入图片描述
原文这里的代码其实是有点问题的,我们看一下他这里给出的着色器代码:

#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D texture_diffuse1;

void main()
{    
    FragColor = texture(texture_diffuse1, TexCoords);
}

再看我们在Mesh对象的Draw方法中设置的采样器名称:
在这里插入图片描述
很明显这里是对不上的,那么为什么我们还能看到模型呢?是因为0号纹理单元默认是一直激活的,在GLSL中的纹理采样器默认都会绑定到0号纹理单元,而我们在代码中恰好又绑定了纹理对象到这个纹理单元,所以能看到模型。那么我们可以改一下这个函数,让其激活并设置非零号纹理单元,就可以解决问题了——实际上并不能,你可以看一下texture对象的构造函数,其内调用了glBindTexture函数,也就是说只要使用了纹理对象,0号纹理单元上就一定绑定了某个纹理对象,那么在GLSL中进行采样时就一定要小心,采样的结果可能并不是黑色!出于这个原因我决定修改命名规则,我们使用sampler2D数组,同时传递两个整数给gpu,告诉它我们使用了多少个漫反射贴图和镜面光贴图。

第二个坑点是图片y轴的翻转问题。我们在纹理章节已经学过了:OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部。那么我们利用这个函数翻转图片的y轴:
在这里插入图片描述
但是还记得我们在导入obj文件的时候设置了什么吗?
在这里插入图片描述
aiProcess_FlipUVs将在处理的时候翻转y轴的纹理坐标。如果你感觉贴图很奇怪的话,可能就是这里的问题,两个地方都翻转了等于没有翻转。

回到主题

修改后的代码如下:

m e s h : mesh: mesh:

#pragma once
#ifndef MESH_H
#define MESH_H

#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "shader.h"
#include <vector>
#include "texture.h"
#include <string>
#include <iostream>
using std::vector;
using std::string;
using std::cout;
using std::max;

struct Vertex
{
	// 位置 法向量 纹理坐标
	glm::vec3 Position;
	glm::vec3 Normal;
	glm::vec2 TexCoords;
};

class Mesh
{
public:
	/* 网格数据 */
	vector<Vertex> vertices;
	vector<unsigned int> indices;
	vector<Texture> textures;
	/* 函数 */
	Mesh(const vector<Vertex>& vertices, const vector<unsigned int>& indices, const vector<Texture>& textures);
	void Draw(const Shader& shader);
private:
	/* 渲染数据 */
	unsigned int VAO, VBO, EBO;
	
	void setupMesh();
};

#endif // !MESH_H

#include "mesh.h"

Mesh::Mesh(const vector<Vertex>& vertices, const vector<unsigned int>& indices, const vector<Texture>& textures) :
	vertices(vertices), indices(indices), textures(textures)
{
	setupMesh();
}

void Mesh::setupMesh()
{
	// 初始化VAO VBO EBO
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);

	// 绑定VAO
	glBindVertexArray(VAO);
	// 绑定VBO 复制顶点数据
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
	// 绑定EBO 复制索引数据
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);

	// 顶点位置
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
	glEnableVertexAttribArray(0);
	// 顶点法线
	// C++内置的offsetof函数 能自动返回结构对象中 某变量距离结构体对象首地址的偏移值:
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
	glEnableVertexAttribArray(1);
	// 顶点纹理坐标
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
	glEnableVertexAttribArray(2);
	
	// 解绑VAO
	glBindVertexArray(0);
}


unsigned int maxDiffuseNr = 0;
unsigned int maxSpecularNr = 0;

void Mesh::Draw(const Shader& shader)
{
	// 当前漫反射纹理和镜面光纹理的编号
	unsigned int diffuseNr = 0;
	unsigned int specularNr = 0;
	for (unsigned int i = 0; i < textures.size(); i++)
	{
		// 激活纹理单元 并绑定
		textures[i].use(i);
		// 获取纹理序号和类型
		string number;
		string type = textures[i].getName();
		if (type == "texture_diffuse")
			number = std::to_string(diffuseNr++);
		else if (type == "texture_specular")
			number = std::to_string(specularNr++);
		shader.setInt("material." + type + "[" + number + "]", i);
	}
	shader.setInt(string("material.") + "texture_diffuse_num", diffuseNr);
	shader.setInt(string("material.") + "texture_specular_num", specularNr);

	maxDiffuseNr = max(maxDiffuseNr, diffuseNr);
	maxSpecularNr = max(maxSpecularNr, specularNr);
	glBindVertexArray(VAO);
	glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
	glBindVertexArray(0);
	// always good practice to set everything back to defaults once configured.
	glActiveTexture(GL_TEXTURE0);
}

m o d e l : model: model:

#pragma once
#ifndef MODEL_H
#define MODEL_H
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include "mesh.h"
#include <unordered_map>

using std::unordered_map;

class Model
{
	/* 函数 */
public:
	Model(const string& path)
	{
		loadModel(path);
	}
	void Draw(const Shader& shader);
private:
	/* 模型数据 */
	vector<Mesh> meshes;
	/* 模型数据所在目录 */
	string directory;
	/* 记录已经加载过的纹理 */
	vector<Texture> texturesLoaded;
	/* 纹理文件到索引的哈希表 */
	unordered_map<string, unsigned int> texturesHashTable;

	void loadModel(const string& path);
	void processNode(aiNode* node, const aiScene* scene);
	Mesh processMesh(aiMesh* mesh, const aiScene* scene);
	vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, const string& typeName);
};

#endif // !MODEL_H

#include "model.h"
#include <iostream>
using std::cout;
using std::endl;

extern unsigned int maxDiffuseNr;
extern unsigned int maxSpecularNr;
bool firstDraw = false;

void Model::Draw(const Shader& shader)
{
	for (unsigned int i = 0; i < meshes.size(); i++)
		meshes[i].Draw(shader);

	if (!firstDraw)
	{
		cout << "First Draw finish\n";
		cout << "Max diffuse texture number is " << maxDiffuseNr << '\n';
		cout << "Max specular texture number is " << maxSpecularNr << '\n';
		firstDraw = 1;
	}
}

void Model::loadModel(const string& path)
{
	Assimp::Importer import;
	// 读取模型文件 第二个参数用于后期处理
	const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
	// 是否读取成功
	if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
	{
		cout << "ERROR::ASSIMP::" << import.GetErrorString() << endl;
		return;
	}
	// 获取文件目录
	directory = path.substr(0, path.find_last_of('/'));
	// 从场景根节点开始递归处理所有节点
	processNode(scene->mRootNode, scene);

	// 打印obj的信息
	cout << "Loading success!\n";
	cout << "This model have " << meshes.size() << " meshes\n\n";
}

void Model::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));
	}
	// 递归处理子节点
	for (unsigned int i = 0; i < node->mNumChildren; i++)
		processNode(node->mChildren[i], scene);
}

Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene)
{
	// 一个Mesh对象包括 Vertex(位置 法向量 纹理坐标) 绘制所需的索引数组 绘制所需的纹理
	vector<Vertex> vertices;
	vector<unsigned int> indices;
	vector<Texture> textures;
	
	// 处理顶点位置 法线 纹理坐标
	for (unsigned int i = 0; i < mesh->mNumVertices; i++)
	{
		Vertex vertex;
		// 顶点位置 该数组永远存在
		vertex.Position = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
		// 法线 不一定存在
		vertex.Normal = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
		// 纹理坐标 不一定存在
		if (mesh->mTextureCoords[0])
			vertex.TexCoords = glm::vec2(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
		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];
		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");
		vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
		textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
		textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
	}
	return Mesh(vertices, indices, textures);
}

vector<Texture> Model::loadMaterialTextures(aiMaterial* mat, aiTextureType type, const string& typeName)
{
	vector<Texture> textures;
	// 类型为type的纹理个数
	for (unsigned int i = 0; i < mat->GetTextureCount(type); i++)
	{
		aiString textureFilePosition;
		// 得到纹理文件的位置 存储在textureFilePosition中
		mat->GetTexture(type, i, &textureFilePosition);
		string str(textureFilePosition.C_Str());
		auto it = texturesHashTable.find(str);
		if (it == texturesHashTable.end())
		{
			// 未加载过
			texturesHashTable[str] = texturesLoaded.size();
			// 读取obj时已经翻转了纹理坐标 此处不需要再翻转图片y轴了
			Texture texture(typeName, directory + "/" + str, false);
			texturesLoaded.push_back(texture);
			textures.push_back(texture);
		}
		else
		{
			// 已经加载过了
			textures.push_back(texturesLoaded[it->second]);
		}
	}
	return textures;
}

s h a d e r : shader: shader:

#version 330 core
layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0 
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

uniform mat4 model;	//模型
uniform mat4 view;	//观察
uniform mat4 projection;	//投影

out vec2 TexCoords;

void main()
{
    TexCoords = aTexCoords;
    // 注意乘法要从右向左读
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
#version 330 core

#define MAX_TEXTURE_NUM 8

struct Material
{
	sampler2D texture_diffuse[MAX_TEXTURE_NUM];
	sampler2D texture_specular[MAX_TEXTURE_NUM];
	int texture_diffuse_num;
	int texture_specular_num;
};

uniform Material material;

in vec2 TexCoords;

out vec4 FragColor;  

void main()
{
	FragColor = vec4(0.0,0.0,0.0,0.0);
	for(int i = 0; i < material.texture_diffuse_num; i++)
	{
		FragColor += texture(material.texture_diffuse[i], TexCoords);
	}
	FragColor.a = 1.0;
}

m a i n : main: main:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include "shader.h"
#include "stb_image.h"
#include "camera.h"
#include "texture.h"
#include "model.h"
using std::cout;

//窗口回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	//绘图视口 3D坐标到2D坐标的转换(映射)和这些参数(宽高)有关
	glViewport(0, 0, width, height);
}

//键盘回调
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

//鼠标回调
void mouse_callback(GLFWwindow* window, double xpos, double ypos);

//滚轮回调
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

//窗口初始大小
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

//物体着色器
const char* vShaderPath = "ShaderFiles/shader.vert";
const char* fShaderPath = "ShaderFiles/shader.frag";
//光源着色器
const char* lightvShaderPath = "ShaderFiles/light_shader.vert";
const char* lightfShaderPath = "ShaderFiles/light_shader.frag";

//混合颜色的插值
float mixValue = 0.2f;
//记录鼠标坐标
float lastX, lastY;
bool firstMouse = true;

//摄像机
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));

//光源位置
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

int main()
{
	//glfw初始化
	glfwInit();
	//告诉glfw我们所使用的opengl版本 此处为3.3
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	//创建窗口
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
	if (window == NULL)
	{
		cout << "Failed to create GLFW window\n";
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
	//设置窗口回调函数
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	//键盘回调函数
	glfwSetKeyCallback(window, key_callback);
	//鼠标回调
	glfwSetCursorPosCallback(window, mouse_callback);
	//滚轮回调
	glfwSetScrollCallback(window, scroll_callback);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		cout << "Failed to initialize GLAD\n";
		return -1;
	}

	//开启深度测试
	glEnable(GL_DEPTH_TEST);

	//着色器对象
	Shader shaderProgram = Shader(vShaderPath, fShaderPath);
	Shader lightShaderProgram = Shader(lightvShaderPath, lightfShaderPath);

	// light positions
	float vertices[] = {
		-0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f, -0.5f,
		 0.5f,  0.5f, -0.5f,
		 0.5f,  0.5f, -0.5f,
		-0.5f,  0.5f, -0.5f,
		-0.5f, -0.5f, -0.5f,

		-0.5f, -0.5f,  0.5f,
		 0.5f, -0.5f,  0.5f,
		 0.5f,  0.5f,  0.5f,
		 0.5f,  0.5f,  0.5f,
		-0.5f,  0.5f,  0.5f,
		-0.5f, -0.5f,  0.5f,

		-0.5f,  0.5f,  0.5f,
		-0.5f,  0.5f, -0.5f,
		-0.5f, -0.5f, -0.5f,
		-0.5f, -0.5f, -0.5f,
		-0.5f, -0.5f,  0.5f,
		-0.5f,  0.5f,  0.5f,

		 0.5f,  0.5f,  0.5f,
		 0.5f,  0.5f, -0.5f,
		 0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f,  0.5f,
		 0.5f,  0.5f,  0.5f,

		-0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f,  0.5f,
		 0.5f, -0.5f,  0.5f,
		-0.5f, -0.5f,  0.5f,
		-0.5f, -0.5f, -0.5f,

		-0.5f,  0.5f, -0.5f,
		 0.5f,  0.5f, -0.5f,
		 0.5f,  0.5f,  0.5f,
		 0.5f,  0.5f,  0.5f,
		-0.5f,  0.5f,  0.5f,
		-0.5f,  0.5f, -0.5f,
	};

	//物体(obj)
	Model modelObj("Obj/Nanosuit/nanosuit.obj");

	//光源
	unsigned int lightVAO;
	unsigned int lightVBO;
	glGenVertexArrays(1, &lightVAO);
	glBindVertexArray(lightVAO);

	glGenBuffers(1, &lightVBO);
	glBindBuffer(GL_ARRAY_BUFFER, lightVBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	//线框模式
	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	//这些uniform不会更新 可以放到循环外面

	while (!glfwWindowShouldClose(window))
	{
		glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		//矩阵运算
		glm::mat4 view = camera.GetViewMatrix();
		glm::mat4 projection = glm::perspective(glm::radians(camera.Fov), SCR_WIDTH * 1.0f / SCR_HEIGHT, 0.1f, 100.0f);

		glm::mat4 model(1.0f);
		model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));
		//激活着色器
		shaderProgram.use();
		shaderProgram.setMat4("model", model);
		shaderProgram.setMat4("view", view);
		shaderProgram.setMat4("projection", projection);
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		modelObj.Draw(shaderProgram);
		
		model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 2.0f));
		model = glm::rotate(model, glm::radians(180.f), glm::vec3(0.0f, 1.0f, 0.0f));
		model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));
		shaderProgram.setMat4("model", model);
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		modelObj.Draw(shaderProgram);
		

		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	//这一步是可选的
	glDeleteVertexArrays(1, &lightVAO);
	glDeleteBuffers(1, &lightVBO);
	//glDeleteBuffers(1, &EBO);

	//释放资源
	glfwTerminate();
	return 0;
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	if (action == GLFW_REPEAT || action == GLFW_PRESS)
	{
		if (key == GLFW_KEY_ESCAPE)
		{
			glfwSetWindowShouldClose(window, GL_TRUE);
			return;
		}
		switch (key)
		{
		case GLFW_KEY_UP:
			mixValue += 0.1f;
			if (mixValue >= 1.0f)
				mixValue = 1.0f;
			break;
		case GLFW_KEY_DOWN:
			mixValue -= 0.1f;
			if (mixValue <= 0.0f)
				mixValue = 0.0f;
			break;
		case GLFW_KEY_W:
			camera.ProcessKeyboard(FORWARD);
			break;
		case GLFW_KEY_S:
			camera.ProcessKeyboard(BACKWARD);
			break;
		case GLFW_KEY_A:
			camera.ProcessKeyboard(LEFT);
			break;
		case GLFW_KEY_D:
			camera.ProcessKeyboard(RIGHT);
			break;
		default:
			break;
		}
	}
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
	if (firstMouse)
	{
		firstMouse = false;
		lastX = xpos, lastY = ypos;
	}
	camera.ProcessMouseMovement(xpos - lastX, lastY - ypos);
	lastX = xpos;
	lastY = ypos;
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
	camera.ProcessMouseScroll(yoffset);
}


在这里插入图片描述

下一步:加入镜面光贴图和光源!

s h a d e r : shader: shader:

#version 330 core
layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0 
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

uniform mat4 model;	//模型
uniform mat4 view;	//观察
uniform mat4 projection;	//投影

out vec3 Normal;
out vec3 FragPos;
out vec2 TexCoords;

void main()
{
    Normal = mat3(transpose(inverse(model))) * aNormal;
    FragPos = vec3(model * vec4(aPos, 1.0));
    TexCoords = aTexCoords;
    // 注意乘法要从右向左读
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

#version 330 core

#define MAX_TEXTURE_NUM 8

struct Material
{
	sampler2D texture_diffuse[MAX_TEXTURE_NUM];
	sampler2D texture_specular[MAX_TEXTURE_NUM];
	int texture_diffuse_num;
	int texture_specular_num;
	float shininess;
};

// 方向光
struct DirLight
{
	vec3 direction;
	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
};

// 点光源
struct PointLight
{
	vec3 position;
	vec3 ambient;
	vec3 diffuse;
	vec3 specular;

	float constant;
	float linear;
	float quadratic;
};

// 聚光-手电筒
struct SpotLight
{
	vec3 position;
	vec3 direction;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;

	// 内圆锥余弦值
	float cutOff;
	// 外圆锥余弦值
	float outerCutOff;
	float constant;
	float linear;
	float quadratic;
};

in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;

out vec4 FragColor;  

uniform vec3 viewPos;

uniform Material material;

uniform DirLight dirLight;
#define NR_POINT_LIGHTS 2
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform SpotLight spotLight;

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalLightSum(vec3 lightAmbient, vec3 lightDiffuse, vec3 lightSpecular, float diff, float spec);

void main()
{
	vec3 norm = normalize(Normal);
	vec3 viewDir = normalize(viewPos - FragPos);
	
	// 定向光照
	vec3 result = CalcDirLight(dirLight, norm, viewDir);
	// 点光源
	for(int i = 0; i < NR_POINT_LIGHTS; i++)
		result += CalPointLight(pointLights[i], norm, FragPos, viewDir);
	// 聚光
	result += CalSpotLight(spotLight, norm, FragPos, viewDir);

	FragColor = vec4(result, 1.0);
}

// 计算某个方向光源对该片段颜色的贡献
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
	vec3 lightDir = normalize(-light.direction);
	// 漫反射着色
	float diff = max(dot(normal,lightDir),0.0);
	// 镜面光着色
	vec3 reflectDir = reflect(-lightDir,normal);
	float spec = pow(max(dot(viewDir,reflectDir),0.0),material.shininess);
	// 合并
	return CalLightSum(light.ambient, light.diffuse, light.specular, diff, spec);
}

// 计算某个点光源对该片段颜色的贡献
vec3 CalPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
	vec3 lightDir = normalize(light.position - fragPos);
	// 漫反射着色
	float diff = max(dot(normal,lightDir),0.0);
	// 镜面光着色
	vec3 reflectDir = reflect(-lightDir,normal);
	float spec = pow(max(dot(viewDir,reflectDir),0.0),material.shininess);
	// 距离
	float dis = length(light.position - fragPos);
	// 衰减
	float attenuation = 1.0 / (light.constant + light.linear * dis + light.quadratic * dis *dis);
	// 合并
	return CalLightSum(light.ambient, light.diffuse, light.specular, diff, spec) * attenuation;
}

// 计算某个聚光灯源对该片段颜色的贡献
vec3 CalSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
	vec3 lightDir = normalize(light.position - fragPos);
	// 漫反射着色
	float diff = max(dot(normal,lightDir),0.0);
	// 镜面光着色
	vec3 reflectDir = reflect(-lightDir,normal);
	float spec = pow(max(dot(viewDir,reflectDir),0.0),material.shininess);
	// 距离
	float dis = length(light.position - fragPos);
	// 衰减
	float attenuation = 1.0 /(light.constant + light.linear * dis + light.quadratic * dis * dis);
	// 内外光切角插值 实现平滑过度效果
	float cosTheta = dot(lightDir, normalize(-light.direction));
	float epsilon = light.cutOff - light.outerCutOff;
	float intensity = clamp((cosTheta  - light.outerCutOff) / epsilon, 0.0, 1.0);
	// 合并
	return CalLightSum(light.ambient, light.diffuse, light.specular, diff, spec) * attenuation * intensity;
}

// 计算环境光、漫反射、镜面光的颜色和
// 没有计算衰减
vec3 CalLightSum(vec3 lightAmbient, vec3 lightDiffuse, vec3 lightSpecular, float diff, float spec)
{
	vec3 diffuse = vec3(0.0,0.0,0.0);
	for(int i = 0; i < material.texture_diffuse_num; i++)
	{
		diffuse += texture(material.texture_diffuse[i], TexCoords).rgb;
	}
	vec3 ambient = lightAmbient * diffuse;
	diffuse = lightDiffuse * diff * diffuse;
	vec3 specular = vec3(0.0,0.0,0.0);
	for(int i = 0; i < material.texture_specular_num; i++)
	{
		specular += texture(material.texture_specular[i], TexCoords).rgb;
	}
	specular = lightSpecular * spec * specular;
	return ambient + diffuse + specular;
}

m a i n : main: main:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include "shader.h"
#include "stb_image.h"
#include "camera.h"
#include "light.h"
#include "texture.h"
#include "model.h"
using std::cout;

//窗口回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	//绘图视口 3D坐标到2D坐标的转换(映射)和这些参数(宽高)有关
	glViewport(0, 0, width, height);
}

//键盘回调
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

//鼠标回调
void mouse_callback(GLFWwindow* window, double xpos, double ypos);

//滚轮回调
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

//窗口初始大小
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

//物体着色器
const char* vShaderPath = "ShaderFiles/shader.vert";
const char* fShaderPath = "ShaderFiles/shader.frag";
//光源着色器
const char* lightvShaderPath = "ShaderFiles/shader.vert";
const char* lightfShaderPath = "ShaderFiles/light_shader.frag";

//混合颜色的插值
float mixValue = 0.2f;
//记录鼠标坐标
float lastX, lastY;
bool firstMouse = true;

//摄像机
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));

//光源位置
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

int main()
{
	//glfw初始化
	glfwInit();
	//告诉glfw我们所使用的opengl版本 此处为3.3
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	//创建窗口
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
	if (window == NULL)
	{
		cout << "Failed to create GLFW window\n";
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
	//设置窗口回调函数
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	//键盘回调函数
	glfwSetKeyCallback(window, key_callback);
	//鼠标回调
	glfwSetCursorPosCallback(window, mouse_callback);
	//滚轮回调
	glfwSetScrollCallback(window, scroll_callback);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		cout << "Failed to initialize GLAD\n";
		return -1;
	}

	//开启深度测试
	glEnable(GL_DEPTH_TEST);

	//着色器对象
	Shader shaderProgram = Shader(vShaderPath, fShaderPath);
	Shader lightShaderProgram = Shader(lightvShaderPath, lightfShaderPath);

	// light positions
	float vertices[] = {
		-0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f, -0.5f,
		 0.5f,  0.5f, -0.5f,
		 0.5f,  0.5f, -0.5f,
		-0.5f,  0.5f, -0.5f,
		-0.5f, -0.5f, -0.5f,

		-0.5f, -0.5f,  0.5f,
		 0.5f, -0.5f,  0.5f,
		 0.5f,  0.5f,  0.5f,
		 0.5f,  0.5f,  0.5f,
		-0.5f,  0.5f,  0.5f,
		-0.5f, -0.5f,  0.5f,

		-0.5f,  0.5f,  0.5f,
		-0.5f,  0.5f, -0.5f,
		-0.5f, -0.5f, -0.5f,
		-0.5f, -0.5f, -0.5f,
		-0.5f, -0.5f,  0.5f,
		-0.5f,  0.5f,  0.5f,

		 0.5f,  0.5f,  0.5f,
		 0.5f,  0.5f, -0.5f,
		 0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f,  0.5f,
		 0.5f,  0.5f,  0.5f,

		-0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f, -0.5f,
		 0.5f, -0.5f,  0.5f,
		 0.5f, -0.5f,  0.5f,
		-0.5f, -0.5f,  0.5f,
		-0.5f, -0.5f, -0.5f,

		-0.5f,  0.5f, -0.5f,
		 0.5f,  0.5f, -0.5f,
		 0.5f,  0.5f,  0.5f,
		 0.5f,  0.5f,  0.5f,
		-0.5f,  0.5f,  0.5f,
		-0.5f,  0.5f, -0.5f,
	};

	//物体(obj)
	Model modelObj("Obj/Nanosuit/nanosuit.obj");

	//光源
	unsigned int lightVAO;
	unsigned int lightVBO;
	glGenVertexArrays(1, &lightVAO);
	glBindVertexArray(lightVAO);

	glGenBuffers(1, &lightVBO);
	glBindBuffer(GL_ARRAY_BUFFER, lightVBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	DirLight dirLight(glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3(0.05f, 0.05f, 0.05f), glm::vec3(0.4f, 0.4f, 0.4f), glm::vec3(0.5f, 0.5f, 0.5f));

	glm::vec3 pointLightPositions[] = {
		glm::vec3(-2.0f,4.0f,1.0f),
		glm::vec3(2.0f,1.0f,0.0f)
	};

	PointLight pointLights[] = {
		PointLight(pointLightPositions[0],glm::vec3(0.05f,0.05f,0.05f),glm::vec3(0.8f,0.8f,0.8f),glm::vec3(1.0f,1.0f,1.0f)),
		PointLight(pointLightPositions[1],glm::vec3(0.05f,0.05f,0.05f),glm::vec3(0.8f,0.8f,0.8f),glm::vec3(1.0f,1.0f,1.0f)),
	};

	SpotLight spotLight(camera.Position, camera.Front, glm::vec3(0.2f, 0.2f, 0.2f), glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(1.0f, 1.0f, 1.0f));

	//线框模式
	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	//这些uniform不会更新 可以放到循环外面
	shaderProgram.use();
	shaderProgram.setFloat("material.shininess", 32.0f);
	setLightAllAttribute(shaderProgram, "dirLight", &dirLight);
	setLightAllAttribute(shaderProgram, "pointLights", pointLights, 2);

	lightShaderProgram.use();
	lightShaderProgram.setVec3("lightColor", glm::vec3(1.0, 1.0, 1.0));

	while (!glfwWindowShouldClose(window))
	{
		glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		//矩阵运算
		glm::mat4 view = camera.GetViewMatrix();
		glm::mat4 projection = glm::perspective(glm::radians(camera.Fov), SCR_WIDTH * 1.0f / SCR_HEIGHT, 0.1f, 100.0f);

		glm::mat4 model(1.0f);
		model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));
		//激活着色器
		shaderProgram.use();
		shaderProgram.setMat4("model", model);
		shaderProgram.setMat4("view", view);
		shaderProgram.setMat4("projection", projection);
		shaderProgram.setVec3("viewPos", camera.Position);
		spotLight.position = camera.Position;
		spotLight.direction = camera.Front;
		setLightAllAttribute(shaderProgram, "spotLight", &spotLight);
		modelObj.Draw(shaderProgram);
		
		model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 2.0f));
		model = glm::rotate(model, glm::radians(180.f), glm::vec3(0.0f, 1.0f, 0.0f));
		model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));
		shaderProgram.setMat4("model", model);
		modelObj.Draw(shaderProgram);

		lightShaderProgram.use();
		lightShaderProgram.setMat4("view", view);
		lightShaderProgram.setMat4("projection", projection);
		glBindVertexArray(lightVAO);
		for (unsigned int i = 0; i < 2; i++)
		{
			glm::mat4 lightModel(1.0f);
			lightModel = glm::translate(lightModel, pointLightPositions[i]);
			lightModel = glm::scale(lightModel, glm::vec3(0.2f, 0.2f, 0.2f));
			lightShaderProgram.setMat4("model", lightModel);
			glDrawArrays(GL_TRIANGLES, 0, 36);
		}
		

		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	//这一步是可选的
	glDeleteVertexArrays(1, &lightVAO);
	glDeleteBuffers(1, &lightVBO);
	//glDeleteBuffers(1, &EBO);

	//释放资源
	glfwTerminate();
	return 0;
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	if (action == GLFW_REPEAT || action == GLFW_PRESS)
	{
		if (key == GLFW_KEY_ESCAPE)
		{
			glfwSetWindowShouldClose(window, GL_TRUE);
			return;
		}
		switch (key)
		{
		case GLFW_KEY_UP:
			mixValue += 0.1f;
			if (mixValue >= 1.0f)
				mixValue = 1.0f;
			break;
		case GLFW_KEY_DOWN:
			mixValue -= 0.1f;
			if (mixValue <= 0.0f)
				mixValue = 0.0f;
			break;
		case GLFW_KEY_W:
			camera.ProcessKeyboard(FORWARD);
			break;
		case GLFW_KEY_S:
			camera.ProcessKeyboard(BACKWARD);
			break;
		case GLFW_KEY_A:
			camera.ProcessKeyboard(LEFT);
			break;
		case GLFW_KEY_D:
			camera.ProcessKeyboard(RIGHT);
			break;
		default:
			break;
		}
	}
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
	if (firstMouse)
	{
		firstMouse = false;
		lastX = xpos, lastY = ypos;
	}
	camera.ProcessMouseMovement(xpos - lastX, lastY - ypos);
	lastX = xpos;
	lastY = ypos;
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
	camera.ProcessMouseScroll(yoffset);
}


在这里插入图片描述

甚至我都必须要承认这个可能是比一直使用的箱子要好看多了。使用Assimp,你能够加载互联网上的无数模型。有很多资源网站都提供了多种格式的免费3D模型供你下载。但还是要注意,有些模型会不能正常地载入,纹理的路径会出现问题,或者Assimp并不支持它的格式。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值