开发者

Adding distant fog with OpenGL?

开发者 https://www.devze.com 2023-01-15 00:02 出处:网络
I\'m using GL_FOG, but it only fogs up my geometry. I would like to find a way for the fog to feel like it encumbers an area, not just fog up my geometry, sort of lik开发者_JAVA百科e a box of fog...

I'm using GL_FOG, but it only fogs up my geometry. I would like to find a way for the fog to feel like it encumbers an area, not just fog up my geometry, sort of lik开发者_JAVA百科e a box of fog...

Is there a way to do this? Or possibly another way to give a smooth draw distance transition?


If you have a skybox/sphere you can apply the fog to that, the only downside is that it will be there if you look straight up as well.

Your best bet is to dive into shaders as Jerry mentioned. Don't worry though they aren't that bad to work with. If you apply the fog to the scene based on distance AND vertical position in space you can create a nice fog effect without taking over your entire skyshape. You can also use a similar method to give yourself some clouds n' the like if you're feeling bold.

Hope this helps!


The built-in fog is pretty simple, and you only get pretty simplistic control over it. If you don't like how it works, chances are you're going to have to do the job on your own in a fragment shader.


You can create a fog by fading the objects fragment color into the background color based on the distance it is from the camera. You use a curve that interpolates this fog factor based on how far it is along the near plane/far plane, 0 is near plane and 1 is far plane. You can do this with both the color and the alpha value and get a nice effect, eliminating the pop-in effect:

Adding distant fog with OpenGL?

Here is the code, which is based off learnOpenGL tutorials:

#include <iostream>
#include <vector>
#include <algorithm>

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>

#include <learnopengl/camera.h>

#include <iostream>

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>


using std::vector;
using std::cout;
using std::endl;
using glm::mat4;
using glm::vec2;
using glm::vec3;
using glm::vec4;
using glm::perspective;
using glm::radians;
using glm::normalize;

void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// grid dimensions
const unsigned int GRID_WIDTH = 600;
const unsigned int GRID_HEIGHT = 600;

float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;

// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;

mat4 view;
mat4 projection;
float fov = 90.0f;

float nearPlane = 0.1f;
float farPlane = 3.0f;

Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));

class Cube {
public:
    unsigned int shaderProgram;
    unsigned int VBO, VAO;

    mat4 model;

    Cube(vec3 pos=vec3(0,0,0)) {

        model = translate(mat4(1.0), pos);
        // scale it down a little bit to see the different blocks
        model = scale(model, vec3(0.99, 0.99, 0.99));

        float vertices[] = {
             0.5, -0.5, -0.5, 0, 0, 1,// 1
            -0.5, -0.5, -0.5, 0, 0, 1,// 0
             0.5,  0.5, -0.5, 0, 0, 1,// 2
            -0.5, -0.5, -0.5, 0, 0, 1,// 0
            -0.5,  0.5, -0.5, 0, 0, 1,// 3
             0.5,  0.5, -0.5, 0, 0, 1,// 2

            -0.5, -0.5,  0.5, 0, 1, 0,// 0
             0.5, -0.5,  0.5, 0, 1, 0,// 1
             0.5,  0.5,  0.5, 0, 1, 0,// 2
             0.5,  0.5,  0.5, 0, 1, 0,// 2
            -0.5,  0.5,  0.5, 0, 1, 0,// 3
            -0.5, -0.5,  0.5, 0, 1, 0,// 0

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

             0.5,  0.5, -0.5, 1, 0.5, 0, // 1
             0.5,  0.5,  0.5, 1, 0.5, 0, // 0
             0.5, -0.5, -0.5, 1, 0.5, 0, // 2
             0.5,  0.5,  0.5, 1, 0.5, 0, // 0
             0.5, -0.5,  0.5, 1, 0.5, 0, // 3
             0.5, -0.5, -0.5, 1, 0.5, 0, // 2

            -0.5, -0.5, -0.5, 1, 1, 1,//0
             0.5, -0.5, -0.5, 1, 1, 1,//1
             0.5, -0.5,  0.5, 1, 1, 1,//2
             0.5, -0.5,  0.5, 1, 1, 1,//2
            -0.5, -0.5,  0.5, 1, 1, 1,//3
            -0.5, -0.5, -0.5, 1, 1, 1,//0

             0.5,  0.5, -0.5, 1, 1, 0, //1
            -0.5,  0.5, -0.5, 1, 1, 0, //0
             0.5,  0.5,  0.5, 1, 1, 0, //2
            -0.5,  0.5, -0.5, 1, 1, 0, //0
            -0.5,  0.5,  0.5, 1, 1, 0, //3
             0.5,  0.5,  0.5, 1, 1, 0, //2

        };


        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);

