LearnOpenGL 基础光照

光照-冯氏光照模型

  • 环境 ambient
  • 漫反射 diffuse
  • 镜面反射 specular
冯氏光照模型
  • 环境光照(Ambient Lighting):即使在黑暗的情况下,世界上也仍然有一些光亮(月亮、一个来自远处的光),所以物体永远不会是完全黑暗的。我们使用环境光照来模拟这种情况,也就是无论如何永远都给物体一些颜色。
  • 漫反射光照(Diffuse Lighting):模拟一个发光物对物体的方向性影响(Directional Impact)。它是冯氏光照模型最显著的组成部分。面向光源的一面比其他面会更亮。
  • 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色,相比于物体的颜色更倾向于光的颜色

光照-颜色

  • 光源颜色 : 光源可以就是发光的物体, 发出的光. 本身自带颜色, 比如白光, 红光
  • 物体本身颜色 : 物体本身的颜色, 就是吸收其他光, 反射自身的颜色,

法向量

法向量是垂直于顶点表面的向量, 简单粗暴的理解就是三个顶点组成一个面, 一个垂直于这个面的向量就是法向量. 法向量注重看方向, 计算法向量的时候都会做归一化的处理. 比如 normalize(Normal)


PS : 简单理解上面几个概念. 然后开始接下来的着重冯氏模型理解




环境 ambient

环境光是一种低强度的光,由光线经过周围环境表面多次反射后形成的,利用环境光可以描述一块区域的亮度,通常在场 景中,环境光的颜色是一个常量.

环境颜色 = 光源的环境光颜色 × 物体的环境材质颜色

漫反射 diffuse

漫反射,是投射在粗糙表面上的光向各个方向反射的现象。当一束平行的入射光线射到粗糙的表面时,表面会把光线向着四面八方反射,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同的方向无规则地反射,这种反射称之为“漫反射”或“漫射”。

diffuse

L : 入射光 N : 法线

DiffuseFactor = max(0, dot(N, L))
漫反射颜色 = 光源的漫反射光颜色 × 物体的漫反射材质颜色 × 漫反射因子

镜面反射 specular

一束平行光射到平面镜上,反射光是平行的,这种反射叫做镜面反射。
镜面反射是指反射波(电磁波、或声波,水波)有确定方向的反射;其反射波的方向与反射平面的法线夹角(反射角),与入射波方向与该反射平面法线的夹角(入射角)相等,且入射波、反射波,及平面法线同处于一个平面内。摄影时应避免镜面反射光线进入摄影机镜头,由于镜面反射光线极强,在像片上将形成一片白色亮点,影响地物本身在像片上的显现。

specular

L : 入射光 N : 法线

R=reflect(L, N)
SpecularFactor = power(max(0, dot(R,V)), shininess)
镜面反射颜色 = 光源的镜面光颜色 × 物体的镜面材质颜色 × 镜面反射因子(SpecularFactor)

最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色







实践

利用前面的一些Demo教程. 修改对应的Shader和顶点数据, 顶点数据中加入了法线向量.

Shader

#define STRINGIZE(x) #x
#define SHADER(shader) STRINGIZE(shader)

/// 着色器程序之间的数据传递
static char *myLightVertexShaderStr = SHADER(
    \#version 330 core\n
                                             
    layout (location = 0) in vec3 position; //顶点数据源输入
    layout (location = 1) in vec3 normal; //法向量
                                              
    uniform mat4 myProjection;//投影矩阵
    uniform mat4 myView;//观察矩阵
    uniform mat4 myModel;//模型矩阵
                                             
    out vec3 Normal;//法线向量
    out vec3 FragPos;//片段位置

    void main()
    {
        gl_Position = myProjection * myView * myModel * vec4(position, 1.0f);
        Normal = normal;
        FragPos = vec3(myModel * vec4(position, 1.0f) );
    }
);

//片元着色器程序
static char *myLightFragmentShaderSrc = SHADER(
    \#version 330 core\n

    in vec3 Normal;
    in vec3 FragPos;

    uniform vec3 objectColor;//物体颜色
    uniform vec3 lightColor;//光照颜色
    uniform vec3 lightPos;//光源位置
                                               
    uniform vec3 viewPos;//镜面反射

    out vec4 color;
                                                               
    void main()
    {
        //环境光ambient
        float ambientStrength = 0.5f;//环境强度
        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 = 1.0f;
        vec3 viewDir = normalize(viewPos - FragPos);//归一化法向量
        vec3 reflectDir = reflect(-lightDir , norm);//返回入射光线i对表面法线n的反射光线。
        float spec = pow(max(dot(viewDir, reflectDir),0.0),8);//幂乘
        vec3 specular = specularStrength * spec * lightColor;//镜面反射

    
        vec3 result = (ambient + diffuse + specular) * objectColor;
        color = vec4(result , 1.0f);
    }
);

顶点数据

GLfloat myLightVertices[] = {
    //顶点数据              法线
   -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,
};

程序

#include "MyLight.hpp"
#include "MyProgram.hpp"
#include "MyLightShader.h"
#include "MyLightVertices.h"
#include "glm.hpp"
#include "matrix_transform.hpp"
#include "type_ptr.hpp"

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#include "stb_image.h"


