개발/OpenGL / / 2022. 4. 4. 22:20

OpenGL 공부일지 - OpenGL Super Bible 프리미티브 프로세싱 - 2

반응형

Tessellation Using Triangles

triangle로 테셀레이션 모드가 설정되면, 테셀레이션 엔진은 더 작은 삼각형으로 쪼갠다.

gl_TessLevelInner[]는 첫 번째 요소인 수형 방향 레벨만을 사용하고, 테셀레이션된 삼각형의 내부영역에 전체적으로 이 값이 적용된다.

gl_TessLevelOuter[]의 경우 첫 세 요소가 삼각형의 세 가장자리에 대한 테셀레이션 인자를 설정하기 위해 사용된다. 

 

테셀레이션 엔진이 테셀레이션된 삼각형에 대응하는 버텍스 생성 시, 각 버텍스가 중심좌표계라 불리는 3차원 좌표로 정의된다. 이 세 요소를 세 입력에 대한 가중치 합의 형태로 사용해서 선형 보간값을 얻는다. 

 

#include <sb6.h>

class tessellatedtri_app : public sb6::application
{
    void init()
    {
        static const char title[] = "OpenGL SuperBible - Tessellated Triangle";

        sb6::application::init();

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

    virtual void startup()
    {
        static const char * vs_source[] =
        {
            "#version 410 core                                                 \n"
            "                                                                  \n"
            "void main(void)                                                   \n"
            "{                                                                 \n"
            "    const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0),  \n"
            "                                   vec4(-0.25, -0.25, 0.5, 1.0),  \n"
            "                                   vec4( 0.25,  0.25, 0.5, 1.0)); \n"
            "                                                                  \n"
            "    gl_Position = vertices[gl_VertexID];                          \n"
            "}                                                                 \n"
        };

        static const char * tcs_source[] =
        {
            "#version 410 core                                                                 \n"
            "                                                                                  \n"
            "layout (vertices = 3) out;                                                        \n"
            "                                                                                  \n"
            "void main(void)                                                                   \n"
            "{                                                                                 \n"
            "    if (gl_InvocationID == 0)                                                     \n"
            "    {                                                                             \n"
            "        gl_TessLevelInner[0] = 5.0;                                               \n"
            "        gl_TessLevelOuter[0] = 5.0;                                               \n"
            "        gl_TessLevelOuter[1] = 5.0;                                               \n"
            "        gl_TessLevelOuter[2] = 5.0;                                               \n"
            "    }                                                                             \n"
            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;     \n"
            "}                                                                                 \n"
        };

        static const char * tes_source[] =
        {
            "#version 410 core                                                                 \n"
            "                                                                                  \n"
            "layout (triangles, equal_spacing, cw) in;                                         \n"
            "                                                                                  \n"
            "void main(void)                                                                   \n"
            "{                                                                                 \n"
            "    gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) +                       \n"
            "                  (gl_TessCoord.y * gl_in[1].gl_Position) +                       \n"
            "                  (gl_TessCoord.z * gl_in[2].gl_Position);                        \n"
            "}                                                                                 \n"
        };

        static const char * fs_source[] =
        {
            "#version 410 core                                                 \n"
            "                                                                  \n"
            "out vec4 color;                                                   \n"
            "                                                                  \n"
            "void main(void)                                                   \n"
            "{                                                                 \n"
            "    color = vec4(0.0, 0.8, 1.0, 1.0);                             \n"
            "}                                                                 \n"
        };

        program = glCreateProgram();
        GLuint vs = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vs, 1, vs_source, NULL);
        glCompileShader(vs);

        GLuint tcs = glCreateShader(GL_TESS_CONTROL_SHADER);
        glShaderSource(tcs, 1, tcs_source, NULL);
        glCompileShader(tcs);

        GLuint tes = glCreateShader(GL_TESS_EVALUATION_SHADER);
        glShaderSource(tes, 1, tes_source, NULL);
        glCompileShader(tes);

        GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fs, 1, fs_source, NULL);
        glCompileShader(fs);

        glAttachShader(program, vs);
        glAttachShader(program, tcs);
        glAttachShader(program, tes);
        glAttachShader(program, fs);

        glLinkProgram(program);

        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);

        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }

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

        glUseProgram(program);
        glDrawArrays(GL_PATCHES, 0, 3);
    }

    virtual void shutdown()
    {
        glDeleteVertexArrays(1, &vao);
        glDeleteProgram(program);
    }

private:
    GLuint          program;
    GLuint          vao;
};

DECLARE_MAIN(tessellatedtri_app)

github 소스코드에서 해당 예제는 tessellatedtri project이다. 

 

#version 450 core

layout (vertices = 3) out;

void main(void)
{
    if (gl_InvocationID == 0)
    {
        gl_TessLevelInner[0] = 5.0;
        gl_TessLevelOuter[0] = 8.0;
        gl_TessLevelOuter[1] = 8.0;
        gl_TessLevelOuter[2] = 8.0;
    }
    gl_out[gl_InvocationID].gl_Position =
    gl_in[gl_InvocationID].gl_Position;
}

위 코드는 테셀레이션 컨트롤 쉐이더 코드이다. 

#version 450 core

layout (triangles) in;

void main(void)
{
    gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) +
    (gl_TessCoord.y * gl_in[1].gl_Position) +
    (gl_TessCoord.z * gl_in[2].gl_Position);
}

위 코드는 테셀레이션 이벨류에이션 쉐이더 코드이다. 

테셀레이션 엔지에 의해 형성도니 각 버텍스에 대한 위치 생성을 위해 입력 버텍스들의 가중치 합을 계산한다. 가장 외부의 테셀레이션 된 삼각형의 세 버텍스에 대한 상대적인 가중치를 나타내는 gl_TessCoord 3요소를 모두 사용한다.

