重要說(shuō)明 : 在中文版的模型教程的最后, 可以從這里找到帶有頂點(diǎn)和片段著色器的完整的源碼。 不知道別人可以不可以完整的加載起來(lái),并且跑起來(lái), 這里的Demo例子在自己的驗(yàn)證里面是實(shí)現(xiàn)不了加載模型并且顯示
解決 : 我切到英文版本的Model教程中找到對(duì)應(yīng)的的源代碼參考.
注意 : 百度了很多相關(guān)的Demo. 千篇一律, 都不完整, 很容易就在這一節(jié)放棄了整個(gè)學(xué)習(xí)了.
上一節(jié)LearnOpenGL Assimp中通過(guò)brew的方式加載assimp的庫(kù). 在上一節(jié)的操作中, 只需要確保引入的庫(kù)正常無(wú)報(bào)錯(cuò)就接著下面的內(nèi)容.
這里的實(shí)現(xiàn)代碼封裝方式引用源代碼的方式. 因?yàn)镾hader的封裝和自己的MyProgram封裝有點(diǎn)不一樣. 里面的大致步驟都是一樣的. 就是裝載著色器程序的方式有些不一樣, MyProgram是直接加載C字符串的方式. Shader是通過(guò)打開(kāi)文件的形式. 這一章節(jié)就使用Shader的方式實(shí)現(xiàn). 多參考不一樣的做法吧.
3D模型
可以在這里下載turbosquid一些免費(fèi)的3D模型, 格式有很多, 自己注冊(cè)一個(gè)賬號(hào)下載就好.
自己下載了的練習(xí)模型, Obj格式 , 直接放在Xcode項(xiàng)目項(xiàng)目中, 也可以自己通過(guò)Xcode的Inspector窗口查看一些基本的屬性內(nèi)容.

相關(guān)頭文件
camera
shader
model
mesh
頂點(diǎn)著色器
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
TexCoords = aTexCoords;
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
片元著色器
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D texture_diffuse1;
void main()
{
FragColor = texture(texture_diffuse1, TexCoords);
}
頂點(diǎn)著色器和片元著色器程序, 很簡(jiǎn)單, 跟LearnOpenGL 紋理之前編寫(xiě)的著色器一樣
程序
#include "MyModelLoadingDemo.hpp"
#include "glad.h"
#include <GLFW/glfw3.h>
#include "glm.hpp"
#include "matrix_transform.hpp"
#include "type_ptr.hpp"
#include "shader.h"
#include "camera.h"
#include "model.h"
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_M_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_S_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
// camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX_X = SCR_WIDTH / 2.0f;
float lastY_Y = SCR_HEIGHT / 2.0f;
bool firstMouse_M = true;
// timing
float deltaTime_T = 0.0f;
float lastFrame_F = 0.0f;
int runMyModelLoadingDemo() {
int result = glfwInit();
if (result == GL_FALSE) {
printf("glfwInit 初始化失敗");
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
GLFWwindow *window = glfwCreateWindow(600, 400, "My Opengl Window", NULL, NULL);
if(!window) {
printf("window 創(chuàng)建失敗");
}
glfwMakeContextCurrent(window);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_M_callback);
glfwSetScrollCallback(window, scroll_S_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// tell stb_image.h to flip loaded texture's on the y-axis (before loading model).
stbi_set_flip_vertically_on_load(true);
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
// 加載著色器程序, Shader里面的方法是直接通過(guò)打開(kāi)文件的形式, 與之前的
// -------------------------
Shader ourShader("/Users/liliguang/Desktop/LearnOpengl/LearnOpenGl/LearnOpenGl/Demo/ModelLoading(模型加載)/myModelLoadingShader.vs", "/Users/liliguang/Desktop/LearnOpengl/LearnOpenGl/LearnOpenGl/Demo/ModelLoading(模型加載)/myModelLoadingShader.fs");
// 加載模型
// -----------
Model ourModel("/Users/liliguang/Desktop/LearnOpengl/LearnOpenGl/LearnOpenGl/Demo/3DSources/drill.obj");
while (!glfwWindowShouldClose(window))
{
// per-frame time logic
// --------------------
float currentFrame = glfwGetTime();
deltaTime_T = currentFrame - lastFrame_F;
lastFrame_F = currentFrame;
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// don't forget to enable shader before setting uniforms
ourShader.use();
//================================================
// view/projection transformations
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 400.0f);
glm::mat4 view = camera.GetViewMatrix();
ourShader.setMat4("projection", projection);
ourShader.setMat4("view", view);
// render the loaded model
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); // translate it down so it's at the center of the scene
model = glm::scale(model, glm::vec3(0.01f, 0.01f, 0.01f)); // it's a bit too big for our scene, so scale it down
ourShader.setMat4("model", model);
ourModel.Draw(ourShader);
//================================================
glfwSwapBuffers(window);
glfwPollEvents();
}
//程序銷毀
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, deltaTime_T);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime_T);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime_T);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime_T);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
void mouse_M_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse_M)
{
lastX_X = xpos;
lastY_Y = ypos;
firstMouse_M = false;
}
float xoffset = xpos - lastX_X;
float yoffset = lastY_Y - ypos;
lastX_X = xpos;
lastY_Y = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
void scroll_S_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
主要地方, 加載你的頂點(diǎn)著色器和片元著色器程序.
// 加載著色器程序, Shader里面的方法是直接通過(guò)打開(kāi)文件的形式, 與之前的
Shader ourShader("/Users/liliguang/Desktop/LearnOpengl/LearnOpenGl/LearnOpenGl/Demo/ModelLoading(模型加載)/myModelLoadingShader.vs", "/Users/liliguang/Desktop/LearnOpengl/LearnOpenGl/LearnOpenGl/Demo/ModelLoading(模型加載)/myModelLoadingShader.fs");
// 加載模型
Model ourModel("/Users/liliguang/Desktop/LearnOpengl/LearnOpenGl/LearnOpenGl/Demo/3DSources/drill.obj");
代碼實(shí)現(xiàn)效果 :

