이번엔 셰이더를 적용해 봅시다.

뭔가 초장부터 중간보스를 만난 느낌이네요.



들어가기 앞서 기본 개념인 버텍스와 픽셀에 대해서 짚어봅시다.

3D 그래픽을 조금이라도 접해본 사람들은 다 알만한 개념이긴 한데 원문에서 이 개념을 설명하기 위해 한 강의를 투자하였기 때문에 간단히 짚고 갑니다.


버텍스: 3차원 그래픽 공간에서 한 점을 의미하는 가장 기본적인 개념입니다. 카르테시안 좌표계에서 x, y, z 요소로 나타낼 수 있죠.

픽셀: 물리적으로 화면에서 한 점을 나타내는 최소 단위입니다. 하나의 픽셀은 RGBA를 조합한 한가지 색만을 표현할 수 있습니다.



쉐이더

쉐이더도 일종의 프로그램입니다. 일반적인 C/C++ 네이티브 프로그램과 다른점은 쉐이더는 CPU가 아니라 GPU에서 처리되는 프로그램이라는 점이죠(그치만 쿠다를 사용한다면?!). 쉐이더는 여러가지가 있는데 책 "OpenGL Super Bible 6th" 에서 발췌한 렌더링 파이프라인 다이어그램에서 그 중 몇가지를 볼 수 있습니다.




이미지에서 볼 수 있듯이 렌더링 파이프라인에서 가장 처음과 끝에 버텍스 쉐이더와 프레그먼트 쉐이더가 위치합니다. 3D를 렌더링하는데 빠져서는 안 될 엄청 중요한 놈들이죠. 이것들 외에 추가로 사용할 수 있는 쉐이더가 몇 개 더 있는데요 테설레이션 쉐이더와 지오메트리 쉐이더입니다. 렌더링 파이프라인에서 얘네들이 없이도 충분히 3D를 화면을 렌더링 할 수 있지만, 사용한다면 더욱 정교하게 픽셀을 관리하여 멋진 효과를 줄 수 있죠. 이러한 쉐이더들은 프로그래밍으로 직접 컨트롤 할 수 있는데 반해, 버텍스 패치(Vertex fetch), 레스터라이제이션(Rasterization), 프레임버퍼 오퍼레이션(Buffer operations)은 직접 컨트롤 할 수 없습니다. 


예제 코드를 작성해보기 전에 쉐이더간의 기본동작에 대해 알아봅시다.

파이프라인에서 한 쉐이더에서 다음 쉐이더로 넘어갈 때 변수를 이용해서 데이터를 전달할 수 있습니다. 이 때 변수들은 다음의 특정 규칙을 따라야 합니다.


1. 변수의 이름과 타입은 앞 쉐이더의 것과 뒷 쉐이더의 것이 일치해야 합니다(뭐 당연한거 같네요).

2. 변수는 파이프라인상에서 바로 다음에 위치한 쉐이더에게만 전달할 수 있습니다. 예를 들어 버텍스 쉐이더, 테설레이션, 프레그먼트 쉐이더를 사용한다고 할 때 버텍스 쉐이더에서 프레그먼트 쉐이더로 데이터를 전달하고 싶다면, 먼저 버텍스 쉐이더에서 테설레이션 쉐이더로 변수를 전달한 다음 다시 테설레이션에서 프레그먼트 쉐이더로 변수를 전달해야 합니다.


또한 CPU에서 보내지는 버텍스 데이터들은 버퍼를 통해 전달되는데, 버텍스 쉐이더에서 밖에 접근할 수 없습니다. 반면 쉐이더간 데이터전달에 사용되는 유니폼 변수들은 어느 쉐이더에서든 접근이 가능합니다(위의 2번 조건과 함께 생각해보면 각 쉐이더가 실행될 때 유니폼 변수들이 같은 메모리 공간을 사용하는거 같아 보이네요. 같은 메모리 주소를 사용하니까 변수 생존 기간이 한 쉐이더에서 다음 쉐이더까지인가 봅니다).


후 드디어 코딩입니다. 쉐이딩 언어로는 GLSL을 사용합니다. OpenGL Shading Language 의 약자에요ㅋ


