OpenGL基础44:光照矫正(上)

对于openGL的API,倒是没有必要花太多时间,重点应该还是在着色器上

一、采样器、glActiveTexture和glBindTexture

在之前测试简单光照时可能出现的两个问题,尽管它们可能不会被察觉:

  1. 着色器中有定义对应的采样器,可是通过glGetUniformLocation方法获取不到对应位置值
  2. 着色器中不止1个采样器,在没有考虑其中某个特定采样器时,它居然也持有纹理

对于①,排除了低级错误后,检查下着色器中对应的采样器是否有参与实质运算,并且这个运算还必须是有效的,如果不满足这个条件,这个采样器就会被“优化”掉,相当于没有定义,当然也获取不到对应位置,例如下面这个着色器,texture_diffuse1就无法被获取:

#version 330 core
struct Material
{
    sampler2D texture_diffuse1;      //贴图
    sampler2D texture_specular1;     //镜面贴图
};
//……
void main()
{
    vec3 result = 0.05f * vec3(texture(material.texture_specular1, texIn));
    //……
    color = vec4(result, 1.0f);
}

对于②,需要再深入了解下采样器、glActiveTexture和glBindTexture的关系:

  • 若在着色器中定义了若干个采样器,那么这些采样器的位置值默认都为0
  • 如果没有执行过glActiveTexture以激活当前的采样器位置值,那么默认激活0
  • 主要通过glBindTexture来绑定贴图,每次glBindTexture,都会将对应贴图绑定在当前glActiveTexture激活的位置值上

别忘了openGL是个状态机

所以,下面的这份代码,在某种情况下就是有问题的了

由于下标i从0开始,因此在循环的第一次,第一张贴图就会绑定到所有位置值为0的采样器上,这样如果存在没有处理的采样器,那么这个采样器就会被绑定第一张贴图(因为它的位置值为0),然而事实上它不应该被绑定任何贴图

for (GLuint i = 0; i < this->textures.size(); i++)
{
    stringstream ss;
    string name = this->textures[i].type;
    if (name == "texture_diffuse")
        ss << diffuseNr++;
    else if (name == "texture_specular")
        ss << specularNr++;
    else if (name == "texture_reflection")
        ss << reflectionNr++;
    name = "material." + name + ss.str();
    glActiveTexture(GL_TEXTURE0 + i + 1);
    glUniform1i(glGetUniformLocation(shader.Program, name.c_str()), i + 1);
    //这样的话,着色器中的纹理名就必须有一个对应的规范,例如“texture_diffuse3”代表第三个漫反射贴图
    //方法不唯一,这是最好理解/最简单的一种规范/写法
    glBindTexture(GL_TEXTURE_2D, this->textures[i].id);
}

解决方法也很简单:

  • 对于没有读取到的贴图(例如一些物体它就是没有镜面贴图,这很正常),给予一张全黑贴图
  • 下标从1开始,无视0位置值

二、Blinn-Phong光照

前置:OpenGL基础20:镜面光照

之前在计算镜面光照时,都是参照的Phong光照算法,现在尝试换一种算法,为了方便测试,将之前的漫发射贴图改为镜面贴图,并只保留镜面贴图:

只看地面,可以发现存在一定的“镜面区域”,在这个区域中光照还是非常强的,但是只要离开这个范围,光照就会迅速减弱并截止,直至变得完全黑暗,这并不是特别科学,哪怕是只有镜面光也不应该出现这么黑暗的截面

出现这个现象还是在于算法,观察向量和反射向量间的夹角不能大于90度。如果点积的结果为负数,镜面光分量就会变为0.0,如下图所示,存在一定的区域光照完全不可见

对于漫发射光照,当然不会出现这个问题,因为若是角度>90°,那么观察者应该是在地面以下

Blinn-Phong着色是Phong着色模型的拓展,与冯氏模型非常相似,只是对镜面光模型的处理上有一些不同,它不再依赖于反射向量,而是采用了所谓的半程向量(Halfway Vector),即光线与视线夹角一半方向上的一个单位向量,当半程向量与法线向量越接近,镜面光分量就越大

这样就保证了不论观察者向哪个方向看,半程向量与表面法线之间的夹角都不会超过90度,若是角度>90°,那么观察者应该是在地面以下,和漫反射的计算一个道理

公式:$$ \bar{H} = \frac{\bar{L} + \bar{V}}{||\bar{L} + \bar{V}||} $$

