본문 바로가기
공허의 유산/표현의 자유

[Unity3D/Shader] 06. 텍스쳐를 이용한 멀티 텍스쳐링 영역 설정과 물성 표현(Normal map, Metallic, Smoothness)

by 바른생활머시마 2023. 2. 23.
728x90
반응형

앞에서 불규칙적인 무늬가 있는 텍스쳐의 어떤 픽셀값을 다른 텍스쳐의 좌표로 활용하는 방법으로 일렁이는 불꽃을 만드는 방법을 보았습니다.

https://learn-and-give.tistory.com/76

 

[Unity3D/Shader] 05. 텍스쳐를 이용하여 불꽃 효과 내기

앞에서 텍스쳐링에 대한 맛보기를 해보았습니다. [Unity3D/Shader] 04. 텍스쳐 맛보기 (tistory.com) [Unity3D/Shader] 04. 텍스쳐 맛보기 앞에서 출력 구조체에 대해서 조금 알아보았습니다. https://learn-and-give.t

learn-and-give.tistory.com

이처럼, 텍스쳐는 이미지로써 사용되는 것은 일부이고, 픽셀 단위로 연산 해야 할 표현 방법에서는 그 표현에 사용 될 인자를 압축적으로 담고 있는 데이터로써 가치나 활용도가 매우 높습니다. 이번에는 여러 종류의 텍스쳐를 하나의 물체에 입힐 때 여러 개의 텍스쳐를 지정 된 영역에 자유롭게 반영하는 방법을 볼 것입니다.

 

 

Vertex Color

참고 서적에서는 Vertex color를 이용하여 각 Vertex에 사용 될 텍스쳐를 지정하여 이를 자연스럽게 섞는 방법으로 설명하고 있습니다. 샘플로 제공되는 FBX는 Vertex에 색상이 적용되어 있습니다. 일단 어떤 모양인지 한번 살펴 보겠습니다.

 

Vertex의 색상은 IN 구조체에서 COLOR라는 이름으로 값을 받아 올 수 있습니다.

쉐이더 코드를 아래와 같이 작성합니다.

Shader "Custom/MultiTexturing"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        //Tags { "RenderType"="Transparent" "Queue"="Transparent"}
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard noambient
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
            float4  color:COLOR;
        };

        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            //o.Albedo = lerp(c.rgb, d.rgb, 0.8);
            o.Emission = IN.color.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

쉐이더를 적용하면 그 전에는 보이지 않던 Vertex의 색상이 나타나게 됩니다.

당연한 이야기지만 여기에 텍스쳐 색상을 섞어주는 것은 너무 간단하죠.

일단 앞으로 사용 할 여러 개의 텍스쳐를 프로젝트에 추가 합니다.

하나의 텍스쳐만 추가해서 Vertex 색상가 섞어보겠습니다.

알베도를 텍스쳐와 Vertex color의 곱으로 설정하겠습니다.

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb*IN.color.rgb;
            o.Alpha = c.a;
        }

결과는 아래와 같이, 예상한 모양으로 나옵니다.

그런데, Vertex Color가 적용 된 데이터를 구하기 어렵다면 그냥 텍스쳐를 써도 무방 할 것 같습니다.

요런 텍스쳐를 만들어서 섞어도 되죠.

장단점이 있을 것 같네요.

이미지가 Vertex Color보다는 데이터양이 많을 가능성이 높을 것 같고, 대신 훨씬 보편적으로 많이 쓰이는 형식이기 때문에 편집이 쉽고 구하기도 쉽겠죠.  뭐 그때 그때 상황에 맞게~~~

 

 

Vertex Color를 이용한 멀티 텍스쳐링

 

Vertex Color는 일종의 코드로 활용한다면, 이 코드에 해당하는 텍스쳐를 그 위치에 입힐 수 있습니다. 그렇게 하면 색상에따라 다른 텍스쳐를 적용 할 수 있게 됩니다. 여기서 중요한 점은 코드에 따라 색상을 할당하는 로직을 어떻게 구현할 것인지가 중요합니다. Vertex Color의 각 색상별 채널의 강도를 텍스쳐가 섞일 비율로 결정하고, 그 과정을 채널별로 반복해서 계산합니다. 결국 채널별로 이미지 전체를 다시 계산하는, 다소 낭비같은 연산을 하게 되네요. 일단 개념적인 공부를 하니까 이런 찜찜함은 잠시 잊기로 하고~

 

