본문 바로가기
3D웹 프로그래밍/opengl

기본적인 Opengl 프로그램 만들기

by 디찌s 2020. 11. 18.
728x90
반응형

모든 Opengl 함수는 gl 로 시작한다.

#include <sb6.h>

class simpleclear_app : public sb6::application
{
    void init()
    {
        static const char title[] = "Simple Clear";

        sb6::application::init();

        memcpy(info.title, title, sizeof(title));
    }

    virtual void render(double currentTime)
    {
        static const GLfloat red[] = { 1.0f, 0.0f, 0.0f, 1.0f };
        glClearBufferfv(GL_COLOR, 0, red);
    }
};

DECLARE_MAIN(simpleclear_app)

ㅇ 해당 함수의 일부 인자타입을 함수 이름 끝에 접미사로 줄여 쓰는 등 여러 가지 네이밍 컨벤션을 따른다.

예를들어 위와같이 glClearBufferfv 에 fv라는 접미사는 함수가 부동소수점f값을 갖는 벡터 v를 사용한다는 의미이다.

 

glClearBufferfv() 함수는 첫번째 인자인 버퍼를 세 번쨰 인자의 값으로 지우라는 명령을 OpenGL에 내린다.

두번째 인자는 drawBuffer는 지울 출력 버퍼가 여럿일떄 사용한다. 여기서는 하나의 버퍼만 사용하면 draw Buffer는 0 기반의 인덱스를 사용하므로, 이예제에서는 값을 0으로 설정한다. 

 

red[] 는 빨간색,녹색,파란색의 뜻이며 빨간색 1.0이니 이값은 빨간색으로 표시한다는 의미이며 4번쨰 값인 1.0f는 투명도를 표현하며 알파라고 부른다. 이값이 0으로 설정하면 프래그먼트는 완전히 투명해지며, 1로 설정하면 완전히 불투명해진다.

 

또한 render함수는 무한루프를 돌며 화면을 전환해주는 역할을 한다.

 

이제 코드를 실행해보면

빨간색 창이 뜨는것을 확인할수있다.

 

시간 변화에 따른 color 변화

#include <sb6.h>
#include <vmath.h>
class simpleclear_app : public sb6::application
{
    void init()
    {
        static const char title[] = "Simple Clear";

        sb6::application::init();

        memcpy(info.title, title, sizeof(title));
    }

    void render(double currentTime)
    {
        const GLfloat red[] = { (float)sin(currentTime) *0.4f +0.5f,
                                       (float)cos(currentTime) *0.5f +0.5f, 0.0f,1.0f};
      
        glClearBufferfv(GL_COLOR, 0, red);
    }
};

DECLARE_MAIN(simpleclear_app)

 

 

 

sin,cos함수 red값을 시간의 따라 주기적으로 변경하여 윈도우창의 색을 변경하는 코드이다.

 

 

Shader 프로그램

 

Opengl은 쉐이더라는 불리는 여러 작은 프로그램을 고정 함수로 연결시켜 작동한다.

 

화면에 나타낼때 그래픽스 프로세서는 사용자가 작성한 쉐이더를 실행시키고 그 입력과 출력을 파이프라인으로 이동시켜 최종 픽셀이 그려지게 한다.

 

OpenGl 쉐이더는 OpenGL 쉐이딩 언어 (GLSL)로 작성한다. 이 언어는 C에 기반을 둔다.

 

사용자가 작성한 쉐이더 소스 코드는 쉐이더 객체로 바뀌어 컴파일 되고 여러 쉐이더 객체들이 하나의 프로그램 객체로 링크된다.

 

OpenGL 쉐이더 스테이지

  - 버텍스 쉐이더

  - 테셀레이션 컨트롤 및  이벨루이션 쉐이더

  - 지오메트리 쉐이더

  - 프래그먼트 쉐이더

  - 컴퓨터 쉐이더

 

왠만한 파이프라인은 적어도 하나의 버텍스 쉐이더로 이루어진다. 하지만 화면에 픽셀을 그리려면 프래그먼트 쉐이더가 필요하다.

 

버텍스 쉐이더

#version 430 core

void main(void){
	gl_Position = vec4(0.0,0.0,0.5,1.0);
}

#v430 core는 version 4.3을 사용한다는 의미이며 core라는 키워드는 opengl의 코어 프로파일의 기능만 사용한다는 의미이다.

 

gl_Position은 버텍스의 출력 위치를 나타낸다. 값으로 vec4(0.0,0.0,0.5,1.0)을 할당하면 버텍스가 Opengl의 클립 공간의 중앙에 위치하게 된다. 클립 공간은  OpenGL파이프라인의 다음 스테이지에서 적용되는 좌표계다.

 

 

프래그먼트 쉐이더

#version 430 core

out vec4 color;

void main(void){
	color = vec4(0.0,0.8,1.0,1.0);
}

 

out 키워드는 color를 출력 변수로 선언하여 프래그먼트 쉐이더에서 출력 변숫값을 윈도우나 화면으로 보낸다.

 