由于算法不同(半程向量和表面法线之间的角度经常会比视线和反射向量之间的夹角更小),在相同的发光参数下,Blinn-Phong着色的平均光照强度要小一点,镜面反射的高光区域倒是会更加锐利,并且当光照不够的情况下,仍然可能出现全黑区域,这只是因为此区域光照强度实在太小

着色器和效果如下:

//blinn-Phong
vec3 halfwayDir = normalize(lightDir + viewDir);  
spec = pow(max(dot(normal, halfwayDir), 0.0), 16.0);
//Phong
vec3 reflectDir = reflect(-lightDir, normal);
spec = pow(max(dot(viewDir, reflectDir), 0.0), 8.0);

上图左半部分为Phong光照,右半部分为Blinn-Phong光照

三、选择正确的光照参数

有的时候,觉得光照比较奇怪也有可能是光照参数设置的不对,例如明明是渲染夜晚的场景,却设置了较高的平行光强度,能发出平行光的基本只有太阳,所以关于平行光的设置一定要符合实际,前面貌似就犯了这个错误,用了夜晚星空的天空盒,却给予了很高的平行光强。除此之外,假设有2个光源,一个是蜡烛,一个是路灯,那么显然后者的亮度更高,前者的衰减更明显……

当然了,情况远远比这个复杂,包括光的颜色不一定都是白色、光源是火焰、有些光源它不属于点光源、平行光、聚光灯的任意一种等等

太多的glGetUniformLocation会感觉过于复杂,所以为了方便暂时把光照单独抽了出来:

修改了各部分光照的参数,更有夜晚的感觉了:

Light.h:

