본문 바로가기
Opengles 3.0 with Android

Chapter 2.1 Shader Build

by SimonLee 2025. 2. 22.

https://github.com/dlgmlals3/OpenGLES3.0_Example/blob/main/Chapter_2/app/src/main/cpp/Scene/Cube.cpp

 

OpenGLES3.0_Example/Chapter_2/app/src/main/cpp/Scene/Cube.cpp at main · dlgmlals3/OpenGLES3.0_Example

Contribute to dlgmlals3/OpenGLES3.0_Example development by creating an account on GitHub.

github.com

InitModel() 함수를 부분을 보자.

 

Original gl 셰이더 프로그램 컴파일을 하려면 아래 절차를 수행한다.

1> 셰이더 생성 및 컴파일

2> 프로그램 생성하여 셰이더를 어태치 후 링킹

3> 프로그램 사용

#include <GLES3/gl3.h>
#include <stdio.h>
#include <stdlib.h>

// -----------------------------------------------------------------------------
// 함수: loadShader
// 설명: 주어진 셰이더 타입(GL_VERTEX_SHADER 또는 GL_FRAGMENT_SHADER)과 소스 코드를
//       이용하여 셰이더 객체를 생성하고 컴파일한다.
// 반환값: 성공하면 컴파일된 셰이더 객체 ID, 실패하면 0을 반환한다.
// -----------------------------------------------------------------------------
GLuint loadShader(GLenum shaderType, const char* pSource) {
    // 1) 셰이더 객체 생성
    GLuint shader = glCreateShader(shaderType);
    if (shader == 0) {
        // 셰이더 객체 생성 실패
        return 0;
    }
    
    // 2) 셰이더 소스 코드 전달
    glShaderSource(shader, 1, &pSource, NULL);
    
    // 3) 셰이더 컴파일
    glCompileShader(shader);
    
    // 4) 컴파일 상태 확인
    GLint compiled;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if (compiled == GL_FALSE) {
        // 컴파일 실패시 오류 로그 출력
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            char* infoLog = (char*)malloc(infoLen);
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            printf("셰이더 컴파일 오류:\n%s\n", infoLog);
            free(infoLog);
        }
        glDeleteShader(shader);
        return 0;
    }
    // 셰이더 ID 전달
    return shader;
}

// -----------------------------------------------------------------------------
// 함수: createProgram
// 설명: 버텍스 셰이더와 프래그먼트 셰이더 소스 코드를 받아, 셰이더를 컴파일하고
//       프로그램 객체를 생성한 후 셰이더를 첨부하여 링킹한다.
// 반환값: 성공하면 프로그램 객체 ID, 실패하면 0을 반환한다.
// -----------------------------------------------------------------------------
GLuint createProgram(const char* vertexSource, const char* fragmentSource) {
    // 1) 버텍스 셰이더 컴파일
    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource);
    if (vertexShader == 0) {
        return 0;
    }
    
    // 2) 프래그먼트 셰이더 컴파일
    GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource);
    if (fragmentShader == 0) {
        glDeleteShader(vertexShader);
        return 0;
    }
    
    // 3) 프로그램 객체 생성
    GLuint program = glCreateProgram();
    if (program == 0) {
        return 0;
    }
    
    // 4) 셰이더 객체를 프로그램에 첨부
    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);
    
    // 5) 프로그램 링킹
    glLinkProgram(program);
    
    // 6) 링킹 상태 확인
    GLint linked;
    glGetProgramiv(program, GL_LINK_STATUS, &linked);
    if (linked == GL_FALSE) {
        // 링킹 실패시 오류 로그 출력
        GLint infoLen = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            char* infoLog = (char*)malloc(infoLen);
            glGetProgramInfoLog(program, infoLen, NULL, infoLog);
            printf("프로그램 링킹 오류:\n%s\n", infoLog);
            free(infoLog);
        }
        glDeleteProgram(program);
        return 0;
    }
    
    // 7) (Optional) 프로그램 검증: 현재 OpenGL 상태에서 실행 가능한지 확인
    glValidateProgram(program);
    GLint validated;
    glGetProgramiv(program, GL_VALIDATE_STATUS, &validated);
    if (validated == GL_FALSE) {
        printf("프로그램 검증 실패.\n");
    }
    
    // 8) (Optional) 셰이더 객체는 프로그램에 붙였으므로 더 이상 필요하지 않음
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    
    return program;
}