        glBindVertexArray(VAO);

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

        // position attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);

        // color attribute
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(1);

        const char *vertexShaderSource = "#version 330 core\n"
            "layout (location = 0) in vec3 aPos;\n"
            "layout (location = 1) in vec3 aCol;\n"
            "uniform mat4 model;\n"
            "uniform mat4 view;\n"
            "uniform mat4 projection;\n"
            "out vec4 outColor;\n"
            "out vec3 FragPos;\n"
            "void main()\n"
            "{\n"
            "   gl_Position = projection * view * model * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
            "   outColor = vec4(aCol,1.0f);\n"
            "   FragPos = vec3(model * vec4(aPos.x, aPos.y, aPos.z, 1.0));\n"
            "}\0";
        const char *fragmentShaderSource = "#version 330 core\n"
            "out vec4 FragColor;\n"
            "in vec4 outColor;\n"
            "in vec3 FragPos;\n"

            "uniform float nearPlane;\n"
            "uniform float farPlane;\n"
            "uniform vec3 viewPos;\n"
            "uniform vec3 fogColor;\n"
            "float getFogFactor(float d, float nearPlane, float farPlane)\n"
            "{\n"
            "    float FogMax = 1.0f * farPlane;\n"
            "    float FogMin = 0.5f * farPlane;\n"
            "    if (d>=FogMax) return 1.0f;\n"
            "    if (d<=FogMin) return 0.0f;\n"
            "   return 1.0f - (FogMax - d) / (FogMax - FogMin);\n"
            "}\n"

            "float getFogFactorAlpha(float d, float nearPlane, float farPlane)\n"
            "{\n"
            "    float FogMax = 1.0f * farPlane;\n"
            "    float FogMin = 0.7f * farPlane;\n"

            "    if (d>=FogMax) return 1.0f;\n"
            "    if (d<=FogMin) return 0.0f;\n"

            "    return 1.0f - (FogMax - d) / (FogMax - FogMin);\n"
            "}\n"

            "void main()\n"
            "{\n"
            "   float d = distance(viewPos, FragPos);\n"
            "   float fogFactor = getFogFactor(d, nearPlane, farPlane);\n"
            "   float alpha = getFogFactorAlpha(d, nearPlane, farPlane);\n"
            "   FragColor = mix(outColor, vec4(fogColor, 1.0f), fogFactor);\n"
            "   FragColor.a = 1-alpha;\n"
            "}\n\0";

        // vertex shader
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        // check for shader compile errors
        int success;
        char infoLog[512];
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
            cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl;
        }
        // fragment shader
        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        // check for shader compile errors
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
            cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl;
        }
        // link shaders
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        // check for linking errors
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
            cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << endl;
        }
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
    }

    void setMatrix(std::string name, glm::mat4 mat) {
        glUseProgram(shaderProgram);
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, GL_FALSE, &mat[0][0]);
    }
    void setVec3(std::string name, glm::vec3 value) {
        glUseProgram(shaderProgram);
        glUniform3f(glGetUniformLocation(shaderProgram, name.c_str()), value.x, value.y, value.z);
    }

    void setFloat(std::string name, float value) {
        glUseProgram(shaderProgram);
        glUniform1f(glGetUniformLocation(shaderProgram, name.c_str()), value);
    }

    void draw() {
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
    }


    vec3 getPosition() {
        return vec3(model[3]);
    }
};

Cube *cube;

int main()
{

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Fog example", NULL, NULL);
    if (window == NULL)
    {
        cout << "Failed to create GLFW window" << endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);
    glfwSetScrollCallback(window, scroll_callback);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        cout << "Failed to initialize GLAD" << endl;
        return -1;
    }

    projection = perspective(radians(fov), (float)SCR_WIDTH/(float)SCR_HEIGHT, nearPlane, farPlane);
    vec3 fogColor(0.8f, 0.9f, 1.0f);
    glClearColor(fogColor.x, fogColor.y, fogColor.z, 1.0);

    cube = new Cube();


    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 

    while (!glfwWindowShouldClose(window))
    {

        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;


        processInput(window);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        cube->setFloat("nearPlane", nearPlane);
        cube->setFloat("farPlane", farPlane);
        cube->setVec3("fogColor", fogColor);
        cube->setVec3("viewPos", camera.Position);
        cube->setMatrix("model", cube->model);
        cube->setMatrix("view", camera.GetViewMatrix());
        cube->setMatrix("projection", projection);
        cube->draw();

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();

    delete cube;

    return 0;
}

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);     

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        camera.ProcessKeyboard(RIGHT, deltaTime);
}


// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    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;

    camera.ProcessMouseMovement(xoffset, yoffset);
}

// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}


void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{

}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号