외곽선은 명암이나 색조가 확 달라지는 픽셀을 찾은 결과이다.
현재 픽셀을 기준으로 상하 좌우 텍셀을 체크하면서 외곽선인지 판단하면 된다.
이때 컨벌루션(Convolution)이란 것을 사용한다.
컨벌루션 : 블러나 샤프닝 등 효과에 쓰임,
현재 픽셀 기준, 주위 펙셀마다 가중치를 곱해서 결과를
모두 더한 값으로 현재 픽셀의 값을 변경하는 연산이다.
소벨 연산자(sobel operator)의 커널을 이용해 컨벌루션을 해 외곽선을 찾을 수 있다.
![](https://blog.kakaocdn.net/dn/bi3c3J/btroAz6Gn1Z/Kr7mkUsLVW350uFKkFi4s1/img.png)
Kx는 좌우연산, Ky는 상하연산을 한다. 결과값은 Lx, Ly이라고 하자.
현재의 픽셀이 0.9라고 가정을 하자.
![](https://blog.kakaocdn.net/dn/cEtZDq/btroDf7OldO/vOkHD2ebUKJLrhHxcMHTI0/img.png)
주변 픽셀들도 0.9로 모두 같은 모습이다.
이러면 외곽선이 생길 일이 없겠죠?
저 소벨 연산자의 커널을 곱해서 다 더한 값은 0이다.
-0.9 + 0.9 + -1.8 + 1.8 + -0.9 + 0.9 = 0
명암차가 클 수록 Lx의 값도 커진다.
또한 왼쪽이 오른쪽보다 크면 값이 작아지고 (음수)
오른쪽이 더 크다면 값이 커진다 (양수).
결국 0이 아니면 외곽선이 있다는 것.
기존 흑백 셰이더에서
이어서 해서 버텍스 셰이더는 생략하겠다.
PS 픽셀 셰이더
![](https://blog.kakaocdn.net/dn/bzO5T3/btroDgewWBW/k0NzDEnos3my1fSkAJbDBK/img.png)
앞에서 이야기한 커널 3x3 행렬을 만들고
for문을 돌리며 주변 픽셀들 마다 Kx, Ky를 곱한다.
![](https://blog.kakaocdn.net/dn/NCevi/btroAz6JU65/JNgWNKWJHGh6H2YKa3Qt4k/img.png)
픽셀 하나 하나 불러와야하는데 옆 픽셀과 현재 픽셀의 UV좌표 차이를 알아야한다.
UV좌표는 0~1이고, U는 1/텍스쳐 width, V는 1/텍스쳐 height의 차이가 난다.
이건 gPixelOffset 전역변수에
![](https://blog.kakaocdn.net/dn/IFMkM/btroDgS9xTd/mcZqAFreXjUYHDWT4jfUKK/img.png)
이렇게 해서 텍스쳐를 불러온다.
주변 픽셀들에 커널을 곱해서 가중치를 구했다면 피타고라스 정리처럼 계산해야한다.
hlsl 함수인 sqrt() 루트 연산 함수를 쓴다. (pow()는 제곱)
![](https://blog.kakaocdn.net/dn/zRhxl/btroDvoYvtt/ep7knsNRVkVziawNc6VPqk/img.png)
책에서 그냥 더해도 된다는데 빗변의 길이를 구하는 피타고라스 정리를 사용했다.
luminance는 우리가 흑백셰이더 만들었을때 그 위키피디아에서 인간 색영역에 맞춘 공식이다.
그 결과를 리턴하면 된다.
결과
![](https://blog.kakaocdn.net/dn/kQsHs/btroDgyRyYC/H4ES1akzMLHhdm4Zb7Fsk1/img.gif)
양각효과
는 위 내용과 비슷하다. 커널을 좀 수정한다.
![](https://blog.kakaocdn.net/dn/bbFKYc/btroywJgMOO/JIgPvXglKIFr3E1fxCo9U0/img.png)
![](https://blog.kakaocdn.net/dn/mIkQn/btrozjiz0FX/iakJu9ea3d71O5APR8zbe1/img.png)
컨벌루션의 결과를 누적하는 res를 리턴한다.
결과
![](https://blog.kakaocdn.net/dn/WCtMq/btroDrUscWs/oaoZPhaAFZKmkJAhqsfGrk/img.gif)
이렇게 외곽선과 엠보스 효과를 적용해 보았다.
책에서는 색상보정, HDR효과, 비네트효과, Dof효과, SSAO 효과, 잔상효과 등
여러 영상처리기법을 소개했다.
책을 보며 따라 구현하며 빛, 그림자, 영상처리기법을
완벽히는 아니여도 흐름을 이해하는 기회도 있었지만,
무엇보다 셰이더와 친해질 수 있는 기회가 되어 좋았다.
좋은 책을 써주신 김포프 교수님께 존경을 표한다.
//vs
struct VS_INPUT
{
float4 mPosition : POSITION;
float2 mUV : TEXCOORD0;
};
struct VS_OUTPUT
{
float4 mPosition : POSITION;
float2 mUV : TEXCOORD0;
};
VS_OUTPUT vs_main( VS_INPUT Input )
{
VS_OUTPUT Output;
Output.mPosition = Input.mPosition;
Output.mUV= Input.mUV;
return( Output );
}
//ps
struct PS_INPUT
{
float2 mUV : TEXCOORD0;
};
sampler2D SceneSampler;
float2 gPixelOffset;
float3x3 Kx = {-1, 0, 1,
-2, 0, 2,
-1, 0, 1};
float3x3 Ky = { 1, 2, 1,
0, 0, 0,
-1,-2,-1};
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.3f, 0.59f, 0.11f));
Lx+= luminance * Kx[y+1][x+1];
Ly+= luminance * Ky[y+1][x+1];
}
}
float L = sqrt((Lx*Lx)+(Ly*Ly));
return float4(L.xxx, 1);
}
//vs
struct VS_INPUT
{
float4 mPosition : POSITION;
float2 mUV : TEXCOORD0;
};
struct VS_OUTPUT
{
float4 mPosition : POSITION;
float2 mUV : TEXCOORD0;
};
VS_OUTPUT vs_main( VS_INPUT Input )
{
VS_OUTPUT Output;
Output.mPosition = Input.mPosition;
Output.mUV= Input.mUV;
return( Output );
}
//ps
struct PS_INPUT
{
float2 mUV : TEXCOORD0;
};
sampler2D SceneSampler;
float2 gPixelOffset;
float3x3 K = {-2,-1, 1,
-1, 0, 1,
0, 1, 2};
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.3f, 0.59f, 0.11f));
res+= luminance * K[y+1][x+1];
}
}
res+=0.1f;
return float4(res.xxx, 1);
}
'STUDY > Shader Programming' 카테고리의 다른 글
Shader Programming - 다이렉트X11 난반사광, 정반사광 적용 (0) | 2022.03.17 |
---|---|
셰이더 프로그래밍 - AMD RenderMonkey 사용해서 쉐이더 공부를 해보자9 (영상처리 기법, 포스트프로세싱 흑백 세피아) (0) | 2021.12.21 |
셰이더 프로그래밍 - AMD RenderMonkey 사용해서 쉐이더 공부를 해보자8 (그림자 매핑) (0) | 2021.12.21 |
셰이더 프로그래밍 - AMD RenderMonkey 사용해서 쉐이더 공부를 해보자7 (UV 애니메이션 Animation & 울렁효과) (0) | 2021.12.20 |
셰이더 프로그래밍 - AMD RenderMonkey 사용해서 쉐이더 공부를 해보자6 (환경 매핑, 입방체 매핑) (0) | 2021.12.20 |