为什么我的灯光计算错了?

时间:2017-01-19 22:03:56

标签: c++ opengl glsl lighting

我正在使用Blinn-Phong算法来计算场景中的光照,而我还没有考虑距离。在修复第一个问题之前,我还禁用了HDR和Specular计算。

我大约95%肯定在我的片段着色器中出现了错误,但我正在设置一个完整的MCVE以防万一我在其他地方犯了错误。

片段着色器:

R"DATA(#version 430
layout(location = 0) in vec3 vertex_color;
layout(location = 1) in vec3 vertex_normal;
layout(location = 2) in vec3 vertex_position;

layout(location = 0) out vec4 fragment_color;

uniform float ambient_factor = 0.05;
uniform float diffuse_factor = 1;
//Specular Lighting is disabled until I figure out where the mistake is.
uniform float specular_factor = 0;
uniform float specular_shininess = 15;

uniform vec4 light_position = vec4(2, 0, 2, 1);
uniform vec4 light_color = vec4(1, 1, 1, 1);

uniform float exposure = 1;

uniform vec4 eye;

void main() {
    vec3 texture_color = vertex_color;
    vec3 normalized_fragment_normal = normalize(vertex_normal);

    vec3 light_direction = normalize(light_position.xyz - vertex_position);
    vec3 eye_direction = normalize(eye.xyz - vertex_position);
    vec3 halfway_vector = normalize(light_direction + eye_direction);

    float diffuse_strength = clamp(dot(normalized_fragment_normal, light_direction), 0, 1);
    float blinn_phong_specular_strength = max(dot(normalized_fragment_normal, halfway_vector), 0);

    vec3 working_color = vec3(
        texture_color * ambient_factor + 
        light_color.xyz * light_color.w * texture_color * diffuse_strength * diffuse_factor + 
        light_color.xyz * light_color.w * pow(blinn_phong_specular_strength, specular_shininess) * specular_factor
    );

    //For handling HDR, but disabling for now so that the lighting differences/mistakes are more obvious.
    //const float gamma = 2.2;
    //vec3 mapped = vec3(1) - exp(-working_color * exposure);

    //mapped = pow(mapped, vec3(1 / gamma));

    //fragment_color = vec4(mapped, 1);
    fragment_color = vec4(working_color, 1);
})DATA"

顶点着色器:

R"DATA(#version 430
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
layout(location = 2) in vec3 normal;

layout(location = 0) out vec3 vertex_color;
layout(location = 1) out vec3 vertex_normal;
layout(location = 2) out vec3 vertex_position;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model_position;
uniform mat4 model_normal;

void main() {
    gl_Position = projection * view * model_position * vec4(position, 1);
    vertex_color = color;
    vertex_normal = (model_normal * vec4(normal, 0)).xyz;
    vertex_position = (model_position * vec4(position, 1)).xyz;
})DATA"

Shaders.h:

#pragma once

#pragma warning( push )
#pragma warning( once : 4251)
#include<glbinding\gl\gl.h>
#include<glbinding\Binding.h>
#pragma warning( pop ) 

const char * vert_src = ""
#include "Cube.vert.glsl"
;

const char * frag_src = ""
#include "Cube.frag.glsl"
;

//The two Light shaders are just pass-through shaders that make the cube look pure white.
const char * light_vert_src = ""
#include "Light.vert.glsl"
;

const char * light_frag_src = ""
#include "Light.frag.glsl"
;

namespace shaders {
    using namespace gl;
    class failed_shader_compilation_exception : public std::runtime_error {
        std::string error_log;
    public:
        failed_shader_compilation_exception(std::string const& why, std::string const& err) :
            std::runtime_error(why),
            error_log(err) {}
        std::string get_log() const {
            return error_log;
        }
    };