예제프로그램에서는 쉐이더 소스를 파일로 작성하고, 메인 루틴에서 실행중에 동적으로 이 쉐이더 소스를 읽어와 컴파일하고 렌더링 파이프 라인에 적용합니다. 먼저 이러한 동작을 하는 C++ 모듈을 만들어 보죠.


프로젝트에 Core라는 폴더를 만들고 그 안에 Shader_Loader.h 와 Shader_Loader.cpp 파일을 작성합니다.


// Shader_Loader.h
#pragma once

#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>

namespace Core
{

    class Shader_Loader
    {
    private:

        std::string ReadShader(char *filename);
        GLuint CreateShader(GLenum shaderType,
            std::string source,
            char* shaderName);

    public:

        Shader_Loader(void);
        ~Shader_Loader(void);
        GLuint CreateProgram(char* VertexShaderFilename,
            char* FragmentShaderFilename);

    };
}


// Shader_Loader.cpp
#include "Shader_Loader.h" 
#include <iostream>
#include <fstream>
#include <vector>

using namespace Core;

Shader_Loader::Shader_Loader(void){}
Shader_Loader::~Shader_Loader(void){}

std::string Shader_Loader::ReadShader(char *filename)
{

    std::string shaderCode;
    std::ifstream file(filename, std::ios::in);

    if(!file.good())
    {
        std::cout<<"Can't read file "<<filename<<std::endl;
        std::terminate();
    }

    file.seekg(0, std::ios::end);
    shaderCode.resize((unsigned int)file.tellg());
    file.seekg(0, std::ios::beg);
    file.read(&shaderCode[0], shaderCode.size());
    file.close();
    return shaderCode;
}

GLuint Shader_Loader::CreateShader(GLenum shaderType, 
                                   std::string source, char* shaderName)
{

    int compile_result = 0;

    GLuint shader = glCreateShader(shaderType);
    const char *shader_code_ptr = source.c_str();
    const int shader_code_size = source.size();

    glShaderSource(shader, 1, &shader_code_ptr, &shader_code_size);
    glCompileShader(shader);
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_result);

    //check for errors
    if (compile_result == GL_FALSE)
    {

        int info_log_length = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
        std::vector<char> shader_log(info_log_length);
        glGetShaderInfoLog(shader, info_log_length, NULL, &shader_log[0]);
        std::cout << "ERROR compiling shader: " << shaderName << std::endl << &shader_log[0] << std::endl;
        return 0;
    }
    return shader;
}

GLuint Shader_Loader::CreateProgram(char* vertexShaderFilename,
                                    char* fragmentShaderFilename)
{

    //read the shader files and save the code
    std::string vertex_shader_code = ReadShader(vertexShaderFilename);
    std::string fragment_shader_code = ReadShader(fragmentShaderFilename);

    GLuint vertex_shader = CreateShader(GL_VERTEX_SHADER, vertex_shader_code, "vertex shader");
    GLuint fragment_shader = CreateShader(GL_FRAGMENT_SHADER, fragment_shader_code, "fragment shader");

    int link_result = 0;
    //create the program handle, attatch the shaders and link it
    GLuint program = glCreateProgram();
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);

    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &link_result);
    //check for link errors
    if (link_result == GL_FALSE)
    {

        int info_log_length = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
        std::vector<char> program_log(info_log_length);
        glGetProgramInfoLog(program, info_log_length, NULL, &program_log[0]);
        std::cout << "Shader Loader : LINK ERROR" << std::endl << &program_log[0] << std::endl;
        return 0;
    }
    return program;
}


gl 라이브러리 함수 및 Shader_Loader 클래스의 설명입니다.


GLuint: 

쉐이더와 프로그램 핸들들을 갖습니다. GLuint 변수는 기본적으로 이러한 엔티티(쉐이더와 프로그램 핸들들)를 갖는 빈 객체로 존재합니다. OpenGL 에서는 '프로그램' 이라는 용어를 좀 헷갈리게 사용하는데요, OpenGL에서 정의하는 '프로그램'이라는 용어는 쉐이더들(vertex, fragment, tessellation, geometry)을 담을 수 있는 컨테이너를 의미합니다.


glCreateShader(GLenum shader_type):

인자로 전달받는 shader_type 으로 비어있는 쉐이더 객체를 생성하고 핸들을 반환합니다.