Tessellation Using Isolines

isolines의 경우 삼각형 생성 없이 테셀레이션 영역의 동일한 v좌표의 선을 따라 라인 프리미티브를 생성하는 테셀레이션 엔진 모드이다.

gl_TessLevelOuter[] 의 첫 두 요소에 저장되는 두 개의 외부 테셀레이션 인자는 각각 선의 개수와 선당 조각의 개수를 나타낸다. gl_TessLevelInner는 사용되지 않는다.

#version 450 core

layout (vertices = 4) out;

void main(void)
{
    if (gl_InvocationID == 0)
    {
        gl_TessLevelOuter[0] = 5.0;
        gl_TessLevelOuter[1] = 5.0;
    }
    gl_out[gl_InvocationID].gl_Position =
    gl_in[gl_InvocationID].gl_Position;
}

위 코드는 테셀레이션 컨트롤 쉐이더 코드이다. 

#version 450 core
layout (isolines) in;
void main(void)
{
    // Interpolate along bottom edge using the x component of the
    // tessellation coordinate
    vec4 p1 = mix(gl_in[0].gl_Position,
        gl_in[1].gl_Position,
        gl_TessCoord.x);
        
    // Interpolate along top edge using the x component of the
    // tessellation coordinate
    vec4 p2 = mix(gl_in[2].gl_Position,
        gl_in[3].gl_Position,
        gl_TessCoord.x);
        
    // Now interpolate those two results using the y component
    // of the tessellation coordinate
    gl_Position = mix(p1, p2, gl_TessCoord.y);
}

위 코드는 테셀레이션 이벨류에이션 쉐이더 코드이다.

위 등차선의 형태에서 조금 수정해본다.

#version 450 core

layout (isolines) in;

void main(void)
{
    float r = (gl_TessCoord.y + gl_TessCoord.x / gl_TessLevelOuter[0]);
    float t = gl_TessCoord.x * 2.0 * 3.14159;
    gl_Position = vec4(sin(t) * r, cos(t) * r, 0.5, 1.0);
}

위 코드는 기존 테셀레이션 이벨류에이션 쉐이더 코드를 수정한 것이다. 여기서는 입력 테셀레이션 좌표를 극형식으로 변경하고, 회전의 형태로 만든다.

 

Tessellation Point Mode

테셀레이션 된 패치를 삼각형이나 선으로 렌더링할 수 있는 것 뿐 아니라 생성된 버텍스들을 개별 점으로 렌더링할 수도 있다. 이를 point mode라고 한다. 다른 테셀레이션 모드처럼 point_mode 입력 레이아웃 지시어로 활성화한다.

 point_mode는 quads, triangles, isolines와 같은 다른 레이아웃 지시어와 함께 지정해야한다. 

layout (triangles, point_mode) in;

위 코드처럼 point mode를 레이아웃 지시어로 설정한다.

 

렌더링한 사진을 보면 오른쪽의 경우 왼쪽과 달리 point만 렌더링되었다. (그래서 안보이는 걸지도..)

Tessellation Subdivision Modes

테셀레이션 엔진이 삼각형이나 사각형 프리미티브를 생성하고, 테셀레이션 컨트롤 쉐이더로 내부 및 외부 테셀레이션 인자를 사용해서 가장자리를 여러 부분으로 분할한다. 그 후 생성한 버텍스들을 점, 선, 삼각형 등으로 그룹화하여 다음 처리 단계로 보낸다. 여기서 가장자리를 분할하는 작업을 제어하는 옵션도 있다.

 

layout (equal_spacing) in;

위 옵션은 기본적으로 테셀레이션 엔진은 각 가장자리를 여러 동일한 크기 조각으로 분할하고, 그 개수를 인자로 지정한다. 이러한 것을 equal_spacing모드라고 하는데 사실상 default 모드이다. 

layout (fractional_even_spacing) in;
// or
layout (fractional_odd_spacing) in;

위 코드의 옵션들은 동일 간격 모드라고 한다. 테셀레이션 인자르 ㄹ설정하면 간단히 패치 프리미티브를 각 가장자리르 따라 지정한 개수로 분할하는 역할을 한다. 

위 세 그림은 왼쪽 부터 equal_spacing, fractional_even_spacing, fractional_odd_spacing 모드로 테셀레이션 된 삼각형이다.

fractional_even_spacing의 경우 4개의 동일한 크기 조각이 있고, 더 작은 두 개의 추가 조각이 있다. 

fractional_odd_spacing의 경우 5개의 동일한 크기의 조각이 있고, 매우 작은 두 개의 추가 조각이 나머지를 구성한다.

 

유니폼의 값을 조절하는 것으로 테셀레이션 컨트롤 쉐이더에서 계산해서 테셀레이션 레벨을 애니메이션 하면 테셀레이션 된 조각의 길이가 부드럽게 실시간으로 변하게 할 수도 있다.

 

Controlling the Winding Order

winding order 즉 감기순서는 애플리케이션이 버텍스들을 opengl에 보내는 순서에 의해 결정된다.

그런데 테셀레이션이 활성화되면, opengl은 모든 버텍스와 연결 정보를 생성해서, 프리미티브의 감기 순서를 결정하기 위해 버텍스가 시계 방향으로 생성되는지 반시계 방향으로 생성될지 지정할 수도 있다.

layout (cw) in;

위 코드를 통해 시계 방향으로 감기 순서를 설정할 수 있다.

layout (ccw) in;

위 코드를 통해 프리미티브 감기 순서를 반시계 방향으로 설정할 수 있다.

감기 순서는 삼각형에서만 적용되므로 애플리케이션이 등치선이나 점 생성에는 감기 순서가 무시된다.

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유