CString을 WCHAR로 변환

아무리 MultiByteToWideChar를 사용해 변환을 해보아도 않되어서, 결국 찾은 방법입니다. 일단 변환은 유니코드 문자열 프로젝트 환경에서 수행해 확인한 것입니다. MFC 기반입니다.

CString sProgID;

    .
    .
    .

USES_CONVERSION;
WCHAR *wszProgID = T2W(sProgID.GetBuffer());

    .
    .
    .

sProgID.ReleaseBuffer();

OpenGL Shader – 9

GLSL을 위한 OpenGL 설정 – InfoLog를 통한 디버깅
원문: http://www.lighthouse3d.com/opengl/glsl/index.php?oglinfo

쉐이더 디버깅은 어렵다. 아직 printf와 같은 출력문도 없고 앞으로도 없을 것이다. 하지만 향후 개발툴에서 쉐이더 디버깅을 위한 기능을 제공할지 모르겠다. 쉐이더 코드가 옳바르게 컴파일 되고 링크 되었는지를 검사하기 위한 함수가 있다.

컴파일 단계들에 대한 결과 상태를 얻기 위한 OpenGL 2.0 형태의 함수는 다음과 같다.

void glGetShaderiv(GLuint object, GLenum type, int *param);
Parameters:
object – 쉐이더나 프로그램에 대한 객체 핸들
type – GL_COMPILE_STATUS
param – 반환값으로써 성공이면 GL_TRUE를, 실패면 GL_FALSE

링크 단계에 대한 결과 상태를 얻기 위한 OpenGL 2.0 형태의 함수는 다음과 같다.

void glGetProgramiv(GLuint object, GLenum type, int *param);
Parameters:
object – 쉐이더나 프로그램에 대한 객체 핸들
type – GL_LINK_STATUS
param – 반환값으로써 성공이면 GL_TRUE이고 실패면 GL_FALSE

위의 OpenGL 2.0 형태의 두개의 함수에 대한 ARB 확장 함수는 하나로 대신할 수 있다.

void glGetObjectParameterivARB(GLhandleARB object, GLenum type, int *param);
Parameters:
object – 쉐이더나 프로그램에 대한 핸들
type – GL_OBJECT_LINK_STATUS_ARB 또는 GL_OBJECT_COMPILE_STATUS_ARB
param – 반환값으로써 성공이면 1, 실패면 0

위에서 언급한 함수들의 두번째 인자(type)에 대해 더 많은 옵션이 있지만, 자세한 설명은 여기서 하지 않겠다. 대신 http://developer.3dlabs.com/openGL2/index.htm를 참고하길 바란다.

에러가 발생할 경우 InfoLog를 사용해서 에러에 대한 더 많은 정보를 얻어올 수 있다. 이 로그는 수행한 마지막 연산에 대한 정보를 담고 있는데, 그 내용은 컴파일에 대한 경고나 에러 또는 링크 단계에서에서 발생한 문제들이다. 로그는 소프트웨어에서 실행될 쉐이더에 대해 더 많은 정보를 제공한다. 예를들어서 사용하고자 하는 기능이 하드웨어에서 지원하지 않는다든지, 아무런 문제가 없다든지와 같은 정보이다. 불행이도 InfoLog 메세지에 대한 스펙은 없다. 그래서 각 벤더마다 그 메세지의 내용이 다르다.

특정한 쉐이더나 프로그램에 대한 InfoLog를 얻기 위한 OpenGL 2.0 형태의 함수는 다음과 같다.

void glGetShaderInfoLog(GLuint object, int maxLen, int *len, char *log);
void glGetProgramInfoLog(GLuint object, int maxLen, int *len, char *log);
Parameters:
object – 쉐이더나 프로그램에 대한 객체 핸들
maxLen – InfoLog로부터 받을수 있는 문자의 최대 개수
len – 실제 받을 문자의 개수
log – 실제 로그 메세지 문자열

위의 함수에 대한 ARB 확장 형태는 다음과 같이 하나의 함수로 대신할 수 있다.

