OpenGL入门学习笔记(十三)----光照基础

实际上在现实生活中的光照是十分复杂的,我们难以在有限的处理能力范围去考虑到所有的因素,因此我们基于我们对于现实物理光的理解提出了几个简化的模型去尽量真实的模拟光照,其中一个模型就是冯氏光照模型(phong lighting model),这个模型主要分为三个部分:环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)、镜面光照(Specular lighting)

环境光照(ambient):

一般来说即使是在一个黑暗的环境,也会有一些其他较微弱的光,比如说月亮或者一些来自其他很远的地方的一些光线,这导致物体实际上并不会使完全的黑色,通常用一个常数来代表这个环境光而总是可以给物体先赋予一个光照。环境中的光通常不只是来自于一个光源,他们通常来自很多地方,然后在我们周围进行传播散射在想不通的反方向反射,而且光的落点很多时候并不是直接可见的,也有很多光线对于我们的的物体的光照的显示有很多间接的影响。有算法会将这些全部考虑在内,叫做全局照明算法(global illumination algorithms),但是这个过程通常是非常繁复的。

我们通常不会用这个繁复的算法,而是用它的一个简化模型,叫做环境光照(ambient lighting),就如前面提到的那样,我们总是采用一个代表环境光照的颜色常量,加在最后物体片段的颜色结果当中,就好像在环境中总有一些散射光即使在环境中没有光源。

设定一个系数作用于环境光。

void main()
{
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;//在这里设定了一个环境光照,无论何时都对物体最终结果产生影响。

    vec3 result = ambient * objectColor;
    FragColor = vec4(result, 1.0);
}  

漫反射光照(diffuse)

这个部分主要模拟的是光源对物体定向的光的影响,也是一个物体的光照最肉眼可见的部分,一个物体面对光源的部分越多,这个物体就越亮。不同于环境光照,漫反射光照开始对物体产生一个较为显著的影响。物体被光源给予的光亮越多,他最终的片段颜色就越接近光源光线的颜色。

具体一个光源的影响取决于光线在物体表面的夹角,其中当光线与物体表面垂直时,物体所受光线的影响最大。为此,我们提出法向量(一个垂直于物体表面的单位向量,例如图中的黄线)。

通过点乘可以轻松的得到两个向量之间的夹角。另外注意在得到夹角时需要时两个归一化向量。点乘的结果可以帮助我们去衡量光源对于物体的影响。

所以我们需要做的就是:找到一个垂直于物体表面的法向量,得到光线的方向向量,为了得到这个我们需要物体和光源位置向量。

首先要做的就是找到物体表面的法向量,我们是根据每个顶点找法向量,但是对于一个单独的点而言不存在这样的法向量。我们可以根据物体上的一个点以及他周围的点所成的表面获取法向量,但是对于我们的立方体而言,这个法向量是很容易认为计算出来的,所以我们可以人为的将法向量添加到顶点数据当中,并及时更新我们的顶点属性。

如果两个立方体用的相同的顶点数据,注意虽然光源不会用到方法向量,但是仍然需要对顶点属性进行更新以匹配新的顶点数据。当我们这样做时,我们实际上是重复使用一组数据,而不是分别对两个立方体使用两组数据,在第二次使用时,我们使用了已经存好的数据,这实际上让我们的程序更高效。

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;  
out vec3 Normal;
  
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = aNormal;
}

为了获得光线方向向量,首先从物体的着色器当中获得顶点的位置,由于我们的计算都是在世界坐标系中进行的,所以实际上我们只要获取到每个顶点的世界坐标就可以了,这个通过model矩阵可以很容易的实现。

另外就是光源的位置。

in vec3 FragPos;  
uniform vec3 lightPos;  

我们在片段着色器分别有这两个变量,同时在主程序根据实际情况设置光源位置,

接下来我们就可以计算散射光照了

vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos); 
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

确保是在单位向量范围内的计算,另外为了防止因为负数的一些不切实际的影响,我们用了一次max操作,让后将散射光照加到最终结果上

vec3 result = (ambient + diffuse) * objectColor;
FragColor = vec4(result, 1.0);

实际上,在先前我们都是直接将法向量进行了传递,实际上我们片段着色器的操作都是在世家坐标系中进行,所以实际上我们应该将这些向量转变的为世界坐标系当中,但是有时候这不只是简单的乘一个model矩阵就可以完成的,当我们的model矩阵当中存在位移或者不同一的缩放时,这可能会导致我们的方法向量扭曲而不垂直于原本的物体表面。

我们需要一个新的Normal矩阵专门服务于我们的法向量。

这个矩阵实际上就是我们的model左上3*3矩阵的

Normal = mat3(transpose(inverse(model))) * aNormal; 

逆的转置,具体的证明参考