    void handle_shader_error(GLuint shader, std::string const& type, bool is_shader = true) {
        int log_length;
        if (is_shader)
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
        else
            glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &log_length);
        std::string errlog;
        errlog.resize(log_length);
        if (is_shader)
            glGetShaderInfoLog(shader, log_length, nullptr, &errlog[0]);
        else
            glGetProgramInfoLog(shader, log_length, nullptr, &errlog[0]);
        if (is_shader)
            glDeleteShader(shader);
        else
            glDeleteProgram(shader);
        throw failed_shader_compilation_exception("The " + type + " failed to Compile", errlog);
    }

    GLuint get_program(const char * vsrc, const char * fsrc) {
        GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
        int ret;

        glShaderSource(vertex_shader, 1, &vsrc, nullptr);
        glCompileShader(vertex_shader);
        glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
        if (!ret) {
            handle_shader_error(vertex_shader, "Vertex Shader");
        }

        GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);

        glShaderSource(fragment_shader, 1, &fsrc, nullptr);
        glCompileShader(fragment_shader);
        glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
        if (!ret) {
            glDeleteShader(vertex_shader);
            handle_shader_error(fragment_shader, "Fragment Shader");
        }

        GLuint program = glCreateProgram();
        glAttachShader(program, vertex_shader);
        glAttachShader(program, fragment_shader);
        glLinkProgram(program);
        glDeleteShader(vertex_shader);
        glDeleteShader(fragment_shader);

        glGetProgramiv(program, GL_LINK_STATUS, &ret);
        if (!ret) {
            handle_shader_error(program, "Program", false);
        }
        return program;
    }
}

Main.cpp的:

#define GLFW_INCLUDE_NONE
#include<GLFW\glfw3.h>

#pragma warning( push )
#pragma warning( once : 4251)
#include<glbinding\gl43core\gl.h>
#include<glbinding\Binding.h>
#pragma warning( pop ) 

#include<iostream>

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

#include "Shaders.h"

float vertex_data[] {
    0, 0, 0,    1, 0, 0,    0, 0, 1,
    1, 0, 0,    1, 0, 0,    0, 0, 1,
    1, 1, 0,    1, 0, 0,    0, 0, 1,
    0, 0, 0,    1, 0, 0,    0, 0, 1,
    1, 1, 0,    1, 0, 0,    0, 0, 1,
    0, 1, 0,    1, 0, 0,    0, 0, 1,

    1, 0, 0,    0, 1, 0,    1, 0, 0,
    1, 0, -1,   0, 1, 0,    1, 0, 0,
    1, 1, -1,   0, 1, 0,    1, 0, 0,
    1, 0, 0,    0, 1, 0,    1, 0, 0,
    1, 1, -1,   0, 1, 0,    1, 0, 0,
    1, 1, 0,    0, 1, 0,    1, 0, 0,

    1, 0, -1,   0, 1, 1,    0, 0, -1,
    0, 0, -1,   0, 1, 1,    0, 0, -1,
    0, 1, -1,   0, 1, 1,    0, 0, -1,
    1, 0, -1,   0, 1, 1,    0, 0, -1,
    0, 1, -1,   0, 1, 1,    0, 0, -1,
    1, 1, -1,   0, 1, 1,    0, 0, -1,

    0, 0, -1,   1, 0, 1,    -1, 0, 0,
    0, 0, 0,    1, 0, 1,    -1, 0, 0,
    0, 1, 0,    1, 0, 1,    -1, 0, 0,
    0, 0, -1,   1, 0, 1,    -1, 0, 0,
    0, 1, 0,    1, 0, 1,    -1, 0, 0,
    0, 1, -1,   1, 0, 1,    -1, 0, 0,

    0, 1, 0,    0, 0, 1,    0, 1, 0,
    1, 1, 0,    0, 0, 1,    0, 1, 0,
    1, 1, -1,   0, 0, 1,    0, 1, 0,
    0, 1, 0,    0, 0, 1,    0, 1, 0,
    1, 1, -1,   0, 0, 1,    0, 1, 0,
    0, 1, -1,   0, 0, 1,    0, 1, 0,

    0, 0, 0,    1, 1, 0,    0, -1, 0,
    0, 0, -1,   1, 1, 0,    0, -1, 0,
    1, 0, -1,   1, 1, 0,    0, -1, 0,
    0, 0, 0,    1, 1, 0,    0, -1, 0,
    1, 0, -1,   1, 1, 0,    0, -1, 0,
    1, 0, 0,    1, 1, 0,    0, -1, 0,
};

