본문 바로가기
Opengles 3.0 with Android

9_4 엠보싱 효과

by SimonLee 2024. 12. 29.

 

우측이 엠보싱 효과

void EdgeDetection::Render()
{
    SetUpPerspectiveProjection(); // 3차원 원근 효과 적용
    // 원숭이 3d obj 렌더링을 한다.
    // FBO에 렌더링이 되고, 퐁라이팅이 적용된다.
    RenderObj(); 
    
    // FBO를 2차원 평면에 렌더링 하기 위한 ortho Projection 적용
    SetUpOrthoProjection(); 
    // 엠보싱 효과 적용
    EdgeDetect();
}

void EdgeDetection::RenderObj()
{
    // 1. 현재 바인딩된 기본 프레임버퍼(Default Framebuffer)의 ID를 가져옵니다.
    //    이는 나중에 프레임버퍼를 기본 상태로 복원하기 위해 필요합니다.
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &DefaultFBO);

    // 2. 사용자 정의 프레임버퍼 객체(FBO)를 바인딩합니다.
    //    이후의 렌더링 결과가 이 프레임버퍼로 출력됩니다.
    glBindFramebuffer(GL_FRAMEBUFFER, FboId);

    // 3. 화면(Viewport)의 크기를 프레임버퍼 크기(TEXTURE_WIDTH, TEXTURE_HEIGHT)로 설정합니다.
    glViewport(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT);

    // 4. 프레임버퍼를 초기화합니다.
    //    배경색을 검정색(0.0f, 0.0f, 0.0f)으로 설정하고, 이를 클리어합니다.
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 5. 프레임버퍼의 컬러 첨부(Attachment)로 텍스처를 설정합니다.
    //    렌더링된 컬러 데이터를 `textureId`에 저장하도록 지정합니다.
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);

    // 6. 프레임버퍼의 깊이 첨부(Attachment)로 텍스처를 설정합니다.
    //    렌더링된 깊이 데이터를 `depthTextureId`에 저장하도록 지정합니다.
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTextureId, 0);

    // 7. 프레임버퍼를 다시 초기화하여 이전의 렌더링 잔여물을 제거합니다.
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 8. 모델 데이터를 렌더링합니다.
    //    여기서 렌더링된 결과는 `textureId`(컬러)와 `depthTextureId`(깊이)에 저장됩니다.
    objModel->Render();

    // 9. 렌더링이 끝난 후 기본 프레임버퍼로 바인딩을 복원합니다.
    //    이후의 렌더링은 기본 프레임버퍼(화면)에 출력됩니다.
    glBindFramebuffer(GL_FRAMEBUFFER, DefaultFBO);
}

 

 

엠보싱 프래그먼트 코드

#version 300 es
precision mediump float;

// 텍스처 좌표 (버텍스 셰이더에서 전달된 값)
in vec2 TexCoord;

// 유니폼 변수
uniform vec2 pixelSize;          // 한 픽셀의 크기 (텍스처 좌표 기준)
uniform sampler2D Tex1;          // 입력 텍스처 샘플러
uniform float ScreenCoordinateX; // 화면 좌표 기준선 (사용 안 함, dlgmlals3 참고)

// 출력 색상
layout(location = 0) out vec4 outColor;

void main()
{
    // 임시로 기준선을 하드코딩 (ScreenCoordinateX 유니폼 대신 사용)
    float ScreenCoordX = 550.0f;

    // 엠보싱 밝기를 조정하는 값 (기본 밝기에 더해줌)
    float EmbossBrightness = 0.7f;

    // 빨간색 선을 그려 기준선을 시각화 (±3 픽셀 두께)
    if(gl_FragCoord.x < ScreenCoordX + 3.0 && gl_FragCoord.x > ScreenCoordX - 3.0) {
        outColor = vec4(1.0, 0.0, 0.0, 1.0); // 빨간색
        return; // 빨간색을 그린 후 더 이상의 처리는 필요 없음
    }

    // 기준선 오른쪽 영역: 엠보싱 효과 적용
    if (gl_FragCoord.x > ScreenCoordX) {
        // 현재 텍셀 (기준점) 샘플링
        vec3 p00 = texture(Tex1, TexCoord).rgb;

        // 수직 방향 (아래쪽 텍셀) 샘플링
        vec3 p01 = texture(Tex1, TexCoord + vec2(0.0, pixelSize.y)).rgb;

        // 수평 방향 (오른쪽 텍셀) 샘플링
        vec3 p02 = texture(Tex1, TexCoord + vec2(pixelSize.x, 0.0)).rgb;

        // 수직 방향 텍셀 차이
        vec3 diffY = p00 - p01;

        // 수평 방향 텍셀 차이
        vec3 diffX = p00 - p02;

        // RGB 채널 중 가장 큰 절대값 (수직 방향 차이)
        float max = diffY.r;
        if(abs(diffY.g) > abs(max)) { max = diffY.g; }
        if(abs(diffY.b) > abs(max)) { max = diffY.b; }

        // RGB 채널 중 가장 큰 절대값 (수평 방향 차이)
        if(abs(diffX.r) > abs(max)) { max = diffX.r; }
        if(abs(diffX.g) > abs(max)) { max = diffX.g; }
        if(abs(diffX.b) > abs(max)) { max = diffX.b; }

        // 밝기 값 계산
        float gray = clamp(max + EmbossBrightness, 0.0, 1.0); // 결과를 [0.0, 1.0]로 제한

        // 흑백으로 변환하여 출력
        outColor = vec4(gray, gray, gray, 1.0);
    }
    // 기준선 왼쪽 영역: 원본 텍스처 출력
    else {
        outColor = texture(Tex1, TexCoord); // 원본 텍스처 색상
    }

    return; // 셰이더 종료
}

 

주변색상과 픽셀값이 차이가 날수록 max 값은 커지게 되고, gray 값이 점점 밝아지게 된다.

즉 경계 부분이 밝아지게 되고, 빛에 닿는 부분 처럼 느껴져서 엠보싱 효과처럼 보인다.