镜面光照(specular):

和先前散射光照类似,这个光照的影响具体也取决于光线方向和物体表面的夹角,但是同时也取决于观察方向。镜面光也受到物体表面的影响,如果观察的物体表面是一个镜子,那么他将会由最强大的镜面反射光。

入射光线在法向量反转后的光线和我们视线的夹角,这个角越小,镜面反射光的影响就越明显,视觉效果就是我们看到的物体表面的一个高光亮点。

其中视线向量可以通过观察者世界坐标和物体坐标的得出,法向量周围的反射光就是反射向量。

和光源一样的,可以设置一个uniform变量来设置观察者位置。

float specularStrength = 0.5;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);

由于我们计算的光线向量实际上是相对于物体表面朝外的,但是这与reflect所期望的第一个变量方向相反,所以我们对它取-,另外我们的计算也都是在单位向量的范围内。接下来计算这个光照

float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor; 

首先确保这个视线和反射光的点乘不是负数,另外在进行了32的乘方,32定义为高光的亮度,这个亮度越大,物体的亮度值越高,它就越能正确地反射光线,而不是四处散射,因此高光就越小

最后

vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);

代码

在原有的相机基础上,展示所有代码

main:

#include<math.h>
#define STB_IMAGE_IMPLEMENTATION
#include<stb_image.h>
#include<glad/glad.h>

#include<GLFW/glfw3.h>
#include <iostream>
#include"Shader.h"
#include"CameraClass.h"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

void key_callback(GLFWwindow* window);
float lastFrame = 0.0f;
float deltaTime = 0.0f;

CameraClass MainCamera(glm::vec3(0.0f, 0.0f, 3.0f));
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xpos, double ypos);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);

bool firstMouse = true;

float lastX = 800.0f / 2.0;
float lastY = 600.0 / 2.0;
//创建着色器在连接时一定注意location位置,还有变量名称一定要完全对照工整!!!

glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(800, 600, "Color", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    glViewport(0, 0, 800, 800);

    glEnable(GL_DEPTH_TEST);
    Shader Object("vertex1.vert", "fragement1.frag");
    Shader Light("Light.vert", "Light.frag");

    float vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };
    GLuint VBO, VAO;
    glGenBuffers(1, &VBO);
    glGenVertexArrays(1, &VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(VAO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);
    glBindVertexArray(0);

    GLuint LightVAO;
    glGenVertexArrays(1, &LightVAO);
    glBindVertexArray(LightVAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);

    

    while (!glfwWindowShouldClose(window)) {
        // per-frame time logic
        // --------------------
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // input
        // -----
        key_callback(window);

        // render
        // ------
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // be sure to activate shader when setting uniforms/drawing objects
        Object.use();
        Object.setvec3("objectColor", glm::vec3(1.0f, 0.5f, 0.31f));
        Object.setvec3("lightColor", glm::vec3(1.0f, 1.0f, 1.0f));
        Object.setvec3("lightPos", lightPos);
        Object.setvec3("viewPos", MainCamera.Position);

        // view/projection transformations
        glm::mat4 projection = glm::perspective(glm::radians(MainCamera.Zoom), (float)800 / (float)600, 0.1f, 100.0f);
        glm::mat4 view = MainCamera.GetMatrix();
        Object.setmat4("projection", projection);
        Object.setmat4("view", view);

        // world transformation
        glm::mat4 model = glm::mat4(1.0f);
        Object.setmat4("model", model);
            
        // render the cube
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        // also draw the lamp object
        Light.use();
        Light.setmat4("projection", projection);
        Light.setmat4("view", view);
        model = glm::mat4(1.0f);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube
        Light.setmat4("model", model);

        glBindVertexArray(LightVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &VAO);
    glDeleteBuffers(1, &LightVAO);

    return 0;
}

void key_callback(GLFWwindow* window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
    /*float cameraSpeed = static_cast<float>(2.5 * deltaTime);*/
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
        /*cameraPos += cameraFront * cameraSpeed;*/
        MainCamera.Process_keyboard(FRONT, deltaTime);
    }
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        /*cameraPos -= cameraFront * cameraSpeed;*/
        MainCamera.Process_keyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        /*cameraPos += glm::cross(cameraUp,cameraFront) * cameraSpeed;*/
        MainCamera.Process_keyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        /*cameraPos -= glm::cross(cameraUp, cameraFront) * cameraSpeed;*/
        MainCamera.Process_keyboard(RIGHT, deltaTime);
}

void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{
    float xpos = static_cast<float>(xposIn);
    float ypos = static_cast<float>(yposIn);

    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
    lastX = xpos;
    lastY = ypos;

    MainCamera.Process_mouseMovement(xoffset, yoffset, true);
}