void glGetInfoLogARB(GLhandleARB object, int maxLen, int *len, char *log);
Parameters:
object – 쉐이더나 프로그램 객체에 대한 핸들
maxLen – InfoLog로부터 받을 수 있는 문자열의 최대 길이
len – 실제 받을 문자열의 길이
log – 실제 로그 메세지 문자열

InfoLog를 통해 얻을 정보의 정확한 길이를 알아야할 필요가 있는데, 얻는 방법은 아래의 함수와 같다. (OpenGL 2.0 형태)

void glGetShaderiv(GLuint object, GLenum type, int *param);
void glGetProgramiv(GLuint object, GLenum type, int *param);
Parameter:
object – 쉐이더나 프로그램 객체에 대한 핸들
type – GL_INFO_LOG_LENGTH
param – 반환값으로써 InfoLog의 길이

위의 함수에 대한 ARB 확장은 아래와 같다.

void glGetObjectParameterivARB(GLhandleARB object, GLenum type, int *param);
Parameters:
object – 쉐이더나 프로그램에 대한 핸들
type – GL_OBJECT_INFO_LOG_LENGTH_ARB
param – 반환값으로써 InfoLog의 길이

다음 함수는 OpenGL 2.0에서 InfoLog의 내용을 출력하는 예이다.

void printShaderInfoLog(GLuint obj)
{
  int infologLength = 0;
  int charsWritten  = 0;
  char *infoLog;

  glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &infologLength);

  if (infologLength > 0)
  {
       infoLog = (char *)malloc(infologLength);
       glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
       printf("%s\n",infoLog);
       free(infoLog);
  }
}

void printProgramInfoLog(GLuint obj)
{
  int infologLength = 0;
  int charsWritten  = 0;
  char *infoLog;

  glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

  if (infologLength > 0)
  {
       infoLog = (char *)malloc(infologLength);
       glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
       printf("%s\n",infoLog);
       free(infoLog);
  }
}

ARB 확장 형태는 아래와 같다.

void printInfoLog(GLhandleARB obj)
{
    int infologLength = 0;
    int charsWritten = 0;
    char *infoLog;

    glGetObjectParameterivARB(obj, 
        GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength);

    if(infologLength > 0)
    {
        info = (char *)malloc(infologLength);
        glGetInfoLogARB(obj,infologLength, &charsWritten, infoLog);
        printf("%s\n", infoLog);
        free(infoLog);
    }
}

OpenGL Shader – 8

GLSL을 위한 OpenGL 설정 – 예제
원본 : http://www.lighthouse3d.com/opengl/glsl/index.php?oglexample1

다음 코드는 이전 섹션에서 설명했던 모든 단계를 포함하고 있다. p, f, v는 OpenGL 2.0 문법으로는 GLuint 타입이고 ARB 확장 문법으로는 GLhandleARB 타입인 전역변수이다.

OpenGL 2.0 문법은 다음과 같다.

void setShaders() {
    char *vs,*fs;
	
    v = glCreateShader(GL_VERTEX_SHADER);
    f = glCreateShader(GL_FRAGMENT_SHADER);	
	
    vs = textFileRead("toon.vert");
    fs = textFileRead("toon.frag");
	
    const char * vv = vs;
    const char * ff = fs;
	
    glShaderSource(v, 1, &vv, NULL);
    glShaderSource(f, 1, &ff, NULL);
	
    free(vs);
    free(fs);
	
    glCompileShader(v);
    glCompileShader(f);
	
    p = glCreateProgram();
		
    glAttachShader(p,v);
    glAttachShader(p,f);
	
    glLinkProgram(p);
    glUseProgram(p);
}

ARB 확장 문법은 다음과 같다.

void setShaders() {
    char *vs,*fs;
	
    v = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
    f = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);	
	
    vs = textFileRead("toon.vert");
    fs = textFileRead("toon.frag");
	
    const char * vv = vs;
    const char * ff = fs;
	
    glShaderSourceARB(v, 1, &vv,NULL);
    glShaderSourceARB(f, 1, &ff,NULL);
	
    free(vs);
    free(fs);
	
    glCompileShaderARB(v);
    glCompileShaderARB(f);
	
    p = glCreateProgramObjectARB();
		
    glAttachObjectARB(p,v);
    glAttachObjectARB(p,f);
	
    glLinkProgramARB(p);
    glUseProgramObjectARB(p);
}

