LearnOpenGL学习笔记—光照04:Lighting Maps
0 前言
本节笔记对应官网学习内容:光照贴图
在光照01中我们初识了颜色的概念,试着做了一下环境光
在光照02中我们学习了一下Phong光照模型与Blinn-Phong光照模型
在光照03中我们初识了材质的概念并封装了对应的类
尽管在上一节中,我们讨论了让每个物体都拥有自己独特的材质从而对光照做出不同的反应的方法。
这样能够很容易在一个光照的场景中给每个物体一个独特的外观,但是这仍不能对一个物体的视觉输出提供足够多的灵活性。
在上一节中,我们将整个物体的材质定义为一个整体,但现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成。
一辆汽车:它的外壳非常有光泽,车窗会部分反射周围的环境,轮胎不会那么有光泽,所以它没有镜面高光,轮毂非常闪亮。
汽车同样会有漫反射和环境光颜色,它们在整个物体上也不会是一样的,汽车有着许多种不同的环境光/漫反射颜色。
总之,这样的物体在不同的部件上都有不同的材质属性。
所以,上一节中的那个材质系统是肯定不够的,它只是一个最简单的模型,所以我们需要拓展之前的系统,引入漫反射和镜面光贴图(Map)。
这允许我们对物体的漫反射分量(以及间接地对环境光分量,它们几乎总是一样的)和镜面光分量有着更精确的控制。
1 diffuse+ambient
我们希望通过某种方式对物体的每个片段单独设置漫反射颜色,这很像在之前教程中详细讨论过的纹理贴图。
我们对同样的原理(使用一张覆盖物体的图像)使用不同的名字,让我们能够逐片段索引其独立的颜色值。
在光照场景中,它通常叫做一个漫反射贴图(Diffuse Map),它是一个表现了物体所有的漫反射颜色的纹理图像。
为了使用漫反射贴图,我们来改改shader的代码
在片元着色器中
in vec2 TexCoord;
struct Material {
vec3 ambient;
sampler2D diffuse;
vec3 specular;
float shininess;
};
......
vec3 diffuse = texture(material.diffuse,TexCoord).rgb;
在顶点着色器中
out vec2 TexCoord;
......
void main(){
......
TexCoord=aTexCoord;
......
}
在main中设置顶点属性时
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 10 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
//glEnableVertexAttribArray(1);
// 法向量属性
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
// 贴图uv属性
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(3);
我们加上对应的漫反射纹理坐标,并且把之前有关笑脸木箱的代码去掉
float vertices[] = {
//position //normal //TexCoord
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-15.0f, -4.0f, -15.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
15.0f, -4.0f, -15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
15.0f, -4.0f, 15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
15.0f, -4.0f, 15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-15.0f, -4.0f, 15.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-15.0f, -4.0f, -15.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
material.h文件中
unsigned int diffuse;
......
Material(Shader* _shader, unsigned int _diffuse, glm::vec3 _specular, glm::vec3 _ambient, float _shininess);
material.cpp中_diffuse也改成unsigned int
并且导入material
#pragma region Init Material
Material* myMaterial = new Material(myShader,
LoadImageToGPU("container.jpg", GL_RGB, GL_RGB, 0),
glm::vec3 (0.5f, 0.5f, 0.5f),
glm::vec3 (1.0f, 0.5f, 0.31f),
150.0f);
#pragma endregion
为了设置uniform,我们在shader.h设置一个方法
void SetUniform1i(const char* paraNameString, int slot);
在cpp中写出对应的构造
void Shader::SetUniform1i(const char * paraNameString, int slot)
{
glUniform1i(glGetUniformLocation(ID, paraNameString), slot);
}
回到main中加入uniform
myMaterial->shader->SetUniform1i("material.diffuse", 0);
接下来我们用新的图当材质(可右键下载到项目目录,然后VS右键添加现有项)
然后修改main中的material部分
#pragma region Init Material
Material* myMaterial = new Material(myShader,
LoadImageToGPU("container2.png", GL_RGBA, GL_RGBA, 0),
glm::vec3 (1.0f, 1.0f, 1.0f),
glm::vec3 (1.0f, 1.0f, 1.0f),
150.0f);
#pragma endregion
片元着色器中物体颜色去掉,只留下定义,并且把贴图应用到漫反射和环境光上
#version 330 core
//着色点和法向
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;
struct Material {
vec3 ambient;
sampler2D diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
out vec4 FragColor;
//uniform sampler2D ourTexture;
//uniform sampler2D ourFace;
uniform vec3 objColor;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 ambientColor;
uniform vec3 cameraPos;
void main(){
vec3 r=lightPos-FragPos;
//光线方向
vec3 lightDir = normalize(r);
//距离系数
float coefficient=50/(pow(r.x,2)+pow(r.y,2)+pow(r.z,2));
//法向
vec3 normal = normalize(Normal);
//漫反射
float diff = max(dot(normal, lightDir), 0.0);
//vec3 diffuse = material.diffuse * coefficient * diff * lightColor;
vec3 diffuse = coefficient * diff * texture(material.diffuse,TexCoord).rgb * lightColor;
//观察方向
vec3 cameraVec= normalize(cameraPos - FragPos);
//Blinn-Phong光照模型镜面反射
vec3 halfwarDir = normalize(lightDir + cameraVec);
float specularAmount = pow(max(dot(normal, halfwarDir), 0.0),material.shininess);
vec3 specular = material.specular * coefficient * specularAmount * lightColor;
//环境光
vec3 ambient= material.ambient * ambientColor * texture(material.diffuse,TexCoord).rgb;
FragColor=vec4(specular+diffuse+ambient,1.0f);
}
}
然后在main文件中加一句
glUniform3f(glGetUniformLocation(myShader->ID, "objColor"), 1.0f, 1.0f, 1.0f);
运行得到
2 specular
镜面高光看起来有些奇怪,因为我们的物体大部分都是木头,我们知道木头不应该有这么强的镜面高光的,并且我们知道钢铁应该是有一些镜面高光的。
所以,我们想要让物体的某些部分以不同的强度显示镜面高光。这个问题看起来和漫反射贴图非常相似。
我们同样可以使用一个专门用于镜面高光的纹理贴图。
这也就意味着我们需要生成一个黑白的(如果你想得话也可以是彩色的)纹理,来定义物体每部分的镜面光强度。
下面是一个镜面光贴图(Specular Map)的例子(可右键下载到项目目录,然后VS右键添加现有项):
- 由于箱子大部分都由木头所组成,而且木头材质应该没有镜面高光,所以漫反射纹理的整个木头部分全部都转换成了黑色。
箱子钢制边框的镜面光强度是有细微变化的,钢铁本身会比较容易受到镜面高光的影响,而裂缝则不会。
按照上面改diffuse的方法 把specular改成sample2D类型的
在material的h与cpp文件改一下specular的类型
在片元着色器内的结构体部分改一下,因为坐标都一样,就用漫反射贴图的坐标就好
struct Material {
vec3 ambient;
sampler2D diffuse;
sampler2D specular;
float shininess;
};
......
//Set Material -> Textures
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, myMaterial->diffuse);
glActiveTexture(GL_TEXTURE0+1);
glBindTexture(GL_TEXTURE_2D, myMaterial->specular);
......
vec3 specular = texture(material.specular,TexCoord).rgb * coefficient * specularAmount * lightColor;
在main中载入图片
#pragma region Init Material
Material* myMaterial = new Material(myShader,
LoadImageToGPU("container2.png", GL_RGBA, GL_RGBA, 0),
LoadImageToGPU("container2_specular.png", GL_RGBA, GL_RGBA, 1),
glm::vec3 (1.0f, 1.0f, 1.0f),
150.0f);
#pragma endregion
......
//Set Material -> Textures
glActiveTexture(GL_TEXTURE0+ Shader::DIFFUSE);
glBindTexture(GL_TEXTURE_2D, myMaterial->diffuse);
glActiveTexture(GL_TEXTURE0+ Shader::SPECULAR);
glBindTexture(GL_TEXTURE_2D, myMaterial->specular);
......
myMaterial->shader->SetUniform1i("material.specular", 1);
运行后可以看到高光只在边框上存在了
通过使用漫反射和镜面光贴图,我们可以给相对简单的物体添加大量的细节。
我们甚至可以使用法线/凹凸贴图(Normal/Bump Map)或者反射贴图(Reflection Map)给物体添加更多的细节,但这些将会留到之后的教程中。
我们接下来把贴图通道的用法写进shader.h
enum Slot
{
DIFFUSE.
SPECULAR
};
在main中
#pragma region Init Material
Material* myMaterial = new Material(myShader,
LoadImageToGPU("container2.png", GL_RGBA, GL_RGBA, Shader::DIFFUSE),
LoadImageToGPU("container2_specular.png", GL_RGBA, GL_RGBA, Shader::SPECULAR),
glm::vec3 (1.0f, 1.0f, 1.0f),
150.0f);
#pragma endregion
......
myMaterial->shader->SetUniform1i("material.diffuse", Shader::DIFFUSE);
myMaterial->shader->SetUniform1i("material.specular", Shader::SPECULAR);
3 Emission
放射光贴图(Emission Map)的东西,它是一个储存了每个片段的发光值(Emission Value)的贴图。
发光值是一个包含(假设)光源的物体发光(Emit)时可能显现的颜色,这样的话物体就能够忽略光照条件进行发光(Glow)。
将这个纹理作为放射光贴图添加到箱子上,产生这些字母都在发光的效果
在material的h文件中
#pragma once
#include "Shader.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
class Material
{
public:
Shader * shader;
unsigned int diffuse;
unsigned int specular;
unsigned int emission;
glm::vec3 ambient;
float shininess;
Material(Shader* _shader, unsigned int _diffuse, unsigned int _specular, unsigned int _emission, glm::vec3 _ambient, float _shininess);
~Material();
};
在material的cpp文件中
#include "Material.h"
Material::Material(Shader * _shader, unsigned int _diffuse, unsigned int _specular, unsigned int _emission, glm::vec3 _ambient, float _shininess):
shader(_shader),
diffuse(_diffuse),
specular(_specular),
emission(_emission),
ambient(_ambient),
shininess(_shininess)
{
}
Material::~Material()
{
}
shader头文件中加上这个EMISSSION分量
enum Slot
{
DIFFUSE,
SPECULAR,
EMISSSION
};
在main中也加上EMISSSION分量
#pragma region Init Material
Material* myMaterial = new Material(myShader,
LoadImageToGPU("container2.png", GL_RGBA, GL_RGBA, Shader::DIFFUSE),
LoadImageToGPU("container2_specular.png", GL_RGBA, GL_RGBA, Shader::SPECULAR),
LoadImageToGPU("matrix.jpg", GL_RGB, GL_RGB, Shader::EMISSSION),
glm::vec3 (1.0f, 1.0f, 1.0f),
150.0f);
#pragma endregion
......
myMaterial->shader->SetUniform1i("material.emission", Shader::EMISSSION);
在片元着色中加上发光的代码,当然也可以加些好玩的东西,比如中间的数字在动
//发光
vec3 emission;
if( texture(material.specular,TexCoord).rgb== vec3(0,0,0) ){
emission= coefficient * texture(material.emission,TexCoord).rgb;
//fun
emission = texture(material.emission,TexCoord + vec2(0.0,time)).rgb;//moving
emission = coefficient * emission * (sin(time) * 0.5 + 0.5) * 2.0;//fading
}
实际效果会动,不过懒得录动图就这样吧_(:з」∠)_,上面的time变量用uniform从main里glfwGetTime()传过去就好
4 完整代码
main.cpp
#include <iostream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "Shader.h"
#include "Camera.h"
#include "Material.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#pragma region Model Data
float vertices[] = {
//position //normal //TexCoord
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-15.0f, -4.0f, -15.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
15.0f, -4.0f, -15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
15.0f, -4.0f, 15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
15.0f, -4.0f, 15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-15.0f, -4.0f, 15.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-15.0f, -4.0f, -15.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
glm::vec3 cubePositions[] = {
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3(2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3(1.3f, -2.0f, -2.5f),
glm::vec3(1.5f, 2.0f, -2.5f),
glm::vec3(1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)
};
#pragma endregion
#pragma region Camera Declare
//建立camera
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
Camera camera(cameraPos, cameraTarget, cameraUp);
#pragma endregion
#pragma region Input Declare
//移动用
float deltaTime = 0.0f; // 当前帧与上一帧的时间差
float lastFrame = 0.0f; // 上一帧的时间
void processInput(GLFWwindow* window) {
//看是不是按下esc键 然后退出
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
//更流畅点的摄像机系统
if (deltaTime != 0) {
camera.cameraPosSpeed = 5 * deltaTime;
}
//camera前后左右根据镜头方向移动
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.PosUpdateForward();
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.PosUpdateBackward();
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.PosUpdateLeft();
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.PosUpdateRight();
if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
camera.PosUpdateUp();
if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
camera.PosUpdateDown();
}
float lastX;
float lastY;
bool firstMouse = true;
//鼠标控制镜头方向
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
if (firstMouse == true)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float deltaX, deltaY;
float sensitivity = 0.05f;
deltaX = (xpos - lastX)*sensitivity;
deltaY = (ypos - lastY)*sensitivity;
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(deltaX, deltaY);
};
//缩放
float fov = 45.0f;
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
if (fov >= 1.0f && fov <= 45.0f)
fov -= yoffset;
if (fov <= 1.0f)
fov = 1.0f;
if (fov >= 45.0f)
fov = 45.0f;
}
#pragma endregion
unsigned int LoadImageToGPU(const char* filename, GLint internalFormat, GLenum format, int textureSlot) {
unsigned int TexBuffer;
glGenTextures(1, &TexBuffer);
glActiveTexture(GL_TEXTURE0 + textureSlot);
glBindTexture(GL_TEXTURE_2D, TexBuffer);
// 为当前绑定的纹理对象设置环绕、过滤方式
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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// 加载并生成纹理
int width, height, nrChannel;
unsigned char *data = stbi_load(filename, &width, &height, &nrChannel, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
printf("Failed to load texture");
}
stbi_image_free(data);
return TexBuffer;
}
int main() {
#pragma region Open a Window
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Open GLFW Window
GLFWwindow* window = glfwCreateWindow(800,600,"My OpenGL Game",NULL,NULL);
if(window == NULL)
{
printf("Open window failed.");
glfwTerminate();
return - 1;
}
glfwMakeContextCurrent(window);
//关闭鼠标显示
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
//回调函数监听鼠标
glfwSetCursorPosCallback(window, mouse_callback);
//回调函数监听滚轮
glfwSetScrollCallback(window, scroll_callback);
//Init GLEW
glewExperimental = true;
if (glewInit() != GLEW_OK)
{
printf("Init GLEW failed.");
glfwTerminate();
return -1;
}
glViewport(0, 0, 800, 600);
glEnable(GL_DEPTH_TEST);
#pragma endregion
#pragma region Init Shader Program
Shader* myShader = new Shader("vertexSource.vert", "fragmentSource.frag");
#pragma endregion
#pragma region Init Material
Material* myMaterial = new Material(myShader,
LoadImageToGPU("container2.png", GL_RGBA, GL_RGBA, Shader::DIFFUSE),
LoadImageToGPU("container2_specular.png", GL_RGBA, GL_RGBA, Shader::SPECULAR),
LoadImageToGPU("matrix.jpg", GL_RGB, GL_RGB, Shader::EMISSSION),
glm::vec3 (1.0f, 1.0f, 1.0f),
150.0f);
#pragma endregion
#pragma region Init and Load Models to VAO,VBO
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER,VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
//glEnableVertexAttribArray(1);
// 法向量属性
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
// 贴图uv属性
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(3);
#pragma endregion
#pragma region Init and Load Textures
//坐标翻转
stbi_set_flip_vertically_on_load(true);
//材质
//unsigned int TexBufferA;
//TexBufferA = LoadImageToGPU("container.jpg",GL_RGB,GL_RGB,0);
//unsigned int TexBufferB;
//TexBufferB = LoadImageToGPU("awesomeface.png", GL_RGBA, GL_RGBA, 1);
#pragma endregion
#pragma region Prepare MVP matrices
//model
glm::mat4 modelMat;
//view
glm::mat4 viewMat;
//projection
glm::mat4 projMat;
#pragma endregion
while (!glfwWindowShouldClose(window))
{
//Process Input
processInput(window);
//Clear Screen
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (unsigned int i = 0; i < 10 ;i++)
{
//Set Model matrix
modelMat = glm::translate(glm::mat4(1.0f), cubePositions[i]);
float angle = 20.0f * i;
modelMat = glm::rotate(modelMat, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
//Set view matrix
viewMat = camera.GetViewMatrix();
//Set projection matrix
projMat = glm::perspective(glm::radians(fov), 800.0f / 600.0f, 0.1f, 100.0f);
//Set Material -> Shader Program
myShader->use();
//Set Material -> Textures
glActiveTexture(GL_TEXTURE0+ Shader::DIFFUSE);
glBindTexture(GL_TEXTURE_2D, myMaterial->diffuse);
glActiveTexture(GL_TEXTURE0+ Shader::SPECULAR);
glBindTexture(GL_TEXTURE_2D, myMaterial->specular);
glActiveTexture(GL_TEXTURE0 + Shader::EMISSSION);
glBindTexture(GL_TEXTURE_2D, myMaterial->emission);
//Set Material -> Uniforms
//glUniform1i(glGetUniformLocation(myShader->ID, "ourTexture"), 0);
//glUniform1i(glGetUniformLocation(myShader->ID, "ourFace"), 1);
glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "modelMat"), 1, GL_FALSE, glm::value_ptr(modelMat));
glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "viewMat"), 1, GL_FALSE, glm::value_ptr(viewMat));
glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "projMat"), 1, GL_FALSE, glm::value_ptr(projMat));
glUniform3f(glGetUniformLocation(myShader->ID, "objColor"), 1.0f, 1.0f, 1.0f);
glUniform3f(glGetUniformLocation(myShader->ID, "ambientColor"), 0.2f, 0.2f, 0.2f);
glUniform3f(glGetUniformLocation(myShader->ID, "lightPos"), 0.0f,3.0f,3.0f);
glUniform3f(glGetUniformLocation(myShader->ID, "lightColor"), 1.0f, 1.0f, 1.0f);
glUniform3f(glGetUniformLocation(myShader->ID, "cameraPos"), camera.Position.x, camera.Position.y, camera.Position.z);
glUniform1f(glGetUniformLocation(myShader->ID, "time"), glfwGetTime());
myMaterial->shader->SetUniform3f("material.ambient", myMaterial->ambient);
myMaterial->shader->SetUniform1i("material.diffuse", Shader::DIFFUSE);
myMaterial->shader->SetUniform1i("material.specular", Shader::SPECULAR);
myMaterial->shader->SetUniform1i("material.emission", Shader::EMISSSION);
myMaterial->shader->SetUniform1f("material.shininess", myMaterial->shininess);
//Set Model
glBindVertexArray(VAO);
//DrawCall
glDrawArrays(GL_TRIANGLES, 0, 36);
if (i == 0) {
glDrawArrays(GL_TRIANGLES, 36, 6);
}
}
//Clean up prepare for next render loop
glfwSwapBuffers(window);
glfwPollEvents();
//Recording the time
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
}
//Exit program
glfwTerminate();
return 0;
}
vertexSource.vert
#version 330 core
layout(location = 0) in vec3 aPos; // 位置变量的属性位置值为 0
//layout(location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
layout(location = 2) in vec3 aNormal; // 法向量的属性位置值为 2
layout(location = 3) in vec2 aTexCoord; // uv变量的属性位置值为 3
//out vec4 vertexColor;
out vec2 TexCoord;
//着色点和法向
out vec3 FragPos;
out vec3 Normal;
//uniform mat4 transform;
uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projMat;
void main(){
gl_Position = projMat * viewMat * modelMat * vec4(aPos.xyz,1.0);
Normal =mat3(transpose(inverse(modelMat)))*aNormal;
FragPos=(modelMat * vec4(aPos.xyz,1.0)).xyz;
//vertexColor = vec4(aColor,1.0);
//TexCoord = aTexCoord;
TexCoord=aTexCoord;
}
fragmentSource.frag
#version 330 core
//着色点和法向
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;
struct Material {
vec3 ambient;
sampler2D diffuse;
sampler2D specular;
sampler2D emission;
float shininess;
};
uniform Material material;
out vec4 FragColor;
//uniform sampler2D ourTexture;
//uniform sampler2D ourFace;
uniform vec3 objColor;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 ambientColor;
uniform vec3 cameraPos;
uniform float time;
void main(){
vec3 r=lightPos-FragPos;
//光线方向
vec3 lightDir = normalize(r);
//距离系数
float coefficient=50/(pow(r.x,2)+pow(r.y,2)+pow(r.z,2));
//法向
vec3 normal = normalize(Normal);
//漫反射
float diff = max(dot(normal, lightDir), 0.0);
//vec3 diffuse = material.diffuse * coefficient * diff * lightColor;
vec3 diffuse = coefficient * diff * texture(material.diffuse,TexCoord).rgb * lightColor;
//观察方向
vec3 cameraVec= normalize(cameraPos - FragPos);
//Blinn-Phong光照模型镜面反射
vec3 halfwarDir = normalize(lightDir + cameraVec);
float specularAmount = pow(max(dot(normal, halfwarDir), 0.0),material.shininess);
vec3 specular = texture(material.specular,TexCoord).rgb * coefficient * specularAmount * lightColor;
//环境光
vec3 ambient= material.ambient * ambientColor * texture(material.diffuse,TexCoord).rgb;
//发光
vec3 emission;
if( texture(material.specular,TexCoord).rgb== vec3(0,0,0) ){
emission= coefficient * texture(material.emission,TexCoord).rgb;
//fun
emission = texture(material.emission,TexCoord + vec2(0.0,time/2)).rgb;//moving
emission = coefficient * emission * (sin(time) * 0.5 + 0.5) * 2.0;//fading
}
FragColor=vec4(specular+diffuse+ambient+emission,1.0f);
}
Shader.cpp
#include "Shader.h"
#include <iostream>
#include <fstream>
#include <SStream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
using namespace std;
Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
//从文件路径中获取顶点/片段着色器
ifstream vertexFile;
ifstream fragmentFile;
stringstream vertexSStream;
stringstream fragmentSStream;
//打开文件
vertexFile.open(vertexPath);
fragmentFile.open(fragmentPath);
//保证ifstream对象可以抛出异常:
vertexFile.exceptions(ifstream::failbit || ifstream::badbit);
fragmentFile.exceptions(ifstream::failbit || ifstream::badbit);
try
{
if (!vertexFile.is_open() || !fragmentFile.is_open())
{
throw exception("open file error");
}
//读取文件缓冲内容到数据流
vertexSStream << vertexFile.rdbuf();
fragmentSStream << fragmentFile.rdbuf();
//转换数据流到string
vertexString = vertexSStream.str();
fragmentString = fragmentSStream.str();
vertexSource = vertexString.c_str();
fragmentSource = fragmentString.c_str();
// 编译着色器
unsigned int vertex, fragment;
// 顶点着色器
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vertexSource, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// 片段着色器
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fragmentSource, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// 着色器程序
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// 删除着色器,它们已经链接到我们的程序中了,已经不再需要了
glDeleteShader(vertex);
glDeleteShader(fragment);
}
catch (const std::exception& ex)
{
printf(ex.what());
}
}
void Shader::use()
{
glUseProgram(ID);
}
void Shader::SetUniform3f(const char * paraNameString, glm::vec3 param)
{
glUniform3f(glGetUniformLocation(ID, paraNameString), param.x, param.y, param.z);
}
void Shader::SetUniform1f(const char * paraNameString, float param)
{
glUniform1f(glGetUniformLocation(ID, paraNameString), param);
}
void Shader::SetUniform1i(const char * paraNameString, int slot)
{
glUniform1i(glGetUniformLocation(ID, paraNameString), slot);
}
void Shader::checkCompileErrors(unsigned int ID, std::string type) {
int sucess;
char infolog[512];
if (type != "PROGRAM")
{
glGetShaderiv(ID, GL_COMPILE_STATUS, &sucess);
if (!sucess)
{
glGetShaderInfoLog(ID, 512, NULL, infolog);
cout << "shader compile error:" << infolog << endl;
}
}
else
{
glGetProgramiv(ID, GL_LINK_STATUS, &sucess);
if (!sucess)
{
glGetProgramInfoLog(ID, 512, NULL, infolog);
cout << "program linking error:" << infolog << endl;
}
}
}
Shader.h
#pragma once
#include <string>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
class Shader
{
public:
Shader(const char* vertexPath, const char* fragmentPath);
std::string vertexString;
std::string fragmentString;
const char* vertexSource;
const char* fragmentSource;
unsigned int ID; //Shader Program ID
enum Slot
{
DIFFUSE,
SPECULAR,
EMISSSION
};
void use();
void SetUniform3f(const char* paraNameString,glm::vec3 param);
void SetUniform1f(const char* paraNameString, float param);
void SetUniform1i(const char* paraNameString, int slot);
//~Shader();
private:
void checkCompileErrors(unsigned int ID, std::string type);
};
Material.cpp
#include "Material.h"
Material::Material(Shader * _shader, unsigned int _diffuse, unsigned int _specular, unsigned int _emission, glm::vec3 _ambient, float _shininess):
shader(_shader),
diffuse(_diffuse),
specular(_specular),
emission(_emission),
ambient(_ambient),
shininess(_shininess)
{
}
Material::~Material()
{
}
Material.h
#pragma once
#include "Shader.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
class Material
{
public:
Shader * shader;
unsigned int diffuse;
unsigned int specular;
unsigned int emission;
glm::vec3 ambient;
float shininess;
Material(Shader* _shader, unsigned int _diffuse, unsigned int _specular, unsigned int _emission, glm::vec3 _ambient, float _shininess);
~Material();
};