Saturday, November 23, 2013

Vertex Buffer Object

Vertex Buffer Object хэрэгжүүлэлт.

Бид гурван хэмжээст объектүүд нь орой болон тэдгээрийг холбосон ирмэгүүдээс бүрддэг гэдгийг мэднэ. Тэгвэл тэдгээр оройнуудыг хаана хадгалж тооцоолдог вэ?

Оройнууд ер нь хаанаас хаа хүртэл юу туулдагийг товчхон бичье.
Эртээ урьдын цагт 3-н хэмжээст объектийг артистууд урлаад файлруу экспорт хийж хадгалж гэнэ. Тэр файлыг нь нээгээд харвал баахан оройн координатууд байна. Компютерийн програмчин тэр файлын бүтэц дарааллыг нь ойлгоод уншиж авах програм бичив. Тэдгээр оройн координатууд одоо програмын санах ой дээр хадгалагдаж буй. Гэвч асар олон цэгийг тооцоолж(рэндэрлэх) явна гэдэг нь үнэндээ их нөөц авсан ажил байдаг. Тийм болохоор эдгээр оройн өгөгдлүүдийг тооцоолоход тун тохиромжтой техник хангамжийн санах ой дээр цааш нь хуулж тавих хэрэг гарчээ. График карт бол энэ ажилдаа маш гаргуун бөгөөд угаасаа ч анхнаасаа ийм л зорилгоор бүтээгдсэн байдаг. Тиймээс график картны ойлгодог хэлээр нь програмын үндсэн санах ойд буй оройн өгөгдлүүдийг график картны санах ой дээр хэсэг зай нөөцлөн авч тэр зайлуу хуулж хийнэ.
График карт дээрхи тэр үүссэн зайг нь Vertex Buffer Object гэж нэрлээд буй.

График картны санах ой дээр өгөгдөл хуулах тодорхой зай авмаар байнаа гэдгийг захиалая. Үүний тулд тэр үүсгэх санах ойн дугаарыг илтгэх ID-тэй болох хэрэгтэй. Цаашид тэр санах ой дээрхи өгөгдөлтэй шууд тулж ажиллахад нь амар болгодог юм.

GLuint triangleVBO;
glGenBuffers(1, &triangleVBO);

Програмын санах ой дээр оройнуудыг тодорхойлсон массив зарлая

const float NUM_OF_VERTICES = 3;
float data[3][3] = {
    { 0.0, 1.0, 0.0},
    { -1.0, -1.0, 0.0},
    { 1.0, -1.0, 0.0}
};

энэ массивийн өгөгдлүүдийг саяны үүсгэсэн GPU санах ойруу хуулж хийе. glBindBuffer функцээр санах ойг ашиглахаар идэвхижүүлнэ.

glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
glBufferData(GL_ARRAY_BUFFER, NUM_OF_VERTICES*3*sizeof(float), data, GL_STATIC_DRAW);

Shader програм нь оролтын өгөгдөлдөө оройнуудын координатуудыг авдаг. Манай тохиолдолд shader маань in_Position гэсэн ганц л оролттой байна. Оролтынх нь байрлалын дугаар нь хамгийн эхнийх буюу 0 юм. Тиймээс shader ийн оролтын параметрийг дугаараар нь хандан тохируулж өгөе. glVertexAttribPointer функц нь энэ параметр дээр ийм урттай, ийм төрлийн өгөгдлүүд орж ирэх юм бас энэ өгөгдлийг buffer-ийн тэддүгээр шилжлэг дээрээс олж уншиж авна гэдгийг тохируулж өгнө ингэснээр shader програм өгөгдлийг buffer-ээс ямар дарааллаар уншиж авахаа мэдэх бололцоотой болно. glEnableVertexAttribArray функцээр shader програмд оролт болж очих аттрибуутуудын массивийн параметрийн дугаарыг идэвхижүүлж өгнө. Яагаад массив ашигладаг вэ гэвэл shader програмын оролтонд оройн байрлалаас гадна өнгө нормал гэх мэтийн утга агуулах өөр олон оролтын аттрибут байлгаж болдог болохоор тэр юм. glBindAttribLocation функцээр shader програм доторхи хувьсагчийн нэр болон shader програмын оролтын аттрибутын дугаар хооронд харгалзаа бий болгож өгдөг. shaderAttribute хувьсагч нь 0 гэсэн утгатай зарлагдсан тул "in_Position" хувьсагч энэ дугаар дээр л оройн өгөгдлүүдийг оролт болгон хүлээн авна.
const unsigned int shaderAttribute = 0;
glVertexAttribPointer(shaderAttribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(shaderAttribute);
glBindAttribLocation(programID, shaderAttribute, "in_Position");

Програмын кодтой танилцая
Vertex Shader
#version 150
 
in  vec3 in_Position;
 
void main(void) 
{
    gl_Position = vec4(in_Position.x, in_Position.y, in_Position.z, 1.0);
}
Fragment Shader
#version 150
 
precision highp float;
 
out vec4 fragColor;
 
void main(void) 
{
    fragColor = vec4(1.0,1.0,1.0,1.0);
}


code
/* 
 * File:   main.cpp
 * Author: Sharavsambuu
 *
 * Created on November 17, 2013, 8:18 PM
 */

#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 triangleVBO;
    const unsigned int shaderAttribute = 0;
    const float NUM_OF_VERTICES = 3;
    float data[3][3] = {
        { 0.0, 1.0, 0.0},
        { -1.0, -1.0, 0.0},
        { 1.0, -1.0, 0.0}
    };
    
    glGenBuffers(1, &triangleVBO);
    glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
    glBufferData(GL_ARRAY_BUFFER, NUM_OF_VERTICES*3*sizeof(float), data, GL_STATIC_DRAW);
    glVertexAttribPointer(shaderAttribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(shaderAttribute);
    glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
    

    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);
    
    glBindAttribLocation(programID, shaderAttribute, "in_Position");
    
    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 startTime = glfwGetTime();
    float buffTime = 0.0;
    int counter = 0;
    while (!glfwWindowShouldClose(window)) {
        
        glUseProgram(programID);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glUseProgram(0);

        glfwSwapBuffers(window);
        glfwPollEvents();

        double lastTime = glfwGetTime();
        deltaTime = lastTime - startTime;
        startTime = lastTime;
        counter++;
        buffTime += deltaTime;
        if (buffTime > 1.0f) {
            buffTime = 0.0f;
            cout << "fps " << counter << endl;
            counter = 0;
        }
    }

    glDeleteProgram(programID);
    glfwTerminate();
    return 0;
}


скрээншот