glShaderSource(GLuint shader, GLsizei count, 

                     const GLchar **shader_code, const GLint *legth):

shader 객체에 shader_code 를 로드합니다. *GLchar 은 캐릭터형 배열이며 이 배열의 갯수를 count 로 알려줍니다. length 는 각 *GLchar 의 길이 정보를 갖는 배열입니다. 예제에선 소스코드를 한개씩 전달하였으므로 count 를 1로 전달하였죠.


glCompileShader(GLuint shader):

소스코드를 컴파일합니다.


glGetShaderiv(GLuint shader, GLenum pname, GLint *prarams):

에러를 확인하고 콘솔로 검출해냅니다


glCreateProgram():

프로그램 객체를 생성하고 그 핸들을 반환합니다.


glAttachShader(GLuint program, GLuint shader):

프로그램에 쉐이더를 붙입니다.


glLinkProgram(GLuint program):

프로그램 객체를 링킹합니다.


glGetProgramiv(GLuint program, GLenum pname, GLint *params):

glGetShaderiv과 마찬가지로 에러를 확인하고 콘솔로 검출해냅니다.


glUseProgram(GLuint program):

렌더링 루프에서 program 객체를 이용하여 렌더링 하로독 지정합니다.


Shader_Loader: 

생성자 함수...


~Shader_Loader: 

소멸자 함수...


CreateShader: 

쉐이더를 생성하고 컴파일.


ReadShader: 

쉐이더 파일의 소스를 읽는 메서드


CreateProgram: 

내부적으로 ReadShader, CreateShader 메서드를 호출 하여 버텍스와 프레그먼트 쉐이더를 불러오고 프로그램에 담은 후 링킹하는 메서드



버텍스 쉐이더와 프레그먼트 쉐이더

쉐이더에서 가장 중요한 것은 각 쉐이더들은 버텍스를 다루는데 자신만의 고유의 역할을 갖는다는 것입니다. 버텍스 쉐이더는 x,y,z 3차원 좌표로 이루어진 버텍스들이 2차원 화면에서는 어떤 위치에 그려질지 정사영하는 일을, 프래그먼트 쉐이더는 각 픽셀들이 어떤 색상을 갖는지 정하는 일을 하는 식이죠.


버텍스 쉐이더에서 여러분이 꼭 기억해야할 변수는 gl_Position 입니다. 이 변수는 미리정의 되어 있으며 현재 버텍스의 작업이 다 끝난 후의 스크린상의 점의 위치를 가리킵니다.


프레그먼트 쉐이더의 가장 큰 역할은 각각의 프레그먼트들의 색상을 결정하는 것입니다. 이 쉐이더에서 반환값은 색상버퍼뿐이며, 이 색상버퍼에 표현되지 않은 오브젝트들은 전부 검정색으로 표현됩니다.


다음으로 버텍스 쉐이더의 코드를 작성해 보죠. Core 폴더와 같이 Shaders 폴더도 하나 만들고 Vertex_Sahder.glsl 과 Fragment_Shader.glsl 파일을 작성합니다.


// Vertex_Shader.glsl
#version 330 core

void main(void)
{
    const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
                                     vec4(-0.25, -0.25, 0.5, 1.0),
                                     vec4( 0.25, 0.25, 0.5, 1.0));
    gl_Position = vertices[gl_VertexID];
}


// Fragment_Shader.glsl
#version 330 core
out vec4 color;

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


모든 쉐이더는 먼저 OpenGL 의 버전을 명시해주어야 합니다. 전 3.3버전이므로 330 이라고 적었습니다. 원글에서는 430 이라고 적었네요. 버전 다음에 나오는 core라는 단어는 해당 버전의 glsl 핵심 함수들을 사용할 것임을 나타냅니다. 


다음으로 main 함수로 넘어가봅시다. 3D 그래픽에서 삼각형을 그리기 위해서는 꼭짓점을 나타내는 세 개의 버텍스가 필요합니다. OpenGL의 버텍스 쉐이더에서는 이 버텍스들을 스크린상의 어디에 나타낼 것인지 계산을 한 후 gl_Position 변수에 담아 다음 쉐이더로 보내죠.



