본문 바로가기
셰이더 (Shader)/셰이더 프로그래밍 입문 - Pope Kim (완)

[HLSL] 챕터12 - 외곽선 찾기 / 양각 효과

by Minkyu Lee 2023. 3. 21.

  • 외곽선 찾기

// 외곽선이란?
3D 공간을 2D 이미지에 그려넣으면 물체 개념이 없다.

외곽선 정확히 찾기 힘들다.

그러나 명암이나 색조가 다르면 외곽선을 인식할 수 있다.

 

명암이 확 바뀌는 픽셀을 찾아 외곽선이라 해주자.

비교할 대상이 필요해진다.

주위 픽셀들을 모두 살펴야한다.

 

살펴보는 픽셀 많아지면 느려진다.

상하 좌우 대각선 한단계만 찾아보는 것을 3x3

두단계 살펴보는 것을 5x5 텍셀을 살펴본다고 표현하겠다.

 

// 컨벌루젼 사용

3x3, 5x5 픽셀 한번에 계산하기 편하게끔 컨벌루션사용한다. (convolution)

현재 픽셀 중심으로 그 주위에 있는 픽셀마다 가중치를 곱한다.

그 결과를 모두 더한다.

이 값으로 현재 픽셀의 값을 변경하는 연산이다.

 

행렬 형태로 저장한 가중치의 집합커널이라고 한다. (kernel)

 

2D 영상처리 기법에서 컨벌루션 사용 매우 많다.

포토샵의 블러, 샤프닝도 컨벌루션이다.

 

// 소벨 연산자

외곽선 찾기에서 컨벌루션 사용해보자.

외곽선 찾기에 사용할 커널 여러가지가 있을 수 있다.

그 중 소벨 연산자 사용해보자. (sobel operator)

위키피디아 참고해보자.

 

Kx              Ky

-1  0  +1       +1 +2 +1

-2  0  +2        0    0    0

-1  0  +1       -1   -2   -1

 

Kx는 좌우 검출,

Ky는 상하 외곽선 검출한다.

 

// Lx 설명

Kx의 결과를 Lx라고 한다.

 

인접한 픽셀 값이 모두 같다면 0이 된다.

 

오른쪽이 왼쪽에 비해 명암이 크다면 양수가 된다.

명암차가 클수록 값이 더 커진다.

 

왼쪽이 오른쪽에 비해 크다면 음수가 된다.

명암차가 클수록 값이 더 작아진다.

 

Ly도 원리는 동일하다.

 

// 쉽게 정리

0에서 값이 멀어질수록,

양수 값이 클수록 음수 값이 작을수록

외곽선을 확실하게 보여준다.

 

따라서 Lx Ly의 절대값을 구해서 외곽선 보여주면 된다.

한번에 계산하려면 더해서 한다.

L = |Lx| + |Ly|

 

위처럼 해도 되지만,

소벨 연산자는 빗변의 길이를 구하는 피타고라스 정의를 사용하였다.

 

// 기초설정

챕터11의 설정과 다 동일하다.

픽셀셰이더만 바꿀 것이다.

 

픽셀셰이더

struct PS_INPUT
{
   float2 mUV: TEXCOORD0; 
};

sampler2D SceneSampler; // render target texture sampler

float3x3 Kx = {-1, 0, 1,
               -2, 0, 2,
               -1, 0, 1};
float3x3 Ky = {1, 2, 1,
               0, 0, 0,
               -1, -2, -1};

float2 gPixelOffset; // 1/texture width, 1/texture height

float4 ps_main(PS_INPUT Input) : COLOR
{
   float Lx = 0;
   float Ly = 0;
   
   for(int y = -1; y <= 1; ++y)
   {
      for(int x = -1; x <= 1; ++x)
      {
         float2 offset = float2(x, y) * gPixelOffset;
         float3 tex = tex2D(SceneSampler, Input.mUV + offset).rgb;
         float luminance = dot(tex, float3(0.3, 0.59, 0.11)); // same grayscale
         
         Lx += luminance * Kx[y+1][x+1];
         Ly += luminance * Ky[y+1][x+1];
      }
   }
   
   float L = sqrt((Lx * Ly) + (Ly * Ly)); // Pythagorean theorem.
   return float4(L.xxx, 1);
}

위에서 얘기한 Kx, Ky 커널 2개를 선언한다.

 

for루프 돌리면서 주변 픽셀마다 Kx, Ky 곱한다.

누적 시킬 변수 Lx, Ly 만든다.

현재 픽셀을 중앙에 맞추고 주위 픽셀 찾는다.

-1 ~ 1 범위로 루프 돌린다.

 

루프가 돌 때마다 픽셀 하나씩 불러온다.

바로 옆 픽셀과 현재 픽셀 차이는?

UV는 0~1이다.

U는 1/텍스쳐너비

V는 1/텍스쳐높이 만큼 차이가 난다.

 

셰이더 안에서 알 수 있는 방법은 없다.

전역 변수로 넘겨 받는다.

어차피 넘겨 받을거면