這里的看起來(lái)什么都是黑的, 因?yàn)樵谄髦? FragColor = texture(texture_diffuse1, TexCoords);. 我們是用紋理的方式去實(shí)現(xiàn). 在程序中目前只加載了obj文件. 通過(guò)Xcode的Inspector來(lái)看是只有白色的一個(gè)3D模型.
texture_diffuse1 是沒(méi)有的, 相當(dāng)于vec3(0.0f,0.0f,0.0f)吧.
優(yōu)化
修改一下Shader程序, 把obj文件格式的3D模型當(dāng)做一個(gè)素模. 就是沒(méi)有任何紋理的素模. 那么我們給模型加上光照. 參照 : LearnOpenGL 基礎(chǔ)光照
熟悉的配方 : shader修改
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
out vec2 TexCoords;
out vec3 Normal;//法線向量
out vec3 FragPos;//片段位置
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
TexCoords = aTexCoords;
gl_Position = projection * view * model * vec4(aPos, 1.0);
Normal = aNormal;
FragPos = vec3(model * vec4(aPos, 1.0f) );
}
#version 330 core
out vec4 FragColor;
out vec4 color;
in vec2 TexCoords;
in vec3 Normal;
in vec3 FragPos;
uniform sampler2D texture_diffuse1;
//光照
struct Light {
vec3 position;
vec3 ambient;//環(huán)境光照
vec3 diffuse;//漫反射光照
vec3 specular;//鏡面反射光照
};
uniform Light light;
void main()
{
//環(huán)境光ambient
//環(huán)境顏色 = 光源顏色 × 環(huán)境光照強(qiáng)度 × 貼圖
vec3 ambient = vec3(1.0f,1.0f,1.0f) * light.ambient ;
//漫反射diffuse
//DiffuseFactor = max(0, dot(N, L))
//漫反射顏色 = 光源顏色 × 漫反射因子(diffuseFactor) × 漫反射光照強(qiáng)度 × 貼圖
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(vec3(0.0f,0.0f,3.0f) - FragPos);
float diffuseFactor = max(dot(norm, lightDir),0.0);
vec3 diffuse = vec3(1.0f,1.0f,1.0f) * diffuseFactor * light.diffuse ;
//鏡面反射specular
//R=reflect(L, N)
//SpecularFactor = pow(max(dot(R,V),0.0), shininess)
//鏡面反射顏色 = 光源顏色 × 鏡面反射因子(SpecularFactor) × 鏡面光照強(qiáng)度 × 貼圖
vec3 viewDir = normalize(vec3(0.0f,0.0f,3.0f) - FragPos);
vec3 reflectDir = reflect(-lightDir , norm);
float specularFactor = pow(max(dot(viewDir, reflectDir),0.0),64.0f);
vec3 specular = vec3(1.0f,1.0f,1.0f) * specularFactor * light.specular ;
//最終片段顏色:環(huán)境顏色+漫反射顏色+鏡面反射顏色
vec3 result = ambient + diffuse + specular;
color = vec4(result , 1.0f);
}
因?yàn)樵?a target="_blank">mesh中實(shí)現(xiàn)void Draw(Shader &shader) 渲染,
對(duì)片元著色器的光照屬性賦值
GLint lightAmbientLoc = glGetUniformLocation(shader.ID, "light.ambient");
GLint lightDiffuseLoc = glGetUniformLocation(shader.ID, "light.diffuse");
GLint lightSpecularLoc = glGetUniformLocation(shader.ID, "light.specular");
glUniform3f(lightAmbientLoc, 0.2f, 0.2f, 0.2f);
glUniform3f(lightDiffuseLoc, 0.5f, 0.5f, 0.5f);
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
優(yōu)化效果

完整的Demo可以參照Github : LearnOpengl, 自己也可以更換幾個(gè)3D模型練習(xí)