OpenGL 2.0의 형태와 ARB 확장 형태에 대한 GLUT를 사용한 완전한 예제는 다음을 통해 다운로드하길 바란다.

위의 코드는 2개의 간단한 쉐이더 코드와 text 파일을 읽는 함수가 포함되어져 있다. 참고로 위의 코드를 실행하면 그 결과는 다음과 같다.

네, 툰 쉐이딩이로군요~ 약간 어슬픈 느낌이긴 하지만 말입니다.

OpenGL Shader – 7

GLSL을 위한 OpenGL 설정 – 프로그램 만들기
원문 : http://www.lighthouse3d.com/opengl/glsl/index.php?oglprogram

다음 그림은 쉐이더 프로그램을 준비하고 실행해가는데 필요한 함수 호출 단계이다.

첫번째 단계(glCreateProgram)는 프로그램(쉐이더에 대한) 컨테이너를 위한 객체를 생성이다. 이 함수는 컨테이너에 대한 핸들을 반환한다. OpenGL 2.0 문법은 아래와 같다.

GLuint glCreateProgram(void);

ARB 확장 형태의 문법은 아래와 같다.

GLhandleARB glCreateProgramObjectARB(void);

원하는 만큼의 프로그램 컨테이너를 생성할 수 있다. 렌더링 시에 원하는 쉐이더 프로그램으로 번갈아가며 교체할 수 있고, OpenGL에서 기본적으로 제공하는 고정기능(Fixed Functionality)으로 돌아갈 수도 있다. 예를들어서, 반사 쉐이더를 이용해 주전자를 그리고, 동시에 직육면체 큐브는 OpenGL의 원래 기능인, 고정기능으로 그릴 수 있다.

다음 단계는 이전 섹션에서 생성한 쉐이더를 프로그램 컨테이너에 붙이는 glAttachShader이다. 쉐이더는 이 시점에서 컴파일될 필요가 없고, 이 단계까지는 쉐이더 소스 코드를 가지지 않아도 된다. 쉐이더를 프로그램에 붙어기 위해 필요한 것은 오로지 쉐이더 컨테이너이다.

쉐이더를 프로그램에 붙이기 위한 OpenGL 2.0 함수의 형태는 다음과 같다.

void glAttachShader(GLuint program, GLuint shader);
Parameters:
  Program – 프로그램 컨테이너 핸들
  shader – 프로그램 컨테이너에 붙이고자 하는 쉐이더의 핸들

ARB 확장에 대한 함수 형태는 다음과 같다.

void glAttachObjectARB(GLhandleARB program, GLhandleARB shader);
Parameters:
  Program – 프로그램 컨테이너 핸들
  shader –  프로그램 컨테이너에 붙이고자 하는 쉐이더의 핸들

만약, 프로그램에 버텍스와 프레그먼트 쉐이더 두개를 동시에 붙이고자 할때가 있다. 같은 프로그램에 붙이고자 하는 같은 종류의 많은 쉐이더를 가질 수 있는데, 이것은 마치 C언어로 작성된 프로그램이 많음 모듈을 가질 수 있는 것과 같은 경우이다. C언에서와 마찬가지로 각각의 쉐이더에 대해서 딱 하나의 main 함수를 가질 수 있다.

하나의 쉐이더를 여러개의 프로그램에 붙일 수 있는데, 예를들어서 동일한 버텍스 쉐이더를 여러개의 프로그램에서 사용하는 것이다.

마지막 단계는 프로그램을 링크하는 것이다. 이 단계를 수행하기 위해서 세이더는 이전 섹션에서 설명했던 것처럼 반드시 컴파일 되어져 있어야 한다. 이렇게 컴파일 되어져 있는 쉐이더를 링크해주는 OpenGL 2.0 함수는 다음과 같다.

void glLinkProgram(GLuint program);
Parameters:
  program – 프로그램 컨테이너의 핸들

위의 함수를 ARB 확장 형태로 살펴보면 다음과 같다.

