윤곽선 검출

3X3 컨벌루션( convolution ) 필터를 이용해 외곽선을 검출해 본다.

윤곽선( Edge)을 검출하기 위해서는 소벨( Sobel) 연산자를 사용한다.
윤곽선 검출에 사용되는 마스크는 다음과 같다.

좌우 외곽선 검출

상하 외곽선 검출

-1

0

1

-2

0

2

-1

0

1

1

0

1

0

0

0

-1

-2

-1

좌우 외곽선 검출 설명 : 중앙을 중심으로 3 X 3 픽셀의 모든값이 동일하면 결과 값은 0이다.
      왼쪽 픽셀이 크면 음수값이 되고, 오른쪽 픽셀이 크면 양수값이 된다.
      결과 명암값을 lx라 하자.

상하 외곽선 검출 설명 좌우 외곽선 검출과 비슷한 원리이다.
       결과 명암값을 ly라 하자.

lx와 ly의 기울기 크기를 구한다.       

프로그램을 간결하게 할려면 다음과 같이 한다.    L = |lx| + |ly|

패스 설정

ColorConversion.rfx 파일에 윤곽선 검출 패스인 Edge 패스를 추가한다.

Sepia 패스에서 오른쪽 버튼을 눌러 Copy를 선택한다.
ColorConversion 패스에서 오른쪽 버튼을 눌러 Paste를 선택한다.
패스 이름은 Edge로 한다.

Sepia 패스는 디즈블 시킨다.

전역 변수 추가

전역변수 gPixelOffset을 추가한다.

float2 gPixelOffset  

렌더몽키에서 [Add Variable - Float - Float2]를 추가한다.
이름을 gPixelOffset으로 바꾼다.
변수 시멘틱을 ViewportDimensionsInverse로 설정한다.

시멘틱 설명 : 렌더몽키에서 지원하는 시멘틱이다. 엔진에서 변수값을 직접 설정해 주어야 하낟.

ViewportDimensions : 렌더몽키 Preview의 width와 height를 나타낸다.
ViewportDimensionsInverse : 렌더몽키 Preview의 1/width와 1/height를 나타낸다.

 

픽셀 쉐이더

 

struct PS_INPUT
{
   float2 uv : TEXCOORD0;
};

float3x3 convX = { -1, 0, 1,
                   -2, 0, 2,
                   -1, 0, 1};
                     
float3x3 convY = { 1, 2, 1,
                   0, 0, 0,
                  -1, -2, -1};                     
   
float2 gPixelOffset;
   
sampler2D SceneSampler;

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.uv + offset).rgb;
         float luminace = dot(tex, float3( 0.3, 0.59, 0.11));
      
         lx += luminace * convX[y+1][x+1];
         ly += luminace * convY[y+1][x+1];
      }
   }
   
   float l = sqrt(( lx*lx) + (ly*ly));
   return float4( l.xxx, 1);
}

마스크를 3 X 3 행렬로 선언한다.

float3x3 convX = { -1, 0, 1,
                   -2, 0, 2,
                   -1, 0, 1};
                     
float3x3 convY = { 1, 2, 1,
                   0, 0, 0,
                  -1, -2, -1};

현재 픽셀을 중앙에 두고 3 X 3 행렬을 -1에서 1까지 반복한다.

for(int y = -1; y <= 1; ++y)
{
   for(int x = -1; x <= 1; ++x)
   {

x, y에 따라 픽셀의 오프셋을 구한다.
UV좌표는 0 ~ 1 범위이다. 한 개의 픽셀의 크기는 픽셀의 1/픽셀의 폭과  1/픽셀의 높이로 구할 수 있다.
gPixelOffset은 한픽셀의 크기이다.
텍스쳐로 값을 읽어 온다.

float2 offset = float2(x, y) * gPixelOffset;
float3 tex = tex2D( SceneSampler, input.uv + offset).rgb;

명암의 차이로 윤곽선을 찾는다. 칼라를 흑백으로 변환 시킨다.

float luminace = dot(tex, float3( 0.3, 0.59, 0.11));

소벨 연산자의 마스크 값과 명암을 더한다.

lx += luminace * convX[y+1][x+1];
ly += luminace * convY[y+1][x+1];

기울기를 구한다.

float l = sqrt(( lx*lx) + (ly*ly));

명암의 변화가 없는곳은 검은색, 있는곳은 하얀색을 반환한다.
칼라값이 동일해야 흑백으로 나오기 때문에 xxx를 반환한다.

return float4( l.xxx, 1);

다운로드 : ColorConversion_edge.rfx      fieldstone_SM.tga

참고)
http://harmony.cs.pusan.ac.kr/lecture/ipcv/imageprocessing.htm