위 이미지처럼 스크린은  x, y 값의 범위가 모두 -1~1을 갖는 좌표계로 표현 됩니다. (0, 0)은 화면의 정중앙을 나타내죠. 그래서 200x400 해상도의 스크린을 예로 들면 소스코드의 삼각형은 스크린상에서 꼭지점 (125, 150), (75, 150), (125, 150) 으로 표현됩니다. 소스코드에서 사용된 이 좌표표현 방법을 NDC(Nomalized Device Coordinates) 라고 합니다.


실제로 NDC는 x, y, z, w 네가지 요소로 표현이 됩니다. 네 번째 원소인 w는 W를 표현하는데 위 소스의 경우 W가 1.0으로 NDC가 버텍스의 좌표를 다루고 있다는 것을 나타냅니다(W가 뭘 의미하는지는 잘 모르겠네요). NDC 변환이 끝난 후 Window Transformation 또는 Screen Transformation 변환을 합니다. 이 두 변환작업은 장면(scene)을 OpenGL의 viewport(윈도우창 안쪽의 OpenGL 로 그려질 부분)에 맞추기 위해 하는데, 그래픽카드에서 진행되므로 신경쓸 필요 없습니다. 여기까지 모든 변환을 마친 최종 좌표값들은 모든 모양들이 픽셀/프레그먼트로 전환되기 위해 raterization 과정을 거칩니다. 그러면 위 삼각형은 다음 이미지와 같은 형태를 갖게되겠죠.



glVertexID는 현재 처리되고 있는 버텍스의 ID를 나타냅니다. 

프래그먼트 쉐이더에서는 color 변수에 결과값을 내어야합니다. 


마지막으로 main 파일을 조금 손 보도록 하죠.


// main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <vector>

#include "Core/Shader_Loader.h"

using namespace Core;

GLuint program;

void renderScene(void)
{
    glClearColor(1.0, 0.0, 0.0, 1.0);//clear red
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //use the created program
    glUseProgram(program);

    //draw 3 vertices as triangles
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glutSwapBuffers();
}

void Init()
{

    glEnable(GL_DEPTH_TEST);

    //load and compile shaders
    Core::Shader_Loader shaderLoader;
    program = shaderLoader.CreateProgram("Shaders\\Vertex_Shader.glsl",
        "Shaders\\Fragment_Shader.glsl");
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Drawing my first triangle");
    glewInit();

    Init();

    // register callbacks
    glutDisplayFunc(renderScene);
    glutMainLoop();
    glDeleteProgram(program);
    return 0;

}


수정한 main 파일에서는 CreateProgram 메서드를 호출하여 쉐이더 소스를 불러와 컴파일 하였습니다. 그리고 나서 glUseProgram 를 호출하여 렌더링 루프에서 사용할 프로그램을 지정하였고 glDrawArrays 를 호출하여 얼마나 많을 버텍스들을 사용하고 어떻게 그려낼 것인지 지정하였습니다. 마지막으로 새로 등장한 glDrawArrays 에 대해 알아보죠.


glDrawArrays(GLenum mode, GLint first, GLsizei count):

mode 는 드로잉 모드 또는 points, lines, triangles 등의 모형을 가리킵니다. first 는 버텍스 배열내에서, 그리기 시작할 버텍스의 인덱스를 나타내며 count 는 그릴 버텍스의 갯수를 나타냅니다.




후 여기까지 오다니... 힘드네요 힘들어 ㅋㅋ

다음은 이번 강의의 원문 링크와 현재까지의 프로젝트 폴더 구조입니다.


* 원문 링크

http://in2gpu.com/2014/10/20/building-blocks-vertex-pixel/

http://in2gpu.com/2014/10/29/shaders-basics/

http://in2gpu.com/2014/11/24/creating-a-triangle-in-opengl-shader/


* 프로젝트 폴더 구조




그럼 다음에 봅시다~~


아, 위 코드를 실행 했을 때 에러가 없는데도 제대로 동작하지 않는 경우가 있을 수 있습니다. 튜토리얼 작성자 말로는 쉐이더 코드가 일부 하드웨어와 맞지 않아 생기는 현상 같다고 하네요. 이번 강의는 쉐이더의 기본개념을 공부하기 위한 강의이고 실제 필드에선 저렇게 버텍스 쉐이더 안에서 버텍스를 정의하지 않으니 제대로 동작하지 않더라도 짜증내지말고 그냥 다음 강의로 넘어갑시다.ㅎㅎㅎ

