重要说明 : 在中文版的模型教程的最后, 可以从这里找到带有顶点和片段着色器的完整的源码。 不知道别人可以不可以完整的加载起来,并且跑起来, 这里的Demo例子在自己的验证里面是实现不了加载模型并且显示
解决 : 我切到英文版本的Model教程中找到对应的的源代码参考.
注意 : 百度了很多相关的Demo. 千篇一律, 都不完整, 很容易就在这一节放弃了整个学习了.
上一节LearnOpenGL Assimp中通过brew的方式加载assimp的库. 在上一节的操作中, 只需要确保引入的库正常无报错就接着下面的内容.
这里的实现代码封装方式引用源代码的方式. 因为Shader的封装和自己的MyProgram封装有点不一样. 里面的大致步骤都是一样的. 就是装载着色器程序的方式有些不一样, MyProgram是直接加载C字符串的方式. Shader是通过打开文件的形式. 这一章节就使用Shader的方式实现. 多参考不一样的做法吧.
3D模型
可以在这里下载turbosquid一些免费的3D模型, 格式有很多, 自己注册一个账号下载就好.
自己下载了的练习模型, Obj格式 , 直接放在Xcode项目项目中, 也可以自己通过Xcode的Inspector窗口查看一些基本的属性内容.
相关头文件
camera
shader
model
mesh
顶点着色器
#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);
}
顶点着色器和片元着色器程序, 很简单, 跟LearnOpenGL 纹理之前编写的着色器一样
程序
#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 创建失败");
}
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里面的方法是直接通过打开文件的形式, 与之前的
// -------------------------
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);
}
主要地方, 加载你的顶点着色器和片元着色器程序.
// 加载着色器程序, Shader里面的方法是直接通过打开文件的形式, 与之前的
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");
代码实现效果 :
这里的看起来什么都是黑的, 因为在片元着色器中, FragColor = texture(texture_diffuse1, TexCoords);. 我们是用纹理的方式去实现. 在程序中目前只加载了obj文件. 通过Xcode的Inspector来看是只有白色的一个3D模型.
texture_diffuse1 是没有的, 相当于vec3(0.0f,0.0f,0.0f)吧.
优化
修改一下Shader程序, 把obj文件格式的3D模型当做一个素模. 就是没有任何纹理的素模. 那么我们给模型加上光照. 参照 : LearnOpenGL 基础光照
熟悉的配方 : 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;//环境光照
vec3 diffuse;//漫反射光照
vec3 specular;//镜面反射光照
};
uniform Light light;
void main()
{
//环境光ambient
//环境颜色 = 光源颜色 × 环境光照强度 × 贴图
vec3 ambient = vec3(1.0f,1.0f,1.0f) * light.ambient ;
//漫反射diffuse
//DiffuseFactor = max(0, dot(N, L))
//漫反射颜色 = 光源颜色 × 漫反射因子(diffuseFactor) × 漫反射光照强度 × 贴图
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) × 镜面光照强度 × 贴图
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 ;
//最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色
vec3 result = ambient + diffuse + specular;
color = vec4(result , 1.0f);
}
因为在mesh中实现void Draw(Shader &shader) 渲染,
对片元着色器的光照属性赋值
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);
优化效果
完整的Demo可以参照Github : LearnOpengl, 自己也可以更换几个3D模型练习
网友评论