[유니티 쉐이더 스타트업] Part 15 | Cubemap Reflection

jungizz_·2023년 3월 1일
0

Unity Shader StartUp

목록 보기
15/17
post-thumbnail
✔️ 유니티 에셋스토어의 Space Robot Kyle / Free HDR sky 데이터 사용

1. Cubemap 적용

  • 리얼 타임으로 주변을 반사하는 것은 다른 연산들보다 상당히 무겁다.
  • 실시간 반사를 위해 많이 사용하는 방법은 Cubemap이라는 환경 텍스쳐를 이용해서 주변 이미지를 텍스쳐로 만든 후 이를 오브젝트에 적용하는 것이다.
  • Cubemap은 일종의 텍스쳐이며, 텍스쳐 한 장으로 사방향을 모두 표현하는 특수한 텍스쳐이기 때문에 사용 방법이 약간 다르다.
  • Lambert 라이트 기본형에 Cubemap 텍스쳐를 받는 코드를 작성한다.

☑️ Cubemap 적용 코드

  • _Cube("Cubemap", Cube) = "" {} - 2D가 아닌 Cube로 받는다.
  • samplerCUBE _Cube; - Properties에서 입력받은 텍스쳐를 SamplerCUBE 샘플러로 받는다.
  • float3 worldRefl; - UV가 아닌 반사벡터를 받는다. Cubemap은 주변을 둘러싸고 있는 3차원 공간이기 때문에 float3이며, 이것을 UV로 사용한다.
  • float4 re = texCUBE(_Cube, IN.worldRefl); - Cubemap 텍스쳐를 연산하는 texCUBE 함수로 텍스쳐의 컬러를 만든다.
  • o.Emission = re.rgb; - 반사 이미지 자체는 빛의 영향을 받지 않기 때문에 Albedo가 아닌 Emission에 넣는다.
❗ 반사하는 것에 빛을 비춘다고 빛에 영향을 받아 밝아지거나 어두워지는 것이 아닌, '밝거나 어두운 주변 이미지를 반사'하는 것 뿐이다.
  • o.Albedo에 들어간 흰색과, o.Emission에 반사 이미지가 더해져 최종 결과가 1이 한참 넘어가 하얗게 떠보이는 결과물이 나왔다.

  • 반사가 100%인 이미지를 만들기 위해 o.Albedo를 0으로 코드를 수정한다.
    (ex-반사가 100%인 물체: 거울이나 금속은 물체의 자체 색상 즉, Albedo가 보이지 않는다.)

void surf (Input IN, inout SurfaceOutput o)
{
    fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
    float4 re = texCUBE(_Cube, IN.worldRefl);
    o.Albedo = 0;
    o.Emission = re.rgb;
    o.Alpha = c.a;
}
  • 배경까지 적용한 최종 결과이다.

2. NormalMap이 적용된 Cubemap

  • 평범하게 NormalMap을 적용하면 에러가 발생한다.
Shader "Custom/CubeMapShader"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("NormalMap", 2D) = "bump"{}
        _Cube("Cubemap", Cube) = "" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM
        #pragma surface surf Lambert noambient

        sampler2D _MainTex;
        sampler2D _BumpMap; 
        samplerCUBE _Cube;

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float3 worldRefl;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            float4 re = texCUBE(_Cube, IN.worldRefl);
            o.Albedo = 0;
            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Emission = re.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

  • 위 에러는 Input에서 버텍스 월드 노멀에 관련된 데이터worldRefl와 UnpackNormal 함수를 거쳐 탄젠트 노멀이 된 데이터o.Normal을 surf함수에서 동시에 사용해서 발생했다.
    (모든 픽셀이 픽셀 자신을 중심축으로 가진 듯한 좌표계를 탄젠트 좌표계라고 한다.)
  • NormalMap을 사용하기 위해 NormalMap에 대응되는 반사 벡터가 필요하므로 픽셀 월드 노멀로 변화시켜야 한다.

☑️ 픽셀 월드 노멀로 변화

  • 버텍스로부터 받는 반사 벡터는 worldRefl가 존재해야 한다.
  • INTERNAL_DATA 키워드를 추가한다.
struct Input
{
    float2 uv_MainTex;
    float2 uv_BumpMap;
    float3 worldRefl;
    INTERNAL_DATA //세미콜론을 붙이지 않는다!
};
  • 노멀 연산을 위로 올리고, WorldReflectionVector함수로 NormalMap이 적용된 월드 좌표계의 픽셀 노멀을 뽑아 큐브맵의 UV로 사용한다.
void surf (Input IN, inout SurfaceOutput o)
{
    o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
    fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
    float4 re = texCUBE(_Cube, WorldReflectionVector(IN, o.Normal));

    o.Albedo = 0;            
    o.Emission = re.rgb;
    o.Alpha = c.a;
}
  • NormalMap이 잘 적용되었다!

📝 최종 코드 (Albedo 적용, 반사 강도 조절 속성 추가)

Shader "Custom/CubeMapShader"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("NormalMap", 2D) = "bump"{}
        _Cube("Cubemap", Cube) = "" {}
        _ReflPow("Reflection Power", Range(0, 1)) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM
        #pragma surface surf Lambert noambient

        sampler2D _MainTex;
        sampler2D _BumpMap; 
        samplerCUBE _Cube;
        float _ReflPow;

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float3 worldRefl;
            INTERNAL_DATA
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            float4 re = texCUBE(_Cube, WorldReflectionVector(IN, o.Normal));

            o.Albedo = c.rgb * (1 - _ReflPow); //이게 맞는지 모르겠움ㅠ 어려워잉
            o.Emission = re.rgb * _ReflPow;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

➕ 여러가지 노멀 상태의 비교

NormalMap이 들어간 픽셀 월드 노멀을 구할 때도 위와 동일한 방법이다.

  • UnpackNormal의 결과물 - 전부 파랑색인 탄젠트 좌표계의 노멀

  • 버텍스 월드 노멀의 결과물, 버텍스 사이 보간(Interpolation)이 보임
  • NormalMap이 적용된 월드좌표계의 픽셀 노멀의 결과물

3. MaskMap을 이용해서 반사의 영역 조절

  • Albedo맵을 참고해서 Mask맵을 만든다.
  • 위 Mask맵의 R채널 m.r만 사용해서 반사의 영역을 조절했다. 나머지 GBA는 어딘가 응용 가능할 것이다.
profile
( •̀ .̫ •́ )✧

0개의 댓글

관련 채용 정보