SSAO 를 Z버퍼와 연결하기 by 김윤정

공개 레벨로 수정후 올림.

==================================================================================


SSAO (Screen - space Ambient Occlusion) 은 후처리 효과 중 하나로, 구석진 부분에 음영을 더 추가해서 자연스러운 느낌을 나타나게 해주는 기능입니다.


각 점들이 사방으로 벡터를 쏴서 자신이 얼마나 구석진데 있는지 인식해서 검게 칠합니다.
(그냥 보면 잘 안보이기 때문에 매우 강하게 증폭했습니다)


그리고 이걸 그냥 쓰지 않고.. 다운샘플링 (메모리를 절약하기 위해 작은 사이즈로 샘플링) 하고 블러 등의 과정을 거칩니다.
매우 느리기 때문에 옵션도 좀 낮추고요.

그래서 실제 사용하게 되면 이런 모양이 됩니다.



그런데 이게 원래 렌덤한 도트를 블러시킨 것이라 조금 지글대는 점이 적지 않은데 게다가 DOF 등과 합성되면서,
특히 먼 거리에 있는 SSAO가 특히 거슬리도록 작동된단 말입니다.
 

때문에 SSAO를 조금만 강하게 쏘면 뒷 배경에 검은 아티펙트가 너무 눈에 거슬려서, 매우 약하게 사용할 수 밖에 없었던게 지금까지의 현실이었습니다.

일단. SSAO는 세 개의 파일로 동작합니다.

SSAOEffect.cs - 스크립트 파일과
SSAOShader.shader - 쉐이더 파일입니다.
frag_ao.cginc - 인클루드 파일입니다.

cs 파일에서는 각종 셋팅값을 잡아줍니다. 카메라의 모드라던가, 메터리얼을 만들어 준다던가, 그렇게 해서 RT( 렌더링 타겟)을 가능하게 해주는 것이죠 . 인터페이스도 여기서 만들어 줍니다. 그리고 RT와 씬을 연결해 주는 일도 합니다.

shader 파일에서는 여러 패스를 통해 SSAO의 연산을 해 줍니다.
인클루드 파일에서는 쉐이더에서 값을 받아 SSAO 깊이값을 계산해서 리턴해 줍니다. 가장 핵심적인 일을 하지요

어쨌건 최종 값은 shader 파일의 맨 아래, composite pass 에서 합성됩니다.

half4 frag( v2f i ) : COLOR
{
half4 c = tex2D (_MainTex, i.uv[0]);
half ao = tex2D (_SSAO, i.uv[1]).r;
ao = pow (ao, _Params.w);
c.rgb *= ao;
return c;
}

매우 간단합니다 ... 그냥 텍스쳐와 ao 값을 곱해주는 것 뿐.
그러니 여기다가 카메라 깊이값인 Z버퍼를 같이 연산해주면, 가까이 있는 곳에는 SSAO가 보이고, 멀리 있는 곳은 SSAO가 안보인다던가 하는 짓이 가능하단 말입니다.

그렇다면 일단 Z버퍼를 빼 내 오는 것이 문제...

수없이 삽질을 하다가, 힌트를 발견하였습니다.
그것은 _CameraDepthNormalsTexture.

half4 DepthNormTex = tex2D (_CameraDepthNormalsTexture, i.uv[1]);

으로 사용되는 이 기능은, SSAOEffect.cs 파일에서
camera.depthTextureMode |= DepthTextureMode.DepthNormals;
로 활성화 시켰을때 사용되는 기능입니다.

이렇게 해서 Depth와 Normal을 쉐이더에서 사용할 수 있게 됩니다. 설명은 아래와 같습니다.
한 텍스쳐 안에 노말과 깊이값이 모두 들어 있다는 말이지요.

  • DepthTextureMode.DepthNormals: depth and view space normals packed into one texture.

  • 그러므로 이것을 디코딩 시켜주면 쓸 수 있게 됩니다.
    노말은 XY 채널에 들어가고, 깊이값은 ZW 채널에 들어가므로, 디코딩 기능을 찾아보니 찾을 수 있었습니다. 즉

    float DepthMask = DecodeFloatRG (DepthNormTex.zw);

    을 쉐이더에서 사용하면 Z버퍼를 구해낼 수 있게 됩니다. DecodeFloatRG 는 UnityCG.cginc 안에 예약되어 있습니다.

    그래서 Z 버퍼를 사용하면 이렇게 ...


    그냥 까만 화면으로 보여서 잘못된 것인 줄 알고 삽질을 좀 했습니다만, 사실은 0~1 이 카메라의 far 플렌까지의 거리이기 때문에 이정도 거리의 이미지는 까맣게 보이는게 정상이었습니다.

    땅 아래로 카메라를 집어넣어서 멀리 보면 확인할 수 있었지요. 이걸 일찍 봤었으면 .. .

    어쨌거나 Z 버퍼가 제대로 동작한다는 것을 확인했으므로, 이제 거리를 줄이는 일만 남았습니다. smoothstep 명령어가 있습니다.

    DepthMask = smoothstep(0,_NoiseScale.z,DepthMask);

    을 이용해서 거리를 줄여줍시다. _NoiseScale.z 은 cs 파일에서 생성한 백터값 중 안쓰는 값을 빌려서 사용하였습니다.

    이렇게 해서 ...

    깊이값이 잘 나왔습니다. 일정 거리 이상은 하얗게 날라가는 것을 볼 수 있습니다.

    이제 이것을 , 다시



    이 이미지와 더해줍시다. 밝은 부분은 1을 넘어갈 것이므로, saturate 시켜주는 것을 잊지 맙시다.

    잘 되었습니다. 일정 거리 이상은 SSAO가 보이지 않습니다. 


    이 수치는 , 인스펙터 창의 SSAO 컴포넌트에 추가해 놓았습니다. 원하시는대로 조절해서 사용 가능하지요.



    덧글

    • 오즈라엘 2011/09/22 23:29 #

      우와 TA는 전지전능이군요
    • 김윤정 2011/09/23 00:41 #

      게다가 관대하십니다
    • 11호 2011/09/24 16:44 #

      저도 빨리 전지전능하고 관대한 TA가 되고 싶어요!!!! (최종 합성 결과물이 너무 궁금하잖아요!!ㅋ)
    • 김윤정 2011/09/24 17:11 #

      ㅎㅎㅎ 어차피 합성 결과물은 뭐.. 예상하실 수 있으시잖아요.
      공개하려면 오래 걸리겠습니다만.
    ※ 로그인 사용자만 덧글을 남길 수 있습니다.


    MyADD

    <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script>