쉐이더 코드는 요렇게 되겠네요. 추가 된 텍스쳐를 처리하기 위해 코드는 길어졌지만 로직은 이전과 거의 같습니다. 원래 멀티 텍스쳐링을 하면 각 텍스쳐별로 텍스쳐 좌표를 다르게 할당 할 수 있지만, 여기서는 모두 같은 텍스쳐 좌표를 쓰기 때문에 그냥 하나의 텍스쳐 좌표만 사용해도 되겠습니다.

Shader "Custom/MultiTexturing"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _MainTex2 ("Albedo (RGB)", 2D) = "white" {}
        _MainTex3 ("Albedo (RGB)", 2D) = "white" {}
        _MainTex4 ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        //Tags { "RenderType"="Transparent" "Queue"="Transparent"}
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard noambient
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _MainTex2;
        sampler2D _MainTex3;
        sampler2D _MainTex4;

        struct Input
        {
            float2 uv_MainTex;
            float4  color:COLOR;
        };

        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            fixed4 d = tex2D (_MainTex2, IN.uv_MainTex);
            fixed4 e = tex2D (_MainTex3, IN.uv_MainTex);
            fixed4 f = tex2D (_MainTex4, IN.uv_MainTex);
            
            o.Albedo = lerp(c.rgb, d.rgb, IN.color.r);
            o.Albedo = lerp(o.Albedo, e.rgb, IN.color.g);
            o.Albedo = lerp(o.Albedo, f.rgb, IN.color.b);


            //o.Emission = IN.color.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

요기에 서로 다른 텍스쳐 이미지들을 할당 해 줍니다.

결과는~~~

오래 되어 망가진 신전의 정원의 일부인 것 같네요. 분수대가 망가지고 그 옆에 잡초들이 피어나고...

여기에 노멀맵이 적용되면 더욱 입체적이 될 것 같고, 재질 특성이 적용되면 더 멋지겠네요.

반응형

 

 

널리 사용되는 텍스쳐 기반의 처리

Normal map은 Bump mapping이라는 말로도 쓰이는데, 픽셀 단위로 조명을 계산하여 더욱 정교한 입체감을 나타냅니다. 기본적인 원리는 Vertex normal을 보간한 normal 값을 쓰는 것이 아니라, 픽셀별로 normal 정보를 사용하는 것인데, 픽셀별로 법선 정보가 3가지 값이 있으니 형식상 이미지와 동일합니다. 그렇기 때문에 이미지로 데이터를 보관하기 쉽죠. 

그 외 여러가지 텍스쳐 기반의 처리를 할 수 있는데, 이 데이터들을 세트로 적용해야 더 완성도 높은 결과물을 얻을 수 있습니다. 인터넷을 검색 해 보면 어렵지 않게 찾을 수 있는데요, 잠시 검색 해 보니 아래 사이트가 보이네요.

 

http://emmacharnley.blogspot.com/2014/01/

 

Emma Charnley Games Art

Final Year Project- Storytelling in Games: An Environment Envisioned in UDK. For this project I will create the inside of Radagast's house (Brown wizard in The Hobbit) from a piece of unused concept art. My aim is to have a complete scene flythough with se

emmacharnley.blogspot.com

여기서 벽돌로 만들어진 벽의 이미지 텍스쳐와 법선 이미지를 다운받아서 써보겠습니다.

이미지 텍스쳐는 원래 설정되어 있던 첫번째 텍스쳐에 사용해주도록 하고, Normal map은 하나 더 추가하겠습니다.

그리고, 입체감을 좀 더 잘 나태낼 수 있도록 물성과 관련 된 속성도 함께 추가합니다.

Shader "Custom/MultiTexturing"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _MainTex2 ("Albedo (RGB)", 2D) = "white" {}
        _MainTex3 ("Albedo (RGB)", 2D) = "white" {}
        _MainTex4 ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("Normalmap", 2D) = "bump" {}
        _Metallic("Metallic", Range(0,1)) = 0
        _Smoothness("Smoothness", Range(0,1)) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        //Tags { "RenderType"="Transparent" "Queue"="Transparent"}
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard 
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _MainTex2;
        sampler2D _MainTex3;
        sampler2D _MainTex4;
        sampler2D _BumpMap;
        float _Metallic;
        float _Smoothness;

        struct Input
        {
            float2 uv_MainTex;
            float4  color:COLOR;
        };

        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            fixed4 d = tex2D (_MainTex2, IN.uv_MainTex);
            fixed4 e = tex2D (_MainTex3, IN.uv_MainTex);
            fixed4 f = tex2D (_MainTex4, IN.uv_MainTex);
            
            o.Albedo = lerp(c.rgb, d.rgb, IN.color.r);
            o.Albedo = lerp(o.Albedo, e.rgb, IN.color.g);
            o.Albedo = lerp(o.Albedo, f.rgb, IN.color.b);

            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));

            o.Metallic = _Metallic;
            o.Smoothness = (IN.color.b*0.5) * _Smoothness + 0.3;

            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