#ifndef LIGHT_H
#define LIGHT_H
#include<opengl/glew.h>
#include<GLFW/glfw3.h>
#include<opengl/freeglut.h>
#include<glm/glm.hpp>
#include<iostream>
#include<vector>
#include<string>
#include<glm/gtc/matrix_transform.hpp>
using namespace std;
enum Light_Type
{
    SUNLIGHT,
    POINTLIGHT,
    SPOTLIGHT
};
class Light
{
    private:
        int MAX_LIGHT_NUM = 16;
        typedef struct
        {
            glm::vec3 direction;
            glm::vec3 diffuse;
            glm::vec3 specular;
        }SunLight;
        typedef struct
        {
            float k0, k1, k2;
            glm::vec3 position;
            glm::vec3 diffuse;
            glm::vec3 specular;
        }PointLight;
        typedef struct
        {
            float k0, k1, k2;
            float cutOff, outCutOff;
            glm::vec3 direction;
            glm::vec3 position;
            glm::vec3 diffuse;
            glm::vec3 specular;
        }SpotLight;
    public:
        SunLight sunLight;
        vector<PointLight> pointLight;
        vector<SpotLight> spotLight;
        Light()
        {
            Init();
        }
        void Init()
        {
            sunLight.diffuse = glm::vec3(0.0);
            sunLight.specular = glm::vec3(0.0);
            sunLight.direction = glm::vec3(0.0);
            pointLight.clear();
            spotLight.clear();
        }
        int AddSunLight(glm::vec3 direction, glm::vec3 diffuse, glm::vec3 specular)
        {
            sunLight.direction = direction;
            sunLight.diffuse = diffuse;
            sunLight.specular = specular;
            return 1;
        }
        int AddPointLight(glm::vec3 position, glm::vec3 diffuse, glm::vec3 specular, float k0 = 1.0f, float k1 = 0.09f, float k2 = 0.032f)
        {
            PointLight temp;
            temp.position = position;
            temp.diffuse = diffuse;
            temp.specular = specular;
            temp.k0 = k0, temp.k1 = k1, temp.k2 = k2;
            pointLight.push_back(temp);
            return pointLight.size();
        }
        int AddSpotLight(glm::vec3 position, glm::vec3 direction, glm::vec3 diffuse, glm::vec3 specular, 
            float k0 = 1.0f, float k1 = 0.007f, float k2 = 0.0002f, float cutOff = glm::cos(glm::radians(12.5f)), float outCutOff = glm::cos(glm::radians(16.0f)))
        {
            SpotLight temp;
            temp.position = position;
            temp.direction = direction;
            temp.diffuse = diffuse;
            temp.specular = specular;
            temp.k0 = k0, temp.k1 = k1, temp.k2 = k2;
            temp.cutOff = cutOff, temp.outCutOff = outCutOff;
            spotLight.push_back(temp);
            return spotLight.size();
        }
        bool AppAllLightToShader(GLuint program)
        {
            AppLightToShader(program, SUNLIGHT, 0, true);
            for (int i = 0; i < MAX_LIGHT_NUM; i++)
            {
                AppLightToShader(program, POINTLIGHT, i, true);
                AppLightToShader(program, SPOTLIGHT, i, true);
            }
            if (sunLight.direction != glm::vec3(0.0))
                AppLightToShader(program, SUNLIGHT);
            for (int i = 0; i < pointLight.size(); i++)
                AppLightToShader(program, POINTLIGHT, i);
            for (int i = 0; i < spotLight.size(); i++)
                AppLightToShader(program, SPOTLIGHT, i);
            return true;
        }
        bool AppLightToShader(GLuint program, int type, int index = 0, bool isDel = false)
        {
            if (type == (int)SUNLIGHT)
            {
                if (sunLight.direction == glm::vec3(0.0) && !isDel)
                {
                    cout << "错误,无法找到对应的平行光数据" << endl;
                    return false;
                }
                SunLight sun = sunLight;
                if (isDel)
                {
                    glUniform3f(glGetUniformLocation(program, "sunLight.diffuse"), 0.0, 0.0, 0.0);
                    glUniform3f(glGetUniformLocation(program, "sunLight.specular"), 0.0, 0.0, 0.0);
                }
                else
                {
                    glUniform3f(glGetUniformLocation(program, "sunLight.direction"), sun.direction.x, sun.direction.y, sun.direction.z);
                    glUniform3f(glGetUniformLocation(program, "sunLight.diffuse"), sun.diffuse.x, sun.diffuse.y, sun.diffuse.z);
                    glUniform3f(glGetUniformLocation(program, "sunLight.specular"), sun.specular.x, sun.specular.y, sun.specular.z);
                }
            }
            else if (type == (int)POINTLIGHT)
            {
                if (pointLight.size() < index + 1 && !isDel)
                {
                    cout << "错误,无法找到对应的点光源数据" << endl;
                    return false;
                }
                string uniform = "pointLights[" + to_string(index) + "]";
                if (isDel)
                {
                    glUniform3f(glGetUniformLocation(program, (uniform + ".diffuse").c_str()), 0.0, 0.0, 0.0);
                    glUniform3f(glGetUniformLocation(program, (uniform + ".specular").c_str()), 0.0, 0.0, 0.0);
                }
                else
                {
                    PointLight point = pointLight[index];
                    glUniform3f(glGetUniformLocation(program, (uniform + ".position").c_str()), point.position.x, point.position.y, point.position.z);
                    glUniform3f(glGetUniformLocation(program, (uniform + ".diffuse").c_str()), point.diffuse.x, point.diffuse.y, point.diffuse.z);
                    glUniform3f(glGetUniformLocation(program, (uniform + ".specular").c_str()), point.specular.x, point.specular.y, point.specular.z);
                    glUniform1f(glGetUniformLocation(program, (uniform + ".k0").c_str()), point.k0);
                    glUniform1f(glGetUniformLocation(program, (uniform + ".k1").c_str()), point.k1);
                    glUniform1f(glGetUniformLocation(program, (uniform + ".k2").c_str()), point.k2);
                }
            }
            else if (type == (int)SPOTLIGHT)
            {
                if (spotLight.size() < index + 1 && !isDel)
                {
                    cout << "错误,无法找到对应的聚光数据" << endl;
                    return false;
                }
                string uniform = "spotLights[" + to_string(index) + "]";
                if (isDel)
                {
                    glUniform3f(glGetUniformLocation(program, (uniform + ".diffuse").c_str()), 0.0, 0.0, 0.0);
                    glUniform3f(glGetUniformLocation(program, (uniform + ".specular").c_str()), 0.0, 0.0, 0.0);
                }
                else
                {
                    SpotLight spot = spotLight[index];
                    glUniform3f(glGetUniformLocation(program, (uniform + ".position").c_str()), spot.position.x, spot.position.y, spot.position.z);
                    glUniform3f(glGetUniformLocation(program, (uniform + ".direction").c_str()), spot.direction.x, spot.direction.y, spot.direction.z);
                    glUniform3f(glGetUniformLocation(program, (uniform + ".diffuse").c_str()), spot.diffuse.x, spot.diffuse.y, spot.diffuse.z);
                    glUniform3f(glGetUniformLocation(program, (uniform + ".specular").c_str()), spot.specular.x, spot.specular.y, spot.specular.z);
                    glUniform1f(glGetUniformLocation(program, (uniform + ".k0").c_str()), spot.k0);
                    glUniform1f(glGetUniformLocation(program, (uniform + ".k1").c_str()), spot.k1);
                    glUniform1f(glGetUniformLocation(program, (uniform + ".k2").c_str()), spot.k2);
                    glUniform1f(glGetUniformLocation(program, (uniform + ".cutOff").c_str()), spot.cutOff);
                    glUniform1f(glGetUniformLocation(program, (uniform + ".outCutOff").c_str()), spot.outCutOff);
                }
            }
            return true;
        }
};
#endif