이제 버텍스 쉐이더와 프래그먼트 쉐이더가 모두 준비되었고 이제 컴파일후 Opengl에서 실행될수 있는 프로그램으로 링크시킬 차례이다.

 

 

 

쉐이더 컴파일 함수

    GLuint compile_shaders(void) {
        GLuint vertex_shader;
        GLuint fragment_shader;
        GLuint program;
        
        //vertex shader 소스
        static const GLchar* vertex_shader_source[] = {
            "#version 430 core\n"
            "\n"
            "void main(void)\n"
            "{\n"
            "color = vec4(0.0,0.8,1.0,1.0); \n"
            "}"
        };
        //fragment shader 소스
        static const GLchar* fragment_shader_source[] = {
            "#version 430 core \n"
            "\n"
            "out vec4 color;\n"
            "{\n"
            "color=vec4(0.0,0.8,1.0,1.0)\n"
            "}"
        };
        //버텍스 쉐이더를 생성하고 컴파일한다.
        vertex_shader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex_shader, 1, vertex_shader_source, NULL);
        glCompileShader(vertex_shader);

        //프래그먼트 쉐이더를 생성하고 컴파일하낟.
        fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragment_shader, 1, fragment_shader_source, NULL);
        glCompileShader(fragment_shader);

        //프로그램을 생성하고 쉐이더를 어태치(attach,부착)시키고 링크한다.
        program = glCreateProgram();
        glAttachShader(program, vertex_shader);
        glAttachShader(program, fragment_shader);
        glLinkProgram(program);

        //이제 프로그램이 쉐이더를 소유하므로 쉐이더를 삭제한다
        glDeleteShader(vertex_shader);
        glDeleteShader(fragment_shader);

        return program;


    }

 

함수설명

glCreateShader()

 - 빈 쉐이더 객체를 생성한다. 

glShaderSource()

 - 쉐이더 소스 코드를 쉐이더 객체로 전달해서 복사본을 유지한다.

glCompileShader()

 - 쉐이더 객체에 포함된 소스 코드를 컴파일한다.

glCreateProgram()

 - 쉐이더 객체에 어태치시킬 프로그램 객체를 생성한다.

glAttachShader()

 - 쉐이더 객체를 프로그램 객체에 어태치시킨다.

glLinkProgram()

 - 프로그램 객체에 어태치된 모든 쉐이더 객체를 링크한다.

glDeleteShader()

 - 쉐이더 객체를 삭제한다. 쉐이더가 프로그램 객체에 링크되면, 프로그램이 바이너리 코드를 보관하며 쉐이더는 더이상 필요없게된다.

 

 

버텍스 배열 객체

쉐이더 객체를 만들고 컴파일한뒤 프로그램객체에 링크까지 했다. 이제 마지막으로 할일은 버텍스 배열 객체(VAO)를 생성하는것이다.

이는 OpenGL 파이프라인의 버텍스 페치 스테이지를 나타내는 객체며 입력을 버텍스 쉐이더에 공급하기 위해 사용된다.

 

VAO 생성 할때는 glGenVertexArrays()함수를 호출하여 VAO를 콘텍스트에 attach하고 glBindVertexArray()를 호출해야한다.

 

 

public:
    void startup() {
        rendering_program = compile_shaders();
        glGenVertexArrays(1, &vertex_array_object);
        glBindVertexArray(vertex_array_object);
    }

    void shutdown() {
        glDeleteVertexArrays(1, &vertex_array_object);
        glDeleteProgram(rendering_program);
        glDeleteVertexArrays(1, &vertex_array_object);
    }


private:
    GLuint rendering_program;
    GLuint vertex_array_object;

 

위에 나오는 소스코드중 startup 함수는 sb6에서 오버라이드 받아 작성된 함수이며, 초기 프로그램이 시작될때 한번 실행하는 함수이다.

 

 

한 점 렌더링하기

void render(double currentTime)
    {
        const GLfloat color[] ={ (float)sin(currentTime) *0.4f + 0.5f , (float)cos(currentTime) *0.5f +0.5f, 0.0f,1.0f};
      
        glClearBufferfv(GL_COLOR, 0, color);

        //렌더링을 위해 생성했던 프로그램 객체를 사용한다.
        glUseProgram(rendering_program);

        //점을 하나 그린다.
        glDrawArrays(GL_POINTS, 0, 1);
        //점 크기를 확대시킨다
		glPointSize(40.0f);
    }

glDrawArray()함수의 첫번째 인자는 어떤 타입의 그래픽스 프리미티브를 opengl로 렌덩할지 결정한다 두번째인자는 아직 중요하지않으니 넘어가고 마지막인자는 렌더링할 버텍스의 개수다. 각 점은 하나의 버텍스로 표현되면, OpenGL에 하나의 버텍스만 렌더링하도록 명령했기 때문에 하나의 점만 렌더링된다.

 

728x90
반응형

댓글