Normal map은 워낙 대표적이고 널리 쓰이는 것이라 알아서 해주는 것들도 많네요.

결과는~~~

...

위 이미지만 보자면 뭐 별로 감흥이 오지 않습니다. Normal map의 효과를 보려면 적용하기 전과 비교하거나 조명의 변화에 따라 어떻게 보이는지 확인하는 것이 가장 효과적인 방법입니다. 조명의 방향을 바꾸면 어떻게 되는지 한번 보겠습니다.

완전 입체감이 느껴지죠.

 

여기서 한가지 중요한 사실.

Normal map 입력을 받기는 했지만 이를 이용해서 아무것도 하지 않았습니다. 

그냥 o.Normal이라는 값에 Normal map에서 읽은 값을 넣어주었을 뿐입니다.

이것은, 이렇게 전달 된 값을 이용해서, 앞에서 이야기 한 것처럼, 조명 효과를 적용 할 때 Vertex normal 대신 이 값을 사용하여 계산하는 부분이 뒤에 있다는 뜻입니다. Metallic이나 Smoothness 역시 마찬가지 입니다. 

 

 출력 데이터 구조체의 형식은 사용하는 쉐이더에 따라 달랐는데 이는 이런 연산을 어떤 것을 포함하고 있느냐의 차이가 있습니다. 기본적으로 사용하는 쉐이더는 꽤 고급 연산까지 담고 있습니다. 

 그리고 물성에 따른 효과의 경우, 파라미터를 조정하면서 맞추는 것도 느낌에 따라서 하는 것이라 쉽지 않을 수 있습니다. 이를 위해 Unity에서는 관련 된 참고 자료도 제공하고 있습니다.

 

사실 엄청 많은 내용을 담고 있는 부분인데, 일단 요정도 맛만 보고 지나가겠습니다. 

다음엔 조명에 대해서 다시 살펴보도록 하겠습니다.

 

- 참고 자료들 - 

 

https://docs.unity3d.com/Manual/StandardShaderMaterialParameterSmoothness.html

 

Unity - Manual: Smoothness

Metallic mode: Metallic Parameter Normal map (Bump mapping) Smoothness The smoothness parameter, shown in both Metallic & Specular shader modes. The concept of Smoothness applies to both the Specular workflow and the Metallic workflow, and works in very mu

docs.unity3d.com

 

Smoothness

 

 

https://docs.unity3d.com/Manual/StandardShaderMaterialParameterMetallic.html

 

Unity - Manual: Metallic mode: Metallic Parameter

Specular mode: Specular parameter Metallic mode: Metallic Parameter When working in the Metallic workflow (as opposed to the Specular workflow), the reflectivity and light response of the surface are modified by the Metallic level and the Smoothness level.

docs.unity3d.com

Metallic

https://blog.unity.com/technology/working-with-physically-based-shading-a-practical-approach

이 사이트에도 참고 할 만한 좋은 이미지들이 가득하네요.

 

Working with Physically-Based Shading: a Practical Approach | Unity Blog

In order to ensure that your texturing and shader configuration is behaving appropriately, we recommend that you use a simple scene with a variety of lighting setups. This could mean differing skyboxes, lights etc - anything that contributes to illuminatin

blog.unity.com

 

 

Working with Physically-Based Shading: a Practical Approach | Unity Blog

In order to ensure that your texturing and shader configuration is behaving appropriately, we recommend that you use a simple scene with a variety of lighting setups. This could mean differing skyboxes, lights etc - anything that contributes to illuminatin

blog.unity.com

 

 

 

Working with Physically-Based Shading: a Practical Approach | Unity Blog

In order to ensure that your texturing and shader configuration is behaving appropriately, we recommend that you use a simple scene with a variety of lighting setups. This could mean differing skyboxes, lights etc - anything that contributes to illuminatin

blog.unity.com

 

 

 

 

728x90
반응형

댓글