main.cpp:

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include"Shader.h"
#include"Camera.h"
#include"Light.h"
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
#include"Mesh.h"
#include"Model.h"
#include<opengl/freeglut.h>
#include<SOIL.h>

bool keys[1024];
Camera camera;
GLfloat lastX, lastY;
bool firstMouse = true;
bool openSpotLight = true;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
GLuint loadCubemap(vector<const GLchar*> faces);
GLuint getAttachmentTexture();
GLuint getMultiSampleTexture(GLuint samples);           //MSAA几重采样
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
GLuint loadTexture(const GLchar* path, GLboolean alpha);
void cameraMove();
const GLuint WIDTH = 800, HEIGHT = 600;

int main()
{
    //------------------------------------------------------初始化---------------------------------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    glewExperimental = GL_TRUE;
    glewInit();

    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);

    Shader shaderObj("ObjVShader.vert", "ObjFShader.frag", "ObjGShader.geom");
    Shader shaderObjWithoutTex("ObjVShaderWithoutTex.vert", "ObjFShaderWithoutTex.frag");
    Shader shaderLight("LightVShader.vert", "LightFShader.frag");
    Shader shaderStone("StoneVShader.vert", "StoneFShader.frag");
    Shader shaderSkyBox("SkyboxVShader.vert", "SkyboxFShader.frag");
    Shader shaderScreen("ScreenVShader.vert", "ScreenFShader.frag");
    Shader shaderMirror("MirrorVShader.vert", "MirrorFShader.frag");

    //------------------------------------------------------数据定义---------------------------------------------------------
    GLfloat vertices[] = 
    {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

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

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

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

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

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    GLfloat stoneVertices[] = 
    {
        0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
        0.0f, -0.5f,  0.0f,  0.0f,  1.0f,
        1.0f, -0.5f,  0.0f,  1.0f,  1.0f,
        0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
        1.0f, -0.5f,  0.0f,  1.0f,  1.0f,
        1.0f,  0.5f,  0.0f,  1.0f,  0.0f
    };
    glm::vec3 positions[] = 
    {
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 0.89f, 0.0f),
        glm::vec3(0.0f, 1.78f, 0.0f),
        glm::vec3(-2.0f, 0.0f, 0.0f),
        glm::vec3(-2.0f, 0.89f, 0.0f),
        glm::vec3(-3.0f, 0.0f, 0.0f),
        glm::vec3(-2.0f, 0.0f, 1.0f),
        glm::vec3(-1.0f, 0.0f, -4.0f),
    };
    glm::vec3 pointLightPositions[] = 
    {
       glm::vec3(-1.0f, 1.8f, -2.0f),
       glm::vec3(0.0f, 0.8f, 2.0f),
       glm::vec3(-5.0f, 0.8f, 1.0f),
    };
    GLfloat quadVertices[] = 
    {
        -1.0f, 1.0f, 0.0f, 1.0f,
        -1.0f, -1.0f, 0.0f, 0.0f,
         1.0f, -1.0f, 1.0f, 0.0f,
        -1.0f, 1.0f, 0.0f, 1.0f,
         1.0f, -1.0f, 1.0f, 0.0f,
         1.0f, 1.0f, 1.0f, 1.0f
    };
    GLfloat skyboxVertices[] = 
    {
        -1.0f,  1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

        -1.0f,  1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f,  1.0f
    };
    vector<glm::vec3> vegetation;
    vegetation.push_back(glm::vec3(-1.5f, -0.01f, -0.48f));
    vegetation.push_back(glm::vec3(1.5f, -0.01f, 0.51f));
    vegetation.push_back(glm::vec3(0.0f, -0.01f, 0.7f));
    vegetation.push_back(glm::vec3(-0.3f, -0.01f, -2.3f));
    vegetation.push_back(glm::vec3(0.5f, -0.01f, -0.6f));

    //------------------------------------------------------VAO & VBO---------------------------------------------------------
    GLuint lightVAO, lightVBO;
    glGenVertexArrays(1, &lightVAO);
    glGenBuffers(1, &lightVBO);
    glBindVertexArray(lightVAO);
    glBindBuffer(GL_ARRAY_BUFFER, lightVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    GLuint quadVAO, quadVBO;
    glGenVertexArrays(1, &quadVAO);
    glGenBuffers(1, &quadVBO);
    glBindVertexArray(quadVAO);
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    GLuint skyboxVAO, skyboxVBO;
    glGenVertexArrays(1, &skyboxVAO);
    glGenBuffers(1, &skyboxVBO);
    glBindVertexArray(skyboxVAO);
    glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    GLuint stoneVAO, stoneVBO;
    glGenVertexArrays(1, &stoneVAO);
    glGenBuffers(1, &stoneVBO);
    glBindVertexArray(stoneVAO);
    glBindBuffer(GL_ARRAY_BUFFER, stoneVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(stoneVertices), stoneVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    GLuint stone = loadTexture("Texture/stone3.png", true);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    //------------------------------------------------------UBO---------------------------------------------------------
    GLuint UBO;
    /*--已在着色器中设置
    GLuint uniformCameraM = glGetUniformBlockIndex(shaderMirror.Program, "Matrices");
    GLuint uniformCameraM2 = glGetUniformBlockIndex(shaderObj.Program, "Matrices");
    GLuint uniformCameraM4 = glGetUniformBlockIndex(shaderStone.Program, "Matrices");
    glUniformBlockBinding(shaderMirror.Program, uniformCameraM, 0);
    glUniformBlockBinding(shaderMirror.Program, uniformCameraM2, 0);
    glUniformBlockBinding(shaderMirror.Program, uniformCameraM4, 0);
    */
    glGenBuffers(1, &UBO);
    glBindBuffer(GL_UNIFORM_BUFFER, UBO);
    glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
    glBindBufferRange(GL_UNIFORM_BUFFER, 0, UBO, 0, 2 * sizeof(glm::mat4));

    //------------------------------------------------------帧缓冲---------------------------------------------------------
    GLuint FBO, RBO;
    glGenFramebuffers(1, &FBO);
    glBindFramebuffer(GL_FRAMEBUFFER, FBO);

    GLuint textureColorBufferMultiSampled = getMultiSampleTexture(4);           //MSAA 4x抗锯齿
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);

    glGenRenderbuffers(1, &RBO);
    glBindRenderbuffer(GL_RENDERBUFFER, RBO);
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, WIDTH, HEIGHT);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO);
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    GLuint screenFBO;
    glGenFramebuffers(1, &screenFBO);
    glBindFramebuffer(GL_FRAMEBUFFER, screenFBO);
    GLuint textureColorBuffer = getAttachmentTexture();
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorBuffer, 0);
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        cout << "ERROR::FRAMEBUFFER:: Intermediate framebuffer is not complete!" << endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    //------------------------------------------------------设置状态、加载模型和天空盒---------------------------------------------------------
    glEnable(GL_CULL_FACE);
    glEnable(GL_MULTISAMPLE);
    glEnable(GL_STENCIL_TEST);
    glEnable(GL_PROGRAM_POINT_SIZE);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);             //模板测试和深度测试都成功时,将对应像素的模板值设置为用glStencilFunc函数设置的ref值
    glEnable(GL_BLEND);
    glEnable(GL_PROGRAM_POINT_SIZE);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    Model wood("Object/wood/file.fbx", "Object/wood/file.fbx");
    Model ground("Object/ground/ground.fbx", "Object/ground");
    Model mirror("Object/mirror/file.fbx", "Object/mirror");

    vector<const GLchar*> faces;
    faces.push_back("Texture/Skybox/StarryNight1024/Right.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Left.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Up.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Down.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Back.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Front.jpg");
    GLuint cubemapTexture = loadCubemap(faces);

    //------------------------------------------------------实例化---------------------------------------------------------
    glm::mat4* modelMatrices;
    modelMatrices = new glm::mat4[1000];
    glm::mat4 model = glm::mat4(1.0f);
    for (GLuint i = 0; i <= 7; i++)
    {
        model = glm::translate(glm::mat4(1.0f), positions[i]);
        model = glm::scale(model, glm::vec3(0.01f));
        modelMatrices[i] = model;
    }
    wood.UpdateModelMatrix(modelMatrices, 8);

    GLint groundIndex = 0;
    for (int i = -4; i <= 4; i++)
    {
        for (int j = -2; j <= 4; j++)
        {
            model = glm::translate(glm::mat4(1.0f), glm::vec3(i * 3.52f, -0.05f, j * 3.72f));
            model = glm::scale(model, glm::vec3(0.1f));
            model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
            modelMatrices[groundIndex++] = model;
        }
    }
    ground.UpdateModelMatrix(modelMatrices, groundIndex);
    delete[] modelMatrices;

    Light light;
    light.AddSunLight(glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3(0.03f, 0.03f, 0.03f), glm::vec3(0.036f, 0.036f, 0.036f));
    for (int i = 0; i <= 2; i++)
        light.AddPointLight(glm::vec3(pointLightPositions[i].x, pointLightPositions[i].y, pointLightPositions[i].z), glm::vec3(0.8f, 0.8f, 0.8f), glm::vec3(1.0f, 1.0f, 1.0f));
    light.AddSpotLight(glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z), glm::vec3(camera.Front.x, camera.Front.y, camera.Front.z), 
        glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f));

    //------------------------------------------------------渲染ing---------------------------------------------------------
    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();
        glEnable(GL_DEPTH_TEST);
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glStencilMask(0xFF);                  //设置模板缓冲区可写入,如果设置为不可写入之后清空模板缓冲区,将会清空失败!毕竟不可写入了
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        cameraMove();

        shaderLight.Use();
        glm::mat4 view = camera.GetViewMatrix();
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        glm::mat4 model = glm::mat4(1.0f);
        GLint modelLoc = glGetUniformLocation(shaderLight.Program, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glBindBuffer(GL_UNIFORM_BUFFER, UBO);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(view));
        glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(projection));
        glBindBuffer(GL_UNIFORM_BUFFER, 0);

        glBindVertexArray(lightVAO);
        glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);                   //无论模板测试如何,一定可以绘制;
        glUniform3f(glGetUniformLocation(shaderLight.Program, "lightColor"), 1.0f, 1.0f, 1.0f);
        for (int i = 0; i <= 2; i++)
        {
            model = glm::translate(glm::mat4(1.0f), pointLightPositions[i]);
            model = glm::scale(model, glm::vec3(0.2f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glStencilFunc(GL_NOTEQUAL, 0xFF, 0xFF);                 //对应像素模板值若等于256,则对应像素不绘制
        glStencilMask(0x00);                                    //模板缓冲区不再可写
        glUniform3f(glGetUniformLocation(shaderLight.Program, "lightColor"), 1.0f, 1.0f, 0.0f);
        for (int i = 0; i <= 2; i++)
        {
            model = glm::translate(glm::mat4(1.0f), pointLightPositions[i]);
            model = glm::scale(model, glm::vec3(0.22f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        
        glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);
        shaderObj.Use();
        light.spotLight.clear();
        if (openSpotLight)
        {
            light.AddSpotLight(glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z), glm::vec3(camera.Front.x, camera.Front.y, camera.Front.z),
                glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f));
        }
        light.AppAllLightToShader(shaderObj.Program);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "viewPos"), camera.Position.x, camera.Position.y, camera.Position.z);
        wood.Draw(shaderObj, 8);
        ground.Draw(shaderObj, groundIndex + 1);

        shaderMirror.Use();
        model = glm::translate(glm::mat4(1.0f), glm::vec3(-0.51f, -0.3f, -3.71f));
        model = glm::scale(model, glm::vec3(0.0035f));
        model = glm::rotate(model, glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::rotate(model, glm::radians(3.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
        glUniformMatrix4fv(glGetUniformLocation(shaderMirror.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
        mirror.Draw(shaderObj);

        shaderStone.Use();
        glBindVertexArray(stoneVAO);
        glBindTexture(GL_TEXTURE_2D, stone);
        for (GLuint i = 0; i < vegetation.size(); i++)
        {
            model = glm::mat4(1.0f);
            model = glm::translate(model, vegetation[i]);
            model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
            glUniformMatrix4fv(glGetUniformLocation(shaderStone.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
            glDrawArrays(GL_TRIANGLES, 0, 6);
        }
        shaderSkyBox.Use();
        glDepthFunc(GL_LEQUAL);
        view = glm::mat4(glm::mat3(camera.GetViewMatrix()));
        glUniformMatrix4fv(glGetUniformLocation(shaderSkyBox.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(glGetUniformLocation(shaderSkyBox.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
        glBindVertexArray(skyboxVAO);
        glActiveTexture(GL_TEXTURE0);
        glUniform1i(glGetUniformLocation(shaderSkyBox.Program, "skybox"), 0);
        glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glDepthFunc(GL_LESS);

        glBindFramebuffer(GL_READ_FRAMEBUFFER, FBO);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, screenFBO);
        glBlitFramebuffer(0, 0, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        shaderScreen.Use();
        glBindVertexArray(quadVAO);
        glBindTexture(GL_TEXTURE_2D, textureColorBuffer);
        glDrawArrays(GL_TRIANGLES, 0, 6);

        glBindTexture(GL_TEXTURE_2D, 0);
        glBindVertexArray(0);
        glfwSwapBuffers(window);
    }

    //------------------------------------------------------解绑---------------------------------------------------------
    glDeleteFramebuffers(1, &FBO);
    glDeleteFramebuffers(1, &screenFBO);
    glDeleteVertexArrays(1, &lightVAO);
    glDeleteVertexArrays(1, &stoneVAO);
    glDeleteBuffers(1, &stoneVBO);
    glDeleteBuffers(1, &lightVBO);
    glfwTerminate();
    return 0;
}

GLuint getMultiSampleTexture(GLuint samples)
{
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, WIDTH, HEIGHT, GL_TRUE);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
    return texture;
}

GLuint loadCubemap(vector<const GLchar*> faces)
{
    GLuint textureID;
    glGenTextures(1, &textureID);

    int width, height;
    unsigned char* image;
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
    for (GLuint i = 0; i < faces.size(); i++)
    {
        image = SOIL_load_image(faces[i], &width, &height, 0, SOIL_LOAD_RGB);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
        SOIL_free_image_data(image);
    }
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    return textureID;
}

GLuint loadTexture(const GLchar* path, GLboolean alpha)
{
    GLuint textureID;
    glGenTextures(1, &textureID);
    int width, height;
    unsigned char* image;
    if (alpha)
        image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGBA);
    else
        image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGB);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, alpha ? GL_RGBA : GL_RGB, width, height, 0, alpha ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, alpha ? GL_CLAMP_TO_EDGE : GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, alpha ? GL_CLAMP_TO_EDGE : GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);
    SOIL_free_image_data(image);
    return textureID;
}

GLuint getAttachmentTexture()
{
    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);
    return textureID;
}

GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
void cameraMove()
{
    GLfloat currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;

    GLfloat cameraSpeed = 1.0f * deltaTime;
    if (keys[GLFW_KEY_W])
        camera.ProcessKeyboard(Camera_Movement(FORWARD), deltaTime);
    if (keys[GLFW_KEY_S])
        camera.ProcessKeyboard(Camera_Movement(BACKWARD), deltaTime);
    if (keys[GLFW_KEY_A])
        camera.ProcessKeyboard(Camera_Movement(LEFT), deltaTime);
    if (keys[GLFW_KEY_D])
        camera.ProcessKeyboard(Camera_Movement(RIGHT), deltaTime);
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key == GLFW_KEY_TAB && action == GLFW_PRESS)
        openSpotLight = !openSpotLight;
    if (action == GLFW_PRESS)           //如果当前是按下操作
        keys[key] = true;
    else if (action == GLFW_RELEASE)            //松开键盘
        keys[key] = false;
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }
    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;
    lastX = xpos;
    lastY = ypos;
    
    GLfloat sensitivity = 0.05;
    xoffset *= sensitivity;
    yoffset *= sensitivity;
    
    camera.ProcessMouseMovement(xoffset, yoffset);
}

现在可以通过Tab按键来控制聚光灯的开关了

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/107856884