Saturday, November 23, 2013

GPU shader ажиллуулах

Эхлээд shader програмаа бичиж тодорхойлоё. Зорилго нь энгийн гурвалжин дүрс зурах юм. Shader програм нь vertex processing хэсэгтээ энгийн оройн координатын утга хүлээн авна. Цааш нь байрлалын мэдээлэл болгон хувиргаад дамжуулна. fragment processing хэсэг бүх fragment-үүдэд хөх өнгө онооно.


vertex shader
#version 120
attribute vec2 coord2d;
void main(void)
{
    gl_Position = vec4(coord2d, 0.0, 1.0);
}


fragment shader
#version 120
void main(void)
{
    gl_FragColor[0] = 0.0;
    gl_FragColor[1] = 0.0;
    gl_FragColor[2] = 1.0;
}

Shader програмруу vertex processing хэсэгт оролт болж очих гурвалжингийн оройн координатуудыг тодорхойлоё.
float triangle_vertices[] = {
    0.0, 0.8,
    -0.8, -0.8,
    0.8, -0.8
};

vertex shader лүү оройн утга дамжуулахын тулд тэр оройн утгыг нь оролт болгон хадгалах coord2d хувьсагч shader програмын оролтын аттрибутуудын хэддүгээрт байрлалд байгааг нь мэдэх хэрэгтэй. Түүний тулд нэрээр нь хайн байрлалын ID-г нь буцааж авна.

const char* attribute_name = "coord2d";
GLint attribute_coord2d = glGetAttribLocation(programID, attribute_name);


shader програмыг ашиглан гурвалжин дүрсээ зурахын тулд програмын үндсэн цикл дотор байнга дуудан ажиллуулах хэрэгтэй. Үүний тулд shader програмаа glUseProgam() функц ашиглан дуудаад түүний авах attribute байрлалуудыг нь нээж өгөөд тэр байрлалд нь оройн утгуудаа дамжуулж өгөх хэрэгтэй. glDrawArray() функцээр GPU-д оройн өгөгдлүүдийг ашиглан зурах команд өгнө.

    glUseProgram(programID);
    glEnableVertexAttribArray(attribute_coord2d);
    glVertexAttribPointer(
        attribute_coord2d,
        2,
        GL_FLOAT,
        GL_FALSE,
        0,
        triangle_vertices
    );
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glDisableVertexAttribArray(attribute_coord2d);
    glUseProgram(0);


Програмын үндсэн цикл маш олон удаа дуудагдан ажиллах тул өнгөний buffer-ийг нь байнга цэвэрлэж өгч байх хэрэгтэй ингэхгүй бол өмнөх фрэймийн өгөгдөл дээрээ давхарлаж зурах гээд байх болно.
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);


GPU ашиглан дүрс зурах функц мөн shader-лүү оройн өгөгдөл дамжуулах талаар хангалттай дурдсан гэж бодож байна. Програмын бүрэн коодыг энд бас тавъя гэж бодлоо. Mingw ашиглаж хөрвүүлж байгаа болохоор хөрвүүлэхэд хийгдсэн lib холболтын дарааллыг мөн тавилаа.
-lglew32 -lglu32 -lglfw3 -lopengl32 -lglu32 -lgdi32 -luser32 -lkernel32

#define GLEW_STATIC
#include "GL/glew.h"
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>

using namespace std;

GLuint loadShader(string filePath, GLenum shaderType) {
    GLuint shaderID = 0;
    string shaderString;
    ifstream sourceFile(filePath.c_str());
    if (sourceFile) {
        shaderString.assign(
                (istreambuf_iterator<char>(sourceFile)),
                (istreambuf_iterator<char>()));
        shaderID = glCreateShader(shaderType);
        const GLchar* shaderSource = shaderString.c_str();
        glShaderSource(shaderID, 1, (const GLchar**) &shaderSource, NULL);
        glCompileShader(shaderID);
        GLint shaderCompiled = GL_FALSE;
        glGetShaderiv(shaderID, GL_COMPILE_STATUS, &shaderCompiled);
        if (shaderCompiled != GL_TRUE) {
            cout << "cannot compiled" << endl;
            glDeleteShader(shaderID);
            shaderID = 0;
        }
        sourceFile.close();
    } else {
        cout << "cannot open file" << endl;
    }
    return shaderID;
}

int main(int argc, char** argv) {

    GLFWwindow* window;
    if (!glfwInit())
        return EXIT_FAILURE;

    window = glfwCreateWindow(800, 600, "OpenGL Shader", NULL, NULL);
    if (!window) {
        glfwTerminate();
        return EXIT_FAILURE;
    }

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

    glfwMakeContextCurrent(window);

    glewExperimental = GL_TRUE;
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        cout << "error in glewInit " << glewGetErrorString(err) << endl;
        return EXIT_FAILURE;
    }

    cout << "start cooking" << endl;
    float deltaTime = 0.0f;

    GLuint programID = glCreateProgram();
    cout << "created program : " << programID << endl;
    GLuint vertexShader = loadShader("simple.vert.glsl", GL_VERTEX_SHADER);
    if (vertexShader == 0) {
        glDeleteProgram(programID);
        programID = 0;
        return EXIT_FAILURE;
    }
    glAttachShader(programID, vertexShader);
    GLuint fragmentShader = loadShader("simple.frag.glsl", GL_FRAGMENT_SHADER);
    if (fragmentShader == 0) {
        glDeleteProgram(programID);
        programID = 0;
        return EXIT_FAILURE;
    }
    glAttachShader(programID, fragmentShader);
    glLinkProgram(programID);
    GLint programSuccess = GL_TRUE;
    glGetProgramiv(programID, GL_LINK_STATUS, &programSuccess);
    if (programSuccess != GL_TRUE) {
        cout << "error in linkin program " << programID << endl;
        glDeleteProgram(programID);
        programID = 0;
        return EXIT_FAILURE;
    }

    float triangle_vertices[] = {
        0.0, 0.8,
        -0.8, -0.8,
        0.8, -0.8
    };    
    const char* attribute_name = "coord2d";
    GLint attribute_coord2d = glGetAttribLocation(programID, attribute_name);
    

    float startTime = glfwGetTime();
    while (!glfwWindowShouldClose(window)) {
        glClearColor(1.0, 1.0, 1.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        
        glUseProgram(programID);
        glEnableVertexAttribArray(attribute_coord2d);
        glVertexAttribPointer(
                attribute_coord2d,
                2,
                GL_FLOAT,
                GL_FALSE,
                0,
                triangle_vertices
                );
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glDisableVertexAttribArray(attribute_coord2d);
        glUseProgram(0);
        
        glfwSwapBuffers(window);
        glfwPollEvents();

        double lastTime = glfwGetTime();
        deltaTime = lastTime - startTime;
        startTime = lastTime;
    }
    glDeleteProgram(programID);
    glfwTerminate();
    return 0;
}


Бүрэн ажиллагаатай код гэдгийг батлахын тулд screenshot тавилаа. Screenshot тавих нь заримдаа блог постын хувьд бага зэргийн чимэглэл болж өгдөг гэж боддог :)