1/텍스쳐너비, 1/텍스쳐높이로 넘겨받아 나누기 연산을 피한다.

 

텍스쳐를 읽어온다.

 

명암값의 차이로 외곽선을 찾는다.

명암이라 하면 흑백이라는 말이다.

따라서 흑백과 동일 공식 사용한다.

 

여기까지 주변 픽셀들에 커널 곱해서 그 값을 더했다.

이제 Lx와 Ly를 합친다.

피타고라스 정리 이용한다.

 

명암 변화 없으면 검정색, 많으면 흰색이 된다.

 


  • 양각효과 (embossing)

올록볼록한 느낌 내는 것이다.

명암 변화가 없는 곳은 중간회색이 된다.

튀어나오는 곳은 밝은 회색이나 흰색

들어가는 곳은 어두운 회색이나 검정색이 된다.

 

// 튀어나오는 느낌이란?

왼쪽 위 하이라이트를 준다.

오른쪽 아래 그림자를 준다.

 

// 컨벌루전 사용

인접한 픽셀 살펴봐야한다.

컨벌루션 사용한다.

따라서 커널이 필요하다.

 

왼쪽 위 픽셀보다 오른쪽 아래 픽셀이 밝다면 하이라이트

왼쪽 위 픽셀이 오른쪽 아래 픽셀보다 밝다면 그림자를 준다.

 

커널은 다음과 같다.

-2 -1 0

-1  0  1

0  1  2

 

이 커널이 커버하는 픽셀들의 값이 모두 같으면 0이 된다.

이럴 때는 0.5가 되어야한다.

따라서 +0.5를 한다.

따라서 컨벌루션 결과가 0이상이면 0.5이상의 하이라이트 등장한다.

0이하면 0.5이하의 그림자가 보인다.

 

픽셀셰이더

struct PS_INPUT 
{
   float2 mUV : TEXCOORD0;
};

sampler2D SceneSampler;

float3x3 K =  { -2, -1,  0,
                -1,  0,  1,
                 0,  1,  2};

float2 gPixelOffset;
      
float4 ps_main( PS_INPUT Input ) : COLOR
{
   float res = 0;   
   for ( int y = -1; y <= 1; ++y )
   {
      for (int x = -1; x <=1 ; ++x )
      {
         float2 offset = float2(x,y) * gPixelOffset;
         float3 tex = tex2D(SceneSampler, Input.mUV+offset).rgb;
         float luminance = dot(tex, float3(0.3, 0.59, 0.11));
         
         res += luminance * K[y+1][x+1];
      }
   }
   
   res += 0.5f;

   return float4(res.xxx, 1);
}

컨벌루젼을 누적할 변수는 res이다.

루프 끝난 후 0.5를 더한다.

명암에 아무 변화가 없는 경우 중간회색이 된다.

 

  • 기타 고급 포스트이펙트 기법

// 색상보정 (color correction)

말그대로 색상보정

 

// HDR (high dynamic range)

HDR은 블룸과 톤매핑을 포함한다. 

나뭇가지 사이 태양보면 빛이 너무 강렬하게 뿜어진다. 이게 블룸이다.

톤매핑은 사람 눈처럼 어두운 곳과 밝은 곳을 모두 잘 보이게 만드는 것을 말한다.

카메라는 그렇지 않다. 밝거나 어두운 곳 하나만 제대로 보인다.

 

// 비니엣 (vignette)

가장자리 검정테두리

 

// DOF (depth of field)

초점 가까우면 멀리있는 물체 흐릿해진다. 반대도 된다.

 

// SSAQ (screen space ambient occlusion)

법선과 깊이 등을 렌더타깃에 저장한다. 이를 사용해 주변광 차폐 정도를 계산한다.

 

// 잔상효과 (motion blur)

저번 프레임, 현재 프레임 블렌딩 기법이다.

 

// 추가 참고사항

외곽선 찾기 기법은 안티에일리어싱에 사용하기도 한다.

 


  • 고급 셰이더 기법 찾기

// GPU 젬스 시리즈

온라인에 공짜

 

// 셰이더 X / GPU 프로 시리즈

매년 발매, 셰이더X 7편까지 나온 후 이름 GPU 프로로 바꿔서 나오는 중이다.

 

// 컨퍼런스

최신기술은 컨퍼런스가 최고이다.

비디오와 파워포인트가 웹에 많이 공개된다.

 

// 시그래프

매년 북미 개최

시그래프 아시아 지역별 개최

 

시그래프 연회비 5만원 내면 시그래프 온라인 도서관 엄청난 양의 자료 열람 가능

 

// 게임 개발자 컨퍼런스 (GDC)

북미 매년 한 번

게임에서 실제사용 수많은 기법 접할 수 있다.

 

// 게임페스트

마이크로소프트사가 매년 주체한다.

다른 컨퍼런스에 비해 양은 적다.

이 컨퍼런스의 자료는 마이크로소프트사 웹사이트에 잘 올라온다.

 

// 인터넷

교수님, 석박사 학위 학생들의 홈페이지.

그리고 구글신.

댓글