// -----------------------------------------------------------------------------
// 메인 함수 예제: 셰이더 프로그램을 생성하고 사용하는 방법
// -----------------------------------------------------------------------------
int main() {
    // OpenGL ES 초기화 및 컨텍스트 생성은 생략 (각 플랫폼별로 다름)
    
    // 버텍스 셰이더 소스 코드 예제
    const char* vertexShaderSource =
        "#version 300 es\n"
        "layout(location = 0) in vec4 a_Position;\n"
        "void main() {\n"
        "    gl_Position = a_Position;\n"
        "}\n";
    
    // 프래그먼트 셰이더 소스 코드 예제
    const char* fragmentShaderSource =
        "#version 300 es\n"
        "precision mediump float;\n"
        "out vec4 outColor;\n"
        "void main() {\n"
        "    outColor = vec4(1.0, 0.0, 0.0, 1.0);\n"  // 빨간색 출력
        "}\n";
    
    // 셰이더 프로그램 생성 (컴파일, 첨부, 링킹, 검증)
    GLuint program = createProgram(vertexShaderSource, fragmentShaderSource);
    if (program == 0) {
        printf("셰이더 프로그램 생성 실패.\n");
        return -1;
    }
    
    // 9) 셰이더 프로그램 사용: 렌더링 전에 반드시 프로그램을 활성화
    glUseProgram(program);
    
    // ... 이후 렌더링 관련 코드 작성 ...
    
    return 0;
}

 

1) 프로그램이 여러개라면 ?

- 프로그램을 관리하는 프로그램 매니저가 필요하다.

2) 셰이더 코드가 코드가 아니라 파일로 존재한다면, 매번 파일로딩을 하면 성능 문제

- 셰이더 코드 캐싱 필요

3) 셰이더가 여러개라면 ?

- 셰이더를 컴파일 및 관리를 할 셰이더 매니저 필요

 

예제에서 구현된 셰이더 빌드 구조

ProgramManager Program(const char*) 지정한 이름("Cube")의 프로그램이 이미 존재하는지 확인하여, 있으면 해당 프로그램 객체를 반환합니다.
  ProgramInit(const char*) 지정한 이름("Cube")의 새로운 프로그램 객체를 생성 및 초기화합니다.
  AddProgram(PROGRAM*) 생성된 프로그램 객체를 내부 리스트나 관리 구조체에 추가하여 관리합니다.
  ProgramLink(PROGRAM*, int) 첨부된 셰이더들을 기반으로 프로그램 객체를 링킹(Link)하고, 링킹 성공 여부를 반환합니다. (오류 발생시 종료)
ShaderManager ShaderInit(const char*, GLenum) 주어진 소스 참조(예: VERTEX_SHADER_PRG, FRAGMENT_SHADER_PRG)와 셰이더 타입(GL_VERTEX_SHADER, GL_FRAGMENT_SHADER)을 기반으로 셰이더 객체를 생성합니다.
  ShaderCompile(셰이더객체, const char*, int) 제공된 셰이더 소스 코드를 컴파일하며, 컴파일 성공 여부를 반환합니다. (실패 시 오류 로그 출력 후 종료)
기타 관련 함수 reserveCache(const char*, bool) 셰이더 소스 코드를 읽어오기 위한 캐시 버퍼를 할당하여 소스 코드를 임시로 저장합니다.
  freeCache(CACHE*) 사용이 끝난 캐시 버퍼를 해제하여 메모리 누수를 방지합니다.
 
1.프로그램 생성 후 프로그램 매니저에 등록
2.프로그램 객체에서 버텍스, 프래그먼트 셰이더 등록
3.셰이더 캐시 생성
4.셰이더 컴파일
5.셰이더매니저에서 프로그램 링킹
6.프로그램 사용.

 

program = ProgramManagerObj->ProgramInit( ( char * )"Cube" );
ProgramManagerObj->AddProgram( program );
program->VertexShader		= ShaderManager::ShaderInit( VERTEX_SHADER_PRG,	GL_VERTEX_SHADER	);
program->FragmentShader	= ShaderManager::ShaderInit( FRAGMENT_SHADER_PRG, GL_FRAGMENT_SHADER	);

CACHE *m = reserveCache( VERTEX_SHADER_PRG, true );
ShaderManager::ShaderCompile( program->VertexShader, ( char * )m->buffer, 1 )

m = reserveCache( FRAGMENT_SHADER_PRG, true );
ShaderManager::ShaderCompile( program->FragmentShader, ( char * )m->buffer, 1 )
ProgramManagerObj->ProgramLink( program, 1 )
glUseProgram( program->ProgramID );

return;

 

 

과제 > 아래 함수를 지우고 구현해보자.

void Cube::InitModel()