GLFW(2) - uniform

今天來用 OpenGL 畫一個呼吸燈,下面圖片的三角形顏色會隨著時間改變。

1. Header, shaders and vertices:

先加入會使用的函式庫、著色器及頂點座標。

於 Fragment shader 中定義一個 uniform 變數,透過 uniform 變數我們可以將顏色傳遞給 shader。

#include<iostream>
using namespace std;
#include<math.h>
#include<GL/glew.h>
#include<GLFW/glfw3.h>


const GLchar* vertexShaderSource = R"glsl(
    #version 330 core
    layout (location = 0) in vec4 position;

    void main()
    {
        gl_Position = position;
    }
)glsl";

const GLchar* fragmentShaderSource = R"glsl(
    #version 330 core
    
    uniform vec4 frag_color;

    out vec4 color;
    
    void main()
    {
        color = frag_color;
        // color = vec4(.3, .2, .1, 1.);
    }
)glsl";

GLfloat vertices[] = {
     // First triangle
    -0.3, 0.01, 0.0, 1.,
     0.0,  0.5, 0.0, 1.,
     0.3, 0.01, 0.0, 1.,
    
     // Second triangle
     0.0,   -0.5, 0.0, 1.,
    -0.3,  -0.01, 0.0, 1.,
     0.3,  -0.01, 0.0, 1.,
};

GLuint indices[] = {
    0, 1, 2,
    3, 4, 5,
};

2. 撰寫 key 事件:

新增一些按鍵偵測的流程,ESC 會關閉視窗、E 只畫出線條、F 會將顏色填滿。

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

    if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }

    if (glfwGetKey(window, GLFW_KEY_F) == GLFW_PRESS) {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
}

3. 編輯 main:

開個 800*600 的視窗,用 GLEW 進行初始化。

int main() {
    if (!glfwInit()) {
        return -1;
    }

    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(800, 600, "Hello GL", NULL, NULL);
    if (window == NULL) {
        cout << "Fail to create window" << endl;
        return -1;
    }
    glfwMakeContextCurrent(window);

    // load funcs
    glewInit();

將 shader 編譯成 program,簡單檢查一下編譯過程是否有問題。

    // vertex shader
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    int success;
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        cout << "Fail to create the vertex shader" << endl;
        return -1;
    }
    
    // fragment shader
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        cout << "Fail to create the fragment shader" << endl;
        return -1;
    }
    
    // link
    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        cout << "Fail to link" < endl;
        return -1;
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

準備 VAO, VBO 和 EBO。VAO 代表資料的拿法、VBO 代表資料 buffer、代表 index。

    // vao vbo ebo
    unsigned int vao, vbo, ebo;
    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);
    glGenBuffers(1, &ebo);

    glBindVertexArray(vao);

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

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4*sizeof(GL_FLOAT), (void *)0);
    glEnableVertexAttribArray(0);

每一禎利用時間計算 sin 跟 cos 並設定顏色。

我們利用 glGetUniformLocation 取得變數然後用 glUniform4f 設定 uniform。因為 frag_color 是 vec4 所以用 4f 設定。

    glViewport(0, 0, 800, 600);
    glClearColor(.1, .0, .3, 1.);
    while(!glfwWindowShouldClose(window)) {
        processInput(window);
        
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderProgram);
        double t = glfwGetTime();
        float r = (sin(t) + 1.0) / 2.0;
        float g = (cos(t) + 1.0) / 2.0;
        unsigned int colorLocation = glGetUniformLocation(shaderProgram, "frag_color");
        glUniform4f(colorLocation, r, g, 0., 1.); 
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

最後釋放所有資源。

    // release
    glDeleteProgram(shaderProgram);
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &ebo);

    glfwTerminate();
    return 0;
}

2. Makefile:

簡單寫個 mac 的 makefile,linux 要微調 lib 的部分。

TARGET=main.out
LIBS=-lglfw -lglew -framework OpenGL
	
CFLAGS=-std=c++11

$(TARGET): main.cpp
	g++ $(CFLAGS) -o $@ $^ $(LIBS)

.PHONY: clean
clean:
	rm $(TARGET)

留言

熱門文章