Posted by nfyfamraa
,


저번시간에 이어 소스를 조금 추가해보죠!


#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>

void renderScene(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 0.0, 0.0, 1.0); //clear red

    glutSwapBuffers();
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(50, 50);
    glutInitWindowSize(800, 600);
    glutCreateWindow("OpenGL First Window");

    glewInit();
    if (glewIsSupported("GL_VERSION_3_3"))
    {
        std::cout << " GLEW Version is 3.3\n ";
    }
    else
    {
        std::cout << "GLEW 3.3 not supported\n ";
    }

    glEnable(GL_DEPTH_TEST);

    // register callbacks
    glutDisplayFunc(renderScene);

    glutMainLoop();

    return 0;
}

크게 glut 디스플레이 루틴에 장면을 렌더링하는 콜백함수를 추가하였고 glew를 초기화하는 부분과 그래픽카드의 OpenGL 지원을 체크하는 조건문을 추가하였습니다.


위에서 등장한 함수들에 대한 설명입니다.


glutInit:

GLUT 라이브러리 초기화 함수 입니다.


glutInitDisplayMode:

OpenGL 의 디스플레이 모드를 지정합니다. 이 함수는 unsigned int 형을 인자로 받으며 여러 옵션을 지정하기 위해 bit OR 연산자로 묶을 수 있습니다.


GLUT_DEPTH 는 OpenGL 이 깊이 버퍼를 사용하도록 지시합니다. 깊이버퍼는 두 개 이상의 오브젝트가 겹쳤을 때 어떤 색을 출력할지를 결정하는데 사용됩니다.

GLUT_DOUBLE 은 OpenGL 이 더블버퍼링 시스템을 사용하도록 지정합니다.

GLUT_RGBA 는 32비트 Framebuffer 를 8비트의 RGBA 채널에 할당합니다.


glutInitWindowPosition:

윈도우 창의 위치를 픽셀 단위로 지정합니다.


glutInitWindowSize:

윈도우 창의 너비와 높이를 픽셀 단위로 지정합니다.


glutCreateWindow:

윈도우를 생성합니다. 인자로 윈도우 창의 이름을 지정하며 윈도우 ID 값을 반환합니다.


glewInit:

GLEW 초기화 함수입니다.


glewIsSupported:

인자로 전달한 OpenGL 버전이 GLEW 에서 지원할 수 있는 버전인지 확인합니다. 


glutMainLoop:

GLUT processing loop를 시작합니다.


glClear:

OpenGL이 인자로 전달된 버퍼를 비우도록 합니다. 위 코드에서는 GL_COLOR_BUFFER_BIT와 GL_DEPTH_BUFFER_BIT 를 지정하였습니다.


glClearColor:

색상버퍼를 인자값으로 초기화 합니다. 인자순서는 각각 R, G, B, A 입니다.


glutSwapBuffers:

더블 버퍼링 시스템에서 백버퍼와 프론트 버퍼를 바꿉니다.



간단하네요 :)


다음은 이번 강의 원문 링크입니다.

http://in2gpu.com/2014/10/17/creating-opengl-window/



======================================================

추가합니다.


코드를 실행시켜보면 바로 빨간 배경화면이 뜨는게 아니고 검은화면이 뜰겁니다. 그리고 마우스클릭이나 창을 움직이면 빨간배경화면으로 바뀌네요. 왜그런가 검색을 해보니 함수 호출 순서가 잘못되었네요. 먼저 glClearColor로 빨간색으로 배경색을 지정해주고 glClear로 색을 칠합니다. 다음 링크를 참조해 주세요.

http://stackoverflow.com/questions/2898503/background-colour-in-opengl

수정:
   glClearColor(1.0, 0.0, 0.0, 1.0); //clear red
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


'잡것 > OpenGL' 카테고리의 다른 글

[OpenGL 02] 셰이더를 적용해 봅시다.  (0) 2016.10.07
[OpenGL 00] OpenGL을 설치해봅시다.  (0) 2016.10.02
Posted by nfyfamraa
,

시작하기에 앞서 몇가지 알려드립니다