float light_cube_data[]{
    0, 0, 0,    1, 0, 0,    1, 1, 0,
    0, 0, 0,    1, 1, 0,    0, 1, 0,

    1, 0, 0,    1, 0, -1,   1, 1, -1,
    1, 0, 0,    1, 1, -1,   1, 1, 0,

    1, 0, -1,   0, 0, -1,   0, 1, -1,
    1, 0, -1,   0, 1, -1,   1, 1, -1,

    0, 0, -1,   0, 0, 0,    0, 1, 0,
    0, 0, -1,   0, 1, 0,    0, 1, -1,

    0, 1, 0,    1, 1, 0,    1, 1, -1,
    0, 1, 0,    1, 1, -1,   0, 1, -1,

    0, 0, 0,    0, 0, -1,   1, 0, -1,
    0, 0, 0,    1, 0, -1,   1, 0, 0,
};

int main() {
    glfwInit();
    GLFWwindow * window = glfwCreateWindow(300, 300, "bluh", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glbinding::Binding::initialize();
    using namespace gl43core;
    GLuint program = 0, light_program = 0;
    try {
        program = shaders::get_program(vert_src, frag_src);
        light_program = shaders::get_program(light_vert_src, light_frag_src);
    }
    catch (shaders::failed_shader_compilation_exception const& e) {
        std::cerr << e.what() << std::endl;
        std::cerr << e.get_log() << std::endl;
        system("pause");
        return 1;
    }
    glUseProgram(program);
    GLuint vao, vbo;
    GLuint light_vao, light_vbo;

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 9 * sizeof(GLfloat), (void*)0);
    glVertexAttribPointer(1, 3, GL_FLOAT, false, 9 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
    glVertexAttribPointer(2, 3, GL_FLOAT, false, 9 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));

    glGenVertexArrays(1, &light_vao);
    glBindVertexArray(light_vao);
    glGenBuffers(1, &light_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, light_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(light_cube_data), light_cube_data, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 3 * sizeof(GLfloat), (void*)0);

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    glm::mat4 view = glm::lookAt(glm::vec3(2, 3, 3), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));
    struct manip_data {
        glm::quat view_quat;
        glm::vec3 view_position;
        glm::vec4 light_position{ 3, 0, 3, 1 };
        glm::vec4 light_color{ 1, 1, 1, 1 };
        double prior_time;
        double current_time;
    };

    manip_data data;
    data.view_quat = glm::quat(view);
    data.view_position = glm::vec3(glm::inverse(view)[3]);
    data.prior_time = data.current_time = glfwGetTime();

    glfwSetWindowUserPointer(window, &data);
    glfwSetKeyCallback(window, [](GLFWwindow * window, int key, int code, int action, int modifier) {
        manip_data & data = *(reinterpret_cast<manip_data *>(glfwGetWindowUserPointer(window)));
        data.current_time = glfwGetTime();
        double dt = data.current_time - data.prior_time;
        if (action == GLFW_PRESS || action == GLFW_REPEAT) {
            switch (key) {
            case GLFW_KEY_W: data.view_position += glm::inverse(data.view_quat) * glm::vec3(0, 0, -1) * float(dt) * 50.f; break;
            case GLFW_KEY_S: data.view_position += glm::inverse(data.view_quat) * glm::vec3(0, 0, 1) * float(dt) * 50.f; break;
            case GLFW_KEY_A: data.view_position += glm::inverse(data.view_quat) * glm::vec3(-1, 0, 0) * float(dt) * 50.f; break;
            case GLFW_KEY_D: data.view_position += glm::inverse(data.view_quat) * glm::vec3(1, 0, 0) * float(dt) * 50.f; break;
            case GLFW_KEY_LEFT_SHIFT: data.view_position += glm::inverse(data.view_quat) * glm::vec3(0, 1, 0) * float(dt) * 50.f; break;
            case GLFW_KEY_LEFT_CONTROL: data.view_position += glm::inverse(data.view_quat) * glm::vec3(0, -1, 0) * float(dt) * 50.f; break;

            case GLFW_KEY_KP_8: data.view_quat = glm::angleAxis(-float(dt) * 50, glm::vec3(1, 0, 0)) * data.view_quat; break;
            case GLFW_KEY_KP_2: data.view_quat = glm::angleAxis(float(dt) * 50, glm::vec3(1, 0, 0)) * data.view_quat; break;
            case GLFW_KEY_KP_4: data.view_quat = glm::angleAxis(-float(dt) * 50, glm::vec3(0, 1, 0)) * data.view_quat; break;
            case GLFW_KEY_KP_6: data.view_quat = glm::angleAxis(float(dt) * 50, glm::vec3(0, 1, 0)) * data.view_quat; break;
            case GLFW_KEY_KP_9: data.view_quat = glm::angleAxis(-float(dt) * 50, glm::vec3(0, 0, -1)) * data.view_quat; break;
            case GLFW_KEY_KP_7: data.view_quat = glm::angleAxis(float(dt) * 50, glm::vec3(0, 0, -1)) * data.view_quat; break;
            }
        }
        data.prior_time = data.current_time;
    });

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
        data.prior_time = glfwGetTime();
        int width, height;
        glfwGetFramebufferSize(window, &width, &height);
        glViewport(0, 0, width, height);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glm::mat4 projection = glm::perspective(glm::radians(60.f), float(width) / height, 0.1f, 100.f);
        glm::mat4 model;
        glm::mat4 normal_model;

        view = glm::translate(glm::mat4_cast(data.view_quat), -data.view_position);
        glm::vec4 eye = glm::inverse(view)[3];
        glm::vec4 light_position{ 1.25, 0.5, 1.25, 1 };

        glUseProgram(program);
        glBindVertexArray(vao);
        glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, false, glm::value_ptr(projection));
        glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, false, glm::value_ptr(view));
        glUniformMatrix4fv(glGetUniformLocation(program, "model_position"), 1, false, glm::value_ptr(model));
        glUniformMatrix4fv(glGetUniformLocation(program, "model_normal"), 1, false, glm::value_ptr(normal_model));
        glUniform4fv(glGetUniformLocation(program, "eye"), 1, glm::value_ptr(eye));
        glUniform4fv(glGetUniformLocation(program, "light_position"), 1, glm::value_ptr(light_position));

        glDrawArrays(GL_TRIANGLES, 0, sizeof(vertex_data) / sizeof(GLfloat) / 9);

        model = glm::translate(model, glm::vec3(light_position));
        model = glm::scale(model, glm::vec3(0.25, 0.25, 0.25));

        glUseProgram(light_program);
        glBindVertexArray(light_vao);

        glUniformMatrix4fv(glGetUniformLocation(light_program, "projection"), 1, false, glm::value_ptr(projection));
        glUniformMatrix4fv(glGetUniformLocation(light_program, "view"), 1, false, glm::value_ptr(view));
        glUniformMatrix4fv(glGetUniformLocation(light_program, "model_position"), 1, false, glm::value_ptr(model));

        glDrawArrays(GL_TRIANGLES, 0, sizeof(light_cube_data) / sizeof(GLfloat) / 3);

        glfwSwapBuffers(window);
    }

    return 0;
}

这是默认渲染位置的图片,我添加了一些注释来识别我知道灯光是错误的。

Picture of Incorrect Lighting

我正在使用GLFW和glbinding进行OpenGL API访问,但是如果你使用它而不是glbinding,GLEW实际上有相同的代码。我也在使用GLM进行数学运算。

1 个答案:

答案 0 :(得分:3)

您正在重复使用顶点颜色作为法线,并且从不使用数组中的实际法线:

glVertexAttribPointer(1, 3, GL_FLOAT, false, 9 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
glVertexAttribPointer(2, 3, GL_FLOAT, false, 9 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));