void scroll_callback(GLFWwindow* window, double xpos, double ypos) {
    MainCamera.Process_scroll(static_cast<float> (ypos));
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    glViewport(0, 0, width, height);

}

物体的着色器代码

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;
out vec3 Normal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = mat3(transpose(inverse(model))) * aNormal;  
    
    gl_Position = projection * view * vec4(FragPos, 1.0);
}

#version 330 core
out vec4 FragColor;

in vec3 Normal;  
in vec3 FragPos;  
  
uniform vec3 lightPos; 
uniform vec3 viewPos; 
uniform vec3 lightColor;
uniform vec3 objectColor;

void main()
{
    // ambient
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;
  	
    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;
    
    // specular
    float specularStrength = 0.5;
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular = specularStrength * spec * lightColor;  
        
    vec3 result = (ambient + diffuse + specular) * objectColor;
    FragColor = vec4(result, 1.0);
} 

光源着色器

#version 330 core
layout(location = 0) in vec3 aPos;

uniform mat4 view;
uniform mat4 model;
uniform mat4 projection;
void main(){
	gl_Position = projection * view * model * vec4(aPos,1.0f);
}


#version 330 core
out vec4 FragColor;
void main(){

	FragColor = vec4(1.0f);
}

相机

#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
enum CameraMovement {
	FRONT,
	BACKWARD,
	LEFT,
	RIGHT
};

const float YAW = -90.0f;//航向角初始设置为-90,摄像机默认实在x轴正向,为了相机朝向屏幕向内(-z方向),将物体逆时针90
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float ZOOM = 45.0f;
const float SENSITIVITY = 0.1f;
class CameraClass
{
public:
	glm::vec3 Position;
	glm::vec3 Front;
	glm::vec3 Up;
	glm::vec3 Right;
	glm::vec3 WorldUp;

	float Yaw;
	float Pitch;
	float Speed;
	float Sensitivity;
	float Zoom;

	CameraClass(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH);
	CameraClass(float PosX, float PosY, float PosZ, float upX, float upY, float upZ, float yaw, float pitch);

	glm::mat4 GetMatrix();

	void Process_keyboard(CameraMovement direction,float deltaTime);

	void Process_mouseMovement(float offsetX, float offsetY, GLboolean PitchConstrain);

	void Process_scroll(float offsetY);

private:
	void CameraUpdateVector();
};

#include "CameraClass.h"
CameraClass::CameraClass(glm::vec3 position, glm::vec3 up , float yaw , float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), Speed(SPEED), Sensitivity(SENSITIVITY), Zoom(ZOOM) {
	Position = position;
	WorldUp = up;
	Yaw = yaw;
	Pitch = pitch;

}

CameraClass::CameraClass(float PosX, float PosY, float PosZ, float upX, float upY, float upZ, float yaw, float pitch) :Front(glm::vec3(0.0f, 0.0f, -1.0f)), Speed(SPEED), Sensitivity(SENSITIVITY), Zoom(ZOOM) {
	Position = glm::vec3(PosX, PosY, PosZ);
	WorldUp = glm::vec3(upX, upY, upZ);
	Yaw = yaw;
	Pitch = pitch;

}

glm::mat4 CameraClass::GetMatrix() {
	return glm::lookAt(Position, Position + Front, WorldUp);
}

void CameraClass::Process_keyboard(CameraMovement direction, float deltaTime){
	float velocity = Speed * deltaTime;
	if (direction == FRONT)
		Position += Front * velocity;
	else if (direction == BACKWARD)
		Position -= Front * velocity;
	else if (direction == LEFT)
		Position -= Right * velocity;
	else if (direction == RIGHT)
		Position += Right * velocity;
	CameraUpdateVector();
}

void CameraClass::Process_mouseMovement(float offsetX, float offsetY, GLboolean PitchConstrain) {
	Yaw += offsetX * Sensitivity;
	Pitch += offsetY * Sensitivity;
	if (PitchConstrain) {
		if (Pitch > 89.0f)
			Pitch = 89.0f;
		if (Pitch < -89.0f)
			Pitch = -89.0f;
	}
	CameraUpdateVector();
}

void CameraClass::Process_scroll(float offsetY) {
	Zoom -= offsetY;
	if (Zoom < 1.0f)
		Zoom = 1.0f;
	else if (Zoom > 45.0f)
		Zoom = 45.0f;
}

void CameraClass::CameraUpdateVector() {
	glm::vec3 front;
	front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
	front.y = sin(glm::radians(Pitch));
	front.z = cos(glm::radians(Pitch)) * sin(glm::radians(Yaw));
	Front = glm::normalize(front);
	Right = glm::normalize(glm::cross(Front, WorldUp));
	Up = glm::normalize(glm::cross(Right, Front));
}

  • 15
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值