int runMyLightCube() {
    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);
    gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);

    //切换为纹理着色器程序
    MyProgram myProgram = MyProgram(myLightVertexShaderStr, myLightFragmentShaderSrc);

    ///
    GLuint VBO , VAO ;
    unsigned int squareIndicesCount = 0;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(myLightVertices), myLightVertices, GL_STATIC_DRAW);
    
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(0 * sizeof(GLfloat)));
    glEnableVertexAttribArray(0);

    //法线
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    //解绑VAO
    glBindVertexArray(0);
    squareIndicesCount = sizeof(myLightVertices)/(sizeof(myLightVertices[0]) * 5);

    glEnable(GL_DEPTH_TEST);


    
    //进行绘制
    while(!glfwWindowShouldClose(window)){
        glfwPollEvents();
        
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUseProgram(myProgram.program);
        
        
        //================================================
        glm::mat4 model = glm::mat4(1.0f);
        glm::mat4 view = glm::mat4(1.0f);
        glm::mat4 projection = glm::mat4(1.0f);
        
        GLint myModelLoc = glGetUniformLocation(myProgram.program,"myModel");
        GLint myViewLoc = glGetUniformLocation(myProgram.program,"myView");
        GLint myProjectionLoc = glGetUniformLocation(myProgram.program,"myProjection");
        
        projection = glm::perspective(glm::radians(60.0f), 1.0f, 0.01f, 100.f);//投影矩阵
        glUniformMatrix4fv(myProjectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
        
//        model = glm::rotate(model,(GLfloat)glfwGetTime() * 1.0f, glm::vec3(1.0f,0.0f,0.0f));//以x,y轴旋转
        glUniformMatrix4fv(myModelLoc, 1, GL_FALSE, glm::value_ptr(model));

        // Light
        glm::vec3 LightEye   = glm::vec3(2.0f, 2.0f,  2.0f);
        glm::vec3 LightCenter = glm::vec3(0.0f, 0.0f, 0.0f);
        glm::vec3 LightUp    = glm::vec3(0.0f, 1.0f,  0.0f);
        
        glBindVertexArray(VAO);
        view = glm::lookAt(LightEye,       //摄像机位置
                           LightCenter,    //目标
                           LightUp);       //上向量
        
        //================================================

        
        
        
        
        //================================================
        //光源位置
        GLint myLightPosLoc = glGetUniformLocation(myProgram.program,"lightPos");
        glUniform3f(myLightPosLoc,1.0,1.3,3.0f); //
        
        //镜面反射
        GLint myViewPosLoc = glGetUniformLocation(myProgram.program,"viewPos");
        glUniform3f(myViewPosLoc,0.0,0.0f,3.0f); //
        
        //物体颜色, 光照颜色
        GLint objectColorLoc = glGetUniformLocation(myProgram.program,"objectColor");
        GLint lightColorLoc = glGetUniformLocation(myProgram.program,"lightColor");

        glUniform3f(objectColorLoc,1.0f,0.5f,0.35f);
        glUniform3f(lightColorLoc,1.0f,1.0f,1.0f); //白光
        //================================================

        
                    
        glUniformMatrix4fv(myViewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glDrawArrays(GL_TRIANGLES, 0, squareIndicesCount);
        glBindVertexArray(0);

        glfwSwapBuffers(window);
    }

    //程序销毁
    glfwTerminate();
    
    return 1;
}

效果

环境光+漫反射+镜面反射



Shader中的镜面反射

我们通过反射法向量周围光的方向计算反射向量。然后我们计算反射向量和视线方向的角度,如果之间的角度越小,那么镜面光的作用就会越大。它的作用效果就是,当我们去看光被物体所反射的那个方向的时候,我们会看到一个高光。

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

我们先计算视线方向与反射方向的点乘(确保它不是负值),然后得到它的32次幂。这个32是高光的发光值(Shininess)。一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。在下面的图片里,你会看到不同发光值对视觉(效果)的影响:

镜面反射

比如修改Shader中的

float spec = pow(max(dot(viewDir, reflectDir),0.0),2);//2次幂
2次幂
float spec = pow(max(dot(viewDir, reflectDir),0.0),64);//64次幂
64次幂

这里的作用就比较好理解了.



最后总结 :

  • 1.先理解冯氏光照模型的概念, 法线量.
  • 2.主要是Shader里面的程序修改. 怎么设置.
  • 3.Shader中光照的公式运用. 暂时先理解为固定公式吧.

片元着色器

 void main()
    {
        //环境光ambient
        float ambientStrength = 0.5f;//环境强度
        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 = 1.0f;
        vec3 viewDir = normalize(viewPos - FragPos);//归一化法向量
        vec3 reflectDir = reflect(-lightDir , norm);//返回入射光线i对表面法线n的反射光线。
        float spec = pow(max(dot(viewDir, reflectDir),0.0),8);//幂乘
        vec3 specular = specularStrength * spec * lightColor;//镜面反射

    
        vec3 result = (ambient + diffuse + specular) * objectColor;
        color = vec4(result , 1.0f);
    }


参照 :
冯氏光照模型
Unity Shader 内置函数

你可能感兴趣的:(LearnOpenGL 基础光照)