먼저 제 환경은 윈도우7 64bit 이며, IDE는 MS VisualStudio 2015 Community v.14 를 사용합니다.

또한 필자가 매.우. 게으르므로 이 시리즈는 연재따위가 아니며 다음 링크의 

http://in2gpu.com/2014/10/17/creating-opengl-window/

OpenGL 강의를 읽어보면서 정리한 글입니다.



자 시작해 봅시다.

OpenGL 라이브러리만 가지고 윈도우에서 OpenGL을 개발할 순 있습니다만 그러기 위해선 전반적인 winAPI 의 지식이 필요합니다.

그러므로 여기에선 그래픽 로직에만 집중할 수 있도록 FreeGLUT, GLEW 를 이용하여 예제를 작성합니다.

얘네들이 뭐하는건지는 아래 갓택오버플로우에 답변이 이미 올라와 있네요

http://stackoverflow.com/questions/19719055/what-are-the-differences-between-glu-glew-glut-qt-sdl-opengl-and-webgl


요약하자면 다음과 같습니다.

OpenGL: 2D 및 3D 그래픽을 렌더링하기 위한 크로스플랫폼 API 입니다.

GLUT: OpenGL Utility Toolkit 은 windows 환경에서 마우스와 키보드 컨트롤을 도와주는 라이브러리. win API 해보신분들은 아시겠지만 윈도우폼을 만들고 I/O 이벤트를 컨트롤 하는건 복잡하고 여간 귀찮은 일이죠.

GLEW: OpenGL Extension Wrangler Library 은 OpenGL의 확장기능들을 불러오고 사용할 수 있도록 도와주는 크로스플랫폼 라이브러리 입니다.



먼저 FreeGLUT를 설치해 봅시다. 아래링크에서 최신 안정 버전을 다운로드합니다.

http://freeglut.sourceforge.net/

GLUT와 FreeGLUT 라는 용어를 혼용하고 있어서 헷갈릴 수도 있는데 구분을 하자면 GLUT가 1998년을 마지막으로 개발자가 더 이상 업데이트를 하지않는데다 라이선스도 공개용이 아니라 사용에 문제가 있어서 X-Consortium 라이선스로 GLUT와 호환성을 유지하여 만든 것이 바로 FreeGLUT 입니다. 이하의 글에서 GLUT 라고 칭하는 것들은 모두 FreeGLUT 를 의미하는 것임을 유의해 주세요.


현재 3.0.0 버전이 최신 안정 버전이네요.

음.... 다운받아보니 뭔가 정체 모를 파일들이 많이도 있네요...

받은 압축파일은 소스코드이므로 컴파일을 해야 합니다

README 파일에 친절히 설명이 다 되어있네요.

순서대로 따라해봅시다

아래 링크에서 cmake를 다운받습니다.

http://www.cmake.org/cmake/resources/software.html


링크에 들어가보면 버전에 따라 설치버전과 포터블버전을 다운 할 수 있습니다

현재 cmake 최신 버전은 3.6.2 이며 저는 포터블버전인 cmake-3.6.2-win64-x64.zip 를 다운받았습니다.

압축을 풀고.... bin/cmake-gui.exe 를 실행 시킵니다.


Where is the source code 부분에는 freeglut 소스코드를 압축해제한 폴더를 설정해줍니다.

Where to build the binaries 에는 솔루션을 생성할 폴더를 지정합니다. 폴더이름은 freeglut로 해줍시다.

그리고 나서 왼쪽 아래 부분에 Configure을 누르고 현재 자신의 컴퓨터에 설치되어있는 VS 버전을 고릅니다. 저의 경우 Visual Studio 14 2015 이며, 이는 VisualStudio 를 키시고, 도움말 > Microsoft Visual Studio 정보 창에서 확인 할 수 있습니다.

Finish 를 누르면 configure 프로세스가 진행됩니다.



저는 DEMO 와 정적파일은 필요없으니 체크 해제하고 솔루션을 생성하였습니다.

아무래도 정적파일로 생성하면 예제프로그램들 빌드할 때 무거워질거 같아서...



Generate를 누르면 지정한 output 폴더에 VS 솔루션이 생성됩니다.

자 그럼 freeglut/freeglut.sln 을 엽니다.