void glLinkProgramARB(GLhandleARB program);
Parameters:
  program – 프로그램 컨테이너의 핸들

링크가 된 후에, 쉐이더의 소스는 수정될 수 있고 프로그램에 영향을 미치지 않고 다시 컴파일 되어 진다.

위의 그림에서 처럼, 프로그램이 링크되어지면 이제 실제로 프로그램은 사용할 수 있게되는데, 이것은 ARB 확장함수인 glUseProgramObjectARB나 OpenGL 2.0인 glUseProgram을 이용한다. 각 프로그램은 핸들에 할당되어져 있고, 하드웨어가 허락하는 한, 우리가 사용하길 원하는 만큼 프로그램을 링크해 놓고 사용할 수 있다.

실제 컴파일되어진 쉐이더 프로그램을 사용하는 OpenGL 2.0 형태의 함수는 다음과 같다.

void glUseProgram(GLuint prog)
Parameters:
  prog – 사용하길 원하는 프로그램의 핸들이며 고정기능으로 복귀할 경우 0

ARB 확장의 형태는 다음과 같다.

void glUseProgramObjectARB(GLhandleARB prog);
Parameters:
  prog – 사용하길 원하는 프로그램의 핸들이며 고정기능으로 복귀할 경우 0

만약 프로그램이 사용중이고, 다시 링크되어진다면, 자동으로 수정된 내용이 사용되도록 설정되므로, 이 함수를 다시 호출할 필요가 없다. 그리고 OpenGL의 고정기능으로 복귀할 경우에 prog 인자에 0(Zero)을 넣고 호출하면 된다.

OpenGL Shader – 6

OpenGL Setup for GLSL- Shader 생성하기
원문 : http://www.lighthouse3d.com/opengl/glsl/index.php?oglshader

다음 그림은 쉐이더를 생성하는데 필요한 단계를 보이고 있다.
첫번째 단계(glCreateShader)는 쉐이더 컨테이너로써 수행하는 오브젝트를 생성하는 것이다. 이 함수는 컨테이너의 헨들을 반환한다.

이 함수에 대한 OpenGL 2.0 문법은 다음과 같다:

GLuint glCreateShader(GLenum shaderType);
Parameter:
  shaderType – GL_VERTEX_SHADER  또는  GL_FRAGMENT_SHADER

ARB 확장문법은 다음과 같다.

GLhandleARB glCreateShaderObjectARB(GLenum shaderType);
Parameter:
  shaderType – GL_VERTEX_ARB 또는 GL_FRAGMENT_SHADER_ARB

프로그램에 추가하고자 하는 만큼의 쉐이더를 생성할 수 있지만, remember that there can only be a main function for the set of vertex shaders and one main function for the set of fragment shaders in each single program.

다음 단계(glShaderSource)는 특정한 소스 코드를 추가하는 것이다. 소스 코드는 문자 배열이다.

이 함수에 대한 OpenGL 2.0에 대한 형태는 다음과 같다.

void glShaderSource(GLuint shader, int numOfString. const char** strings, int *lenOfStrings);
Parameters:
  shader – 쉐이더의 핸들
  numOfStrings – 문자 배열의 구성 요소 수
  strings – 문자 배열
  lenOfStrings – 각 문자열의 길이를 가지는 배열 또는 NUL값(문자열들이 NULL로 끝남)

다음은 이 함수에 대한 ARB 확장이다.

void glShaderSourceARB(GLhandleARB shader, int numOfStrings, const char **strings, int *lenOfStrings);
Parameters:
  OpenGL 2.0 형태의 인자 설명과 동일함

최종적으로, 쉐이더는 반드시 컴파일 되어져야 한다. 세번째 단계(glCompileShader)가 쉐이더 코드를 컴파일해주며 OpenGL 2.0에서의 형태는 다음과 같다.

void glCompileShader(GLuint shader);
Parameters:
  shader – 쉐이더의 핸들

다음으로 ARB 확장에 대한 형태는 다음과 같다.

void glCompileShaderARB(GLhandleARB shader);
Parameters:
  shader – 쉐이더의 핸들