OpenGL 和 GLSL 在頂點著色器中動態調整裁剪平面引數的簡單程式碼示例

MarsCactus發表於2024-11-10

以下是一個使用 OpenGL 和 GLSL 在頂點著色器中動態調整裁剪平面引數的簡單程式碼示例:

// OpenGL 初始化程式碼
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

GLFWwindow* window;

// 初始化 GLFW
void initGLFW() {
    if (!glfwInit()) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        exit(EXIT_FAILURE);
    }

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

    window = glfwCreateWindow(800, 600, "Dynamic Clip Plane Example", nullptr, nullptr);
    if (!window) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwMakeContextCurrent(window);
}

// 初始化 GLEW
void initGLEW() {
    glewExperimental = GL_TRUE;
    if (glewInit()!= GLEW_OK) {
        std::cerr << "Failed to initialize GLEW" << std::endl;
        glfwTerminate();
        exit(EXIT_FAILURE);
    }
}
// 頂點著色器程式碼
#version 330 core

layout (location = 0) in vec3 position;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec4 clipPlane; // 裁剪平面引數

out vec3 fragPosition;

void main() {
    fragPosition = vec3(model * vec4(position, 1.0));
    // 計算頂點到裁剪平面的距離
    float distance = dot(vec4(fragPosition, 1.0), clipPlane);
    // 如果頂點在裁剪平面背面,則將其位置設定為一個無效值(這裡簡單設定為很大的負值)
    if (distance < 0.0) {
        gl_Position = vec4(-10000.0, -10000.0, -10000.0, 1.0);
    } else {
        gl_Position = projection * view * model * vec4(position, 1.0);
    }
}
// 片段著色器程式碼
#version 330 core

in vec3 fragPosition;
out vec4 color;

void main() {
    color = vec4(fragPosition, 1.0);
}
// 渲染迴圈函式
void renderLoop() {
    while (!glfwWindowShouldClose(window)) {
        // 處理輸入
        glfwPollEvents();

        // 動態更新裁剪平面引數(這裡只是簡單的示例,你可以根據實際需求更新)
        float clipPlaneParameters[4] = {0.5, 0.5, -0.5, -0.5};
        glUniform4fv(glGetUniformLocation(yourShaderProgram, "clipPlane"), 1, clipPlaneParameters);

        // 清除顏色和深度緩衝區
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // 使用你的著色器程式進行渲染
        glUseProgram(yourShaderProgram);

        // 繪製物體(這裡假設你已經有了一個簡單的物體繪製程式碼)
        // 例如:繪製一個三角形
        GLfloat vertices[] = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f
        };

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

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

        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);
        glEnableVertexAttribArray(0);

        glDrawArrays(GL_TRIANGLES, 0, 3);

        glBindVertexArray(0);
        glDeleteBuffers(1, &VBO);
        glDeleteVertexArrays(1, &VAO);

        // 交換前後緩衝區
        glfwSwapBuffers(window);
    }
}
// 主函式
int main() {
    initGLFW();
    initGLEW();

    // 編譯和連結著色器程式
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

    const char* vertexShaderSource = R"(
        #version 330 core

        layout (location = 0) in vec3 position;

        uniform mat4 model;
        uniform mat4 view;
        uniform mat4 projection;
        uniform vec4 clipPlane;

        out vec3 fragPosition;

        void main() {
            fragPosition = vec3(model * vec4(position, 1.0));
            float distance = dot(vec4(fragPosition, 1.0), clipPlane);
            if (distance < 0.0) {
                gl_Position = vec4(-10000.0, -10000.0, -10000.0, 1.0);
            } else {
                gl_Position = projection * view * model * vec4(position, 1.0);
            }
        }
    )";

    const char* fragmentShaderSource = R"(
        #version 330 core

        in vec3 fragPosition;
        out vec4 color;

        void main() {
            color = vec4(fragPosition, 1.0);
        }
    )";

    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    // 檢查頂點著色器編譯錯誤
    GLint success;
    GLchar infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
        std::cerr << "Vertex Shader Compilation Failed: " << infoLog << std::endl;
        glfwTerminate();
        return -1;
    }

    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    // 檢查片段著色器編譯錯誤
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
        std::cerr << "Fragment Shader Compilation Failed: " << infoLog << std::endl;
        glfwTerminate();
        return -1;
    }

    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    // 檢查著色器程式連結錯誤
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
        std::cerr << "Shader Program Linking Failed: " << infoLog << std::endl;
        glfwTerminate();
        return -1;
    }

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // 設定你的著色器程式
    glUseProgram(shaderProgram);

    // 獲取 uniform 變數的位置
    GLint modelLoc = glGetUniformLocation(shaderProgram, "model");
    GLint viewLoc = glGetUniformLocation(shaderProgram, "view");
    GLint projectionLoc = glGetUniformLocation(shaderProgram, "projection");

    // 設定模型、檢視和投影矩陣(這裡只是簡單的示例矩陣,你需要根據實際情況設定)
    glm::mat4 modelMatrix = glm::mat4(1.0f);
    glm::mat4 viewMatrix = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
    glm::mat4 projectionMatrix = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);

    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMatrix));
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(viewMatrix));
    glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projectionMatrix));

    // 進入渲染迴圈
    renderLoop();

    // 清理資源
    glfwTerminate();
    return 0;
}

在上述程式碼中:

  1. 首先初始化 GLFWGLEW,建立一個 OpenGL 視窗。
  2. 在頂點著色器中,定義了一個 uniform 變數 clipPlane 來接收裁剪平面的引數。在 main 函式中,計算每個頂點到裁剪平面的距離,如果頂點在裁剪平面背面,則將其位置設定為一個無效值,使其在渲染時被裁剪掉。
  3. 在片段著色器中,簡單地將頂點位置作為顏色輸出,以便在螢幕上顯示。
  4. renderLoop 函式中,動態更新裁剪平面引數,並進行渲染操作。

請注意,上述程式碼只是一個簡單的示例,實際應用中你需要根據具體需求進一步完善和擴充套件程式碼,例如處理更多的輸入、載入模型資料等。並且確保你的 OpenGL 環境已經正確配置,並且支援 GLSL 的相關特性。

相關文章