빌드 단축키 F7을 눌러 빌드!


에러없이 빌드가 완료되면 freeglut/bin/Debug/freeglutd.dll 파일이, freeglut/lib/Debug/freeglutd.lib 파일이 각각 생성됩니다.

이 동적라이브러리 파일과 헤더파일을 프로젝트에 포함시켜 주어야 합니다.

VS의 기본 인클루드패스에 위 파일들을 복사해 넣거나 프로젝트 설정에서 패스를 추가로 설정해 주어야 합니다.


header include 경로에 압축해제한 폴더중 include 밑에 있는 GL 폴더를 폴더 째로 

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include 밑에 복사합니다.



빌드한 freeglutd.lib 파일은 

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib 폴더 밑에 넣고, 

freeglut.dll 파일은 

C:\Windows\SysWOW64 폴더와 

C:\Windows\System32 폴더에 각각 복사해 넣습니다.

참고로 어플리케이션의 DLL 검색 순서는 다음과 같습니다

https://msdn.microsoft.com/ko-kr/library/7d83bc18.aspx


후... 다음으로 glew를 설치해보도록 하죠

아래 링크에서 최신 릴리즈 소스파일을 다운받습니다. 현재 2.0.0가 최신이군요.

http://glew.sourceforge.net/


압축을 풀어보니 이것도 뭔가 이것저것 담겨 있군요.

build/vc12 폴더에 있는 VS 솔루션을 엽니다.

솔루션 버전과 현재 사용하고 있는 VS 버전이 다른데 프로젝트를 업그레이드할 것이냐고 물어보는데 OK를 눌러서 프로젝트를 업그레이드하고 F7 키를 눌러 빌드를 합니다.

빌드가 성공하면 bin/Debug 폴더와 lib/Debug 폴더 밑에 각각 dll 파일과 lib 파일이 생성되어 있을 겁니다.

이 두 파일과 함께 include/GL 내의 헤더파일들을 적당한 곳에 넣어줍니다.


C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\GL 에 헤더파일들을

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib 에 glew32d.lib 파일을

C:\Windows\SysWOW64 폴더와

C:\Windows\System32 폴더에 glew32d.dll 파일을 넣습니다.


후... 드디어 설치가 끝났군요. 뭐 사실 파일들을 저렇게 뿔뿔이 넣지 않고 한폴더에 모아서 예제프로젝트 내에서 패스 설정만 해줘도 되지만 전 이렇게 하고 싶었어요 데헷.


자 이제 예제 프로그램을 만들어 봅시다.

VS 에서 빈프로젝트를 만들고 Alt+F7 을 눌러 프로젝트 설정을 엽니다

링커 > 입력 > 추가 종속성 을 열고

opengl32.lib

glew32d.lib

freeglutd.lib

을 넣어줍시다



확인을 누르고 다음 코드를 복붙한 후 실행시켜 봅시다.


#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>

void renderScene(void) {}

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

    glutInit(&argc, argv);
    glutInitWindowPosition(50, 50);//optional
    glutInitWindowSize(800, 600); //optional
    glutCreateWindow("OpenGL First Window");

    // register callbacks
    glutDisplayFunc(renderScene);
    glutMainLoop();

    return 0;
}


오... Nice boat...


빈창하나와 콘솔창 하나가 뜰텐데 에러가 없다면 여기까지 잘 따라오신겁니다.


지금까지 위에서 설명한 방법은 소스코드를 직접 다운받아서 컴파일하고 사용하는 방법인데 애초에 컴파일된 파일을 다운받거나 VS내의 NuGet 매니저를 사용하는 방법도 있습니다.

컴파일된 파일을 다운받아서 사용하는 것은 위에서 설명한 과정중 컴파일하는 과정만 빼고 같으니 생략하고 NuGet 매니저를 사용하는 방법을 간략하게 설명하겠습니다.

빈 프로젝트를 만드시고 프로젝트 > NuGet 패키지 관리 를 선택합니다.



nupengl.core 를 검색하고 설치합니다.

그리고 앞의 코드를 빌드 및 실행 ㅇㅅㅇ


NuGet 패키지 관리자를 사용하니까 정말 편하네요(직접 사용해보진 않았지만 말입니다 하하하하)



Posted by nfyfamraa
,