LearnOpenGL学习笔记—光照02:Lighting Basis
- 0 前言
- 1 简单光照模型原理概述(Blinn-Phong光照模型)
- 1.1 漫反射
- 1.2 镜面反射/高光(与Phong光照模型对比)
- 1.3 环境光
- 1.4 合成
- 2 实现
- 2.1 Phong光照模型
- 2.2 Blinn-Phong光照模型/对比
- 3 完整代码
0 前言
本节笔记对应官网学习内容:基础光照
在光照01中我们初识了颜色的概念,试着做了一下环境光
这一节我们学习一下一些简单的光照模型
原教程以Phong光照模型引入,我们以Blinn-Phong光照模型引入
1 简单光照模型原理概述(Blinn-Phong光照模型)
OpenGL光照使用的是简化的模型,对现实的情况进行近似。
这些光照模型都是基于我们对光的物理特性的理解。
其中一个模型被称为Blinn-Phong光照模型。
Blinn-Phong光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。
- 环境光照(Ambient Lighting):上面的茶杯图中,有些部分是背光面但是仍然有颜色,因为其接受了各种间接光照。
来自环境的反射光很复杂,为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。 - 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。
它是Blinn-Phong光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。 - 镜面光照(Specular Lighting):模拟有光泽物体上面出现的高光处。
镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。
在进行这三组光照之前,我们定义一些东西
我们目前的光照考虑在着色点上,着色点在局部上肯定是一个平面
我们可以定义这几个方向(单位向量),平面上的法线n,以及观测方向v,光照方向l,表面材质
我们考虑任何一个点的着色情况,就只看它自己,不考虑其他物体的存在的影响,即没有阴影什么的
1.1 漫反射
-
一束光打到物体表面会漫反射到各个方向上
-
但是表面朝向和光照的夹角不一样的时候,物体的明暗程度就不一样
以下的紫色方块上的黑边代表一个着色点,光看成离散的光线,每根光线带着一样的能量。
一个着色点单位面积能接收到多少能量,表示着它能多亮。
角度不同,单位面积受到的光线“数量”不同,收到的能量也不同。
Lambert’s余弦定律定义了,收到的能量,与 法线和光照方向的余弦 成正比。
-
我们假设光来自于一个点光源,点光源无时无刻不散发出能量,我们可以以球形的方式来看光能量的衰减
在某时刻发出的光能量,在扩散的过程中,扩散的距离变化了,也就是变成球壳的表面积变大了,所以能量在单位点上就变得越来越少了。
(假设真空中不损耗)与点光源距离1的时候,我们定义此时的光源强度为I,传播到距离为r的时候,也就变成了I/r?。
也就是接收到的光源强度也和距离的平方成反比。
-
我们知道距离,就知道了有多少光传到了这个附近,我们又知道根据角度有多少光被吸收,因此可以得到漫反射的公式。
max函数的意义是,如果夹角超过90度我们就当做照不到了。
最前面的系数表示该点对光的吸收效果,表面的材质效果
既然漫反射在各个方向上都是一样的,也就意味无论从哪个方向看漫反射的效果都是相等的(即公式中与观察方向无关)。
-
以下是一些效果图可以助于理解
1.2 镜面反射/高光(与Phong光照模型对比)
-
我们能看到镜面反射(高光)的情况,应该是观察方向和镜面反射方向足够接近的时候
-
观察方向和镜面反射方向足够接近的时候,其实也就是半程向量与法线方向足够接近的时候
我们衡量能不能看到高光,只要看n和h是不是接近
最前面系数为镜面反射系数
在Blinn-Phong光照模型中,简化了物体表面对镜面反射的吸收。
-
虽然n和h点乘的得到的cos可以代表n和h是不是接近,但是cos对于角度范围的容忍度太高,我们需要控制高光的角度可见范围,所以上式会加个指数。
在这个光照模型里大概会控制指数在100到200。
-
以下的图表示了公式的效果
-
如果不用半程向量,直接用反射方向与观察方向,那么就是Phong光照模型,Blinn-Phong光照模型是对它的一种改进。
因为计算上来说,半程向量比反射方向的计算来得容易得多。并且如果观察方向和光线方向同向时,此时与反射方向的夹角大于90度,高光就被认为消失了,我们会在后面的实现中加以比较。
1.3 环境光
对于环境光,直接给着色点赋值一个常量,
环境光假设能量是Iα 表面系数Kα
得到一个(假设的)常数的环境光
1.4 合成
最后合成就得到了Blinn-Phong光照模型
2 实现
Blinn-Phong光照模型只是是对Phong光照模型,在镜面反射上的改进。
那么我们可以先实现一下Phong光照模型,然后稍加改动得到Blinn-Phong光照模型。
2.1 Phong光照模型
我们会用到法向量,所以在顶点着色器的输入里加上
layout(location = 3) in vec3 aNormal; // 法向量的属性位置值为 2
然后我们回去修改顶点数组,加上法向量,最后加一组地面
//顶点 uv 法向
float vertices[] = {-0.5f, -0.5f, -0.5f, 0.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, 1.0f, 0.0f, 0.0f, -1.0f,0.5f, 0.5f, -0.5f, 1.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, 0.0f, 0.0f, 0.0f, -1.0f,-0.5f, -0.5f, 0.5f, 0.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, 1.0f, 0.0f, 0.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.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, 0.0f, 0.0f, 0.0f, 1.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.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, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.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, 1.0f, 0.0f, 1.0f, 0.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, 1.0f, 1.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, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.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, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.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, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,-15.0f, -3.0f, -15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,15.0f, -3.0f, -15.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f,15.0f, -3.0f, 15.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,15.0f, -3.0f, 15.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-15.0f, -3.0f, 15.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,-15.0f, -3.0f, -15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
};
渲染循环中把地面加上
if (i == 0) {glDrawArrays(GL_TRIANGLES, 36, 6);
}
改一下顶点属性
// 位置属性
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);
// uv属性
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
// 法向量属性
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(5 * sizeof(float)));
glEnableVertexAttribArray(3);
改写顶点着色器的内容,颜色属性我们不需要用,输出着色点和法向
#version 330 core
layout(location = 0) in vec3 aPos; // 位置变量的属性位置值为 0
//layout(location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
layout(location = 2) in vec2 aTexCoord; // uv变量的属性位置值为 2
layout(location = 3) in vec3 aNormal; // 法向量的属性位置值为 2//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;
}
- 我们知道我们要把法向量从顶点着色器传到片段着色器,把法向量也转换为世界空间坐标,但是这不是乘以一个模型矩阵就能搞定的。
- 法向量只是一个方向向量,我们只希望对它实施缩放和旋转变换,所以我们只选用模型矩阵左上角3×3的矩阵。
其次,如果模型矩阵执行了不等比缩放,顶点的改变会导致法向量不再垂直于表面了。
- 即使是对于着色器来说,逆矩阵也是一个开销比较大的运算,因此,只要可能就应该避免在着色器中进行逆矩阵运算,因为每个顶点都要GPU算一次。
用作学习目这样做是可以的,但是对于一个对效率有要求的应用来说,在绘制之前最好用CPU计算出法线矩阵,然后通过uniform把值传递给着色器(像模型矩阵一样)。
接下来改片元着色器,这时候会用到光的位置和光的颜色,并且重新设置一下环境光,我们回到main,在改Uniform的那群代码后面加上,同时改环境光,去掉上节笔记的那个物体颜色。
glUniform3f(glGetUniformLocation(myShader->ID, "ambientColor"), 0.2f, 0.2f, 0.2f);
glUniform3f(glGetUniformLocation(myShader->ID, "lightPos"), 0.0f,8.0f,8.0f);
glUniform3f(glGetUniformLocation(myShader->ID, "lightColor"), 1.0f, 1.0f, 1.0f);
这时候根据公式修改片元着色器,此时我们只用漫反射和环境光
#version 330 core
//in vec4 vertexColor;
in vec2 TexCoord;
//着色点和法向
in vec3 FragPos;
in vec3 Normal;out vec4 FragColor; uniform sampler2D ourTexture;
uniform sampler2D ourFace;uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 ambientColor;void main(){ //FragColor = mix(texture(ourTexture,TexCoord),texture(ourFace,TexCoord),texture(ourFace,TexCoord).a*0.2);vec3 r=lightPos-FragPos;//光线方向vec3 lightDir = normalize(r);//距离系数float coefficient=180/(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 = coefficient * diff * lightColor;//材质vec4 objColor= mix(texture(ourTexture,TexCoord),texture(ourFace,TexCoord),texture(ourFace,TexCoord).a*0.2);FragColor=vec4(diffuse+ambientColor,1.0)*objColor;}
我们把原来墨绿色的背景变黑
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
此时运行应该能得到没有高光效果的图(示意)
下面处理镜面反射
在main函数中将相机位置传到片元着色器
glUniform3f(glGetUniformLocation(myShader->ID, "cameraPos"), camera.Position.x, camera.Position.y, camera.Position.z);
在片元着色器中接收
uniform vec3 cameraPos;
......
//观察方向
vec3 cameraVec=normalize(cameraPos - FragPos);
在片元着色器中计算镜面反射向量
//Phong光照模型镜面反射
vec3 reflectDir = reflect(-lightDir, normal);
float specularAmount = pow(max(dot(cameraVec, reflectDir), 0.0),100);
vec3 specular = coefficient * specularAmount * lightColor;FragColor=vec4(diffuse+ambientColor+specular,1.0)*objColor;
- reflect函数要求第一个向量是从光源指向片段位置的向量,但是lightDir当前正好相反,是从片段指向光源,所以取反。
- 第二个参数要求是一个法向量,所以我们提供的是已标准化的norm向量。
我们把光源位置变一下 可以更直接看出效果
glUniform3f(glGetUniformLocation(myShader->ID, "lightPos"), 0.0f,3.0f,3.0f);
为了明显起见,我们把材质换成固有色
vec4 objColor=vec4(1.0f, 0.5f, 0.31f,0.0f);
得到结果
2.2 Blinn-Phong光照模型/对比
在Phong光照模型的基础上,我们用半程向量算镜面反射,同时为了做对比,用按键b来控制用哪个光照模型
另外一般地,使用Blinn-Phong模型时要得到相同强度的镜面光,镜面系数需要为Phong模型的2-4倍
由于两个模型的差别只有镜面反射部分,所以我们把其他的都不考虑,即令coefficient=1
,
并且去除环境光影响
FragColor=vec4(specular+ambientColor,1.0)*objColor;
并且把系数设小一点,放大高光区域,
在片元着色器中加上
uniform int Blinn;
......
if(Blinn==0){//Phong光照模型镜面反射vec3 reflectDir = reflect(-lightDir, normal);specularAmount = pow(max(dot(cameraVec, reflectDir), 0.0),10);specular = coefficient * specularAmount * lightColor;
}else {//Blinn-Phong光照模型镜面反射vec3 halfwarDir = normalize(lightDir + cameraVec);specularAmount = pow(max(dot(normal, halfwarDir), 0.0), 40);specular = coefficient * specularAmount * lightColor;
}
然后在main函数中
int Blinn = 0;
void processInput(GLFWwindow* window) {
......if (glfwGetKey(window, GLFW_KEY_B) == GLFW_PRESS)Blinn =abs(Blinn-1);
}
......
glUniform1i(glGetUniformLocation(myShader->ID, "Blinn"), Blinn);
我们看下效果
首先是Phong光照模型的高光
然后是Blinn-Phong光照模型的高光
使用Blinn-Phong模型的光看上去聚拢程度更高,即远方的镜面反射依然能看见,而对于Phong模型,远方的反射向量与观察向量夹角大于90度,于是远处的镜面反射就消失了。
所以相对而言,Blinn-Phong的效果更加真实,并且其计算也更方便。
3 完整代码
本次修改了两个shader和main函数,在此放出
main.cpp
#include <iostream>#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>#include "Shader.h"
#include "Camera.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
//顶点 uv 法向
float vertices[] = {-0.5f, -0.5f, -0.5f, 0.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, 1.0f, 0.0f, 0.0f, -1.0f,0.5f, 0.5f, -0.5f, 1.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, 0.0f, 0.0f, 0.0f, -1.0f,-0.5f, -0.5f, 0.5f, 0.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, 1.0f, 0.0f, 0.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.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, 0.0f, 0.0f, 0.0f, 1.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.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, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.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, 1.0f, 0.0f, 1.0f, 0.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, 1.0f, 1.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, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.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, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.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, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,-15.0f, -3.0f, -15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,15.0f, -3.0f, -15.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f,15.0f, -3.0f, 15.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,15.0f, -3.0f, 15.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-15.0f, -3.0f, 15.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,-15.0f, -3.0f, -15.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.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; // 上一帧的时间
int Blinn = 0;
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();if (glfwGetKey(window, GLFW_KEY_B) == GLFW_PRESS)Blinn =abs(Blinn-1);}
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 WindowglfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//Open GLFW WindowGLFWwindow* 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 GLEWglewExperimental = 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 ProgramShader* myShader = new Shader("vertexSource.vert", "fragmentSource.frag");#pragma endregion#pragma region Init and Load Models to VAO,VBOunsigned 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);// uv属性glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(2);// 法向量属性glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(5 * 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//modelglm::mat4 modelMat;//viewglm::mat4 viewMat;//projectionglm::mat4 projMat;#pragma endregionwhile (!glfwWindowShouldClose(window)) {//Process InputprocessInput(window);//Clear ScreenglClearColor(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 matrixmodelMat = 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 matrixviewMat = camera.GetViewMatrix();//Set projection matrixprojMat = glm::perspective(glm::radians(fov), 800.0f / 600.0f, 0.1f, 100.0f);//Set Material -> Shader ProgrammyShader->use();//Set Material -> TexturesglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, TexBufferA);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, TexBufferB);//Set Material -> UniformsglUniform1i(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, "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);glUniform1i(glGetUniformLocation(myShader->ID, "Blinn"), Blinn);//Set ModelglBindVertexArray(VAO);//DrawCallglDrawArrays(GL_TRIANGLES, 0, 36);if (i == 0) {glDrawArrays(GL_TRIANGLES, 36, 6);}}//Clean up prepare for next render loopglfwSwapBuffers(window);glfwPollEvents();//Recording the timefloat currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;}//Exit programglfwTerminate();return 0;}
顶点着色器
#version 330 core
layout(location = 0) in vec3 aPos; // 位置变量的属性位置值为 0
//layout(location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
layout(location = 2) in vec2 aTexCoord; // uv变量的属性位置值为 2
layout(location = 3) in vec3 aNormal; // 法向量的属性位置值为 2//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;
}
片段着色器
#version 330 core
//in vec4 vertexColor;
in vec2 TexCoord;
//着色点和法向
in vec3 FragPos;
in vec3 Normal;out vec4 FragColor; uniform sampler2D ourTexture;
uniform sampler2D ourFace;uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 ambientColor;
uniform vec3 cameraPos;uniform int Blinn;void main(){ //FragColor = mix(texture(ourTexture,TexCoord),texture(ourFace,TexCoord),texture(ourFace,TexCoord).a*0.2);vec3 r=lightPos-FragPos;//光线方向vec3 lightDir = normalize(r);//距离系数float coefficient=50/(pow(r.x,2)+pow(r.y,2)+pow(r.z,2));//coefficient=1;//法向vec3 normal = normalize(Normal);//漫反射float diff = max(dot(normal, lightDir), 0.0);vec3 diffuse = coefficient * diff * lightColor;//材质//vec4 objColor= mix(texture(ourTexture,TexCoord),texture(ourFace,TexCoord),texture(ourFace,TexCoord).a*0.2);vec4 objColor= vec4(1.0f, 0.5f, 0.31f,1.0f);//观察方向vec3 cameraVec= normalize(cameraPos - FragPos);vec3 specular;float specularAmount;if(Blinn==0){//Phong光照模型镜面反射vec3 reflectDir = reflect(-lightDir, normal);specularAmount = pow(max(dot(cameraVec, reflectDir), 0.0),100);specular = coefficient * specularAmount * lightColor;}else {//Blinn-Phong光照模型镜面反射vec3 halfwarDir = normalize(lightDir + cameraVec);specularAmount = pow(max(dot(normal, halfwarDir), 0.0), 200);specular = coefficient * specularAmount * lightColor;}FragColor=vec4(specular+diffuse+ambientColor,1.0)*objColor;}