본문 바로가기
튜터리얼_스터디

(HLSL)아티스트를 위한 URP 셰이더 Shader #12 - Lit Shader 사용하기

by 대마왕J 2021. 12. 9.

안내 
여기 나오는 내용은 유니티 쉐이더 스타트업에 나오는 예제를 최신 URP에 맞추어 예제를 번역한 내용입니다. 
때문에 이론적 내용이 상당히 간략하거나 불친절하며, 예제에 대한 설명도 축약되어 있기 때문에
책과 같이 보시는 것을 추천하는 바입니다.

에 .. 원래 여긴 건너뛸까 했습니다.

왜냐면 이게 ... 셰이더 그래프 뿐만이 아니라 HLSL 내장 코드도 신버전에서 꽤 바뀌어 버려서 팍 짜증이 나서 말이죠 ㅋㅋㅋ 아오 빡쳐 .
...하지만 뭐 생각해 보면 여기서 설명하는 정도로는...  그렇게 막 바뀐 것도 아녜요. 
그치만 문제는 그게 아니긴 하지...음.. 왠지는 보시다 보면 알아요 .. 이게 쉬운게 아니거든.. 어떻게 할까... 고민 많이 했는데요 

 

...에라 모르겠다  최신버전인 2021.2.3f1으로 올려서 진행해 버리자

하하하하하하 어때요 뭐 블로그 글인데. 책도 아니잖아요? 중간에 버전업 할 수도 있는거지. 그리고 중간에 실패할 수도 있는거지?!?

 

그래서 책에 216페이지부터 있는 내용을 , 전부 다는 아니더라도 대충 따라서 진행해 보겠습니다. 

...만 사실은 유니티가 SRP 구조로 바뀐 다음부터  그것마저도 그렇게 간단하진 않습니다. 이건 나중에 다시 얘기하죠 

 

Lit 셰이더를 간단하게 사용해 봅시다 

레거시에서는  Standard Shader 라 불렸죠. 일명 PBR 셰이더. 
URP에서는 이걸 Lit 셰이더라 부릅니다. 
그리고 PBR이 아닌 블린퐁 계열의 단순한 버전은 Simple Lit 이라 부르고 말이죠. 

그리고 이 내용은 책은 이미 본 분이 본다는 내용으로 쓰는 것이기 때문에, 통합해서 한 번에 해치우죠!
이론 설명은 곱게접어 하늘위로 날리니까!!

일단 에셋이 필요하겠네요 
PBR 에셋중 공짜인 것으로, 그럴듯 하게 생긴 녀석을 받아봅시다. 
저는 아래 녀석을 받았습니다. 

흠.. 이걸 받아서 씬을 열어보니까 .. .다 깨져 있네요? 몰 ? 루 상태인걸 ??

잘됐다 이건 하늘의 계시다. 공부하기 딱좋은 예제다 
이건 된다 너무 좋다 감사합니다. 유니티 신이시여 

좋아요 이걸로 함 해보죠 . 이거 분명 URP 버전이 아니라 레거시였을 거예요 이런거 많지 ...
에셋 스토에서 뙇 샀더니 레거시인 버전 말이죠. 그리고 받으면 막 저렇게 분홍분홍이고.. 

 

글고 이게이게 보니까 금속과 비금속이 잘 섞여 있는 듯. 
좋아요 만들어 봅시다. 

일단 메터리얼은 하나군요. 하나에 금속과 비금속이 다 들어있는 아주 좋은 구조예요

순서는 스틸, 마블, 브론즈. 
마블만 비금속이고, 받침은 금속과 비금속의 혼재. 

좋아요. 이번엔 셰이더 그래프가 아닌 코드로 진행해 볼께요 

 

Lit 셰이더는 이거예요. Universal Render Pipeline / Lit 

네 뭐 이걸 그냥 쓰면 돼요 
그럼 BaseMap 빼고 나머지는 다 알아서 들어올텐데요 (저것만 안 들어오는건 유니티 실수임) 그거 넣는건 일도 아니니까.. 

사실 이렇게 해서 해결하면 돼요. 레거시로 만들어진것도 이렇게 교체하면 끝. 

셰이더를 Universal Render Pipeline / Lit  으로 바꿔주고, 빠진 알베도 텍스쳐만 잘 넣어주면 뭐 더 할게 없습니다. 

 

Metallic 이 뭐고 Occlusion이 뭐고 등등의 이론은 책에 설명해 놨으니까 여기서는 당연하게도 그냥 넘어갈께요. 

근데 이건 뭐.. 보시면 알겠지만 유니티에서 만들어 준거니까 우리가 뜯거나 이럴 수가 없잖아요? 코드가 보이는 것도 아니니 ...
그러니 사실 우리가 이걸 뜯어서 어떻게 하고 싶으면, 셰이더그래프를 사용하면 됩니다.
이전 시간에 했던 것처럼 Lit 셰이더 그래프를 쓰면 다 돼요. 

이거요 이거

근데 또 Lit  셰이더 그래프도 한계가 있어요. 안에 내부까지 완전 구조를 다 뜯을 수는 없다는 것. 뭔가 섬세하게 라이팅 구조를 바꾼다던가, 깊은 부분까지 수정하고 싶다던가.. 
그것까지 하려면 일이 아주 커집니다. 

 

 

자 그럼 다시 얘기하자면 URP가 되면서 셰이더를 쓰는 방법은 이렇게 3가지 방법으로 늘어났습니다.
...뭐 근본적으로 3개의 방법은 같은거지만 말이죠.  사실 여기서 HLSL로 짤 수 있으면 나머지는 그냥 덤이라고 보시면 돼요. 그래서 다들 HLSL을 공부해야 한다.. 뭐 그러는 거예요 . 안쉬워서 그렇지.. 

셰이더 이름 특징 장점 단점
 Universal Render Pipeline / Lit 셰이더  내장된 유니티 셰이더  PBR 기능으로 잘 구현되어 있어서 쓰기만 하면 된다  기능을 추가하거나 할 수 없다. 주어진대로만 써야 함 
Shader Graph / URP / Lit Shader Graph  내장된 셰이더 그래프 PBR 기능으로 구현되어 있고, 필요한 기능이나 연산을 수정해서 넣을 수 있다  노드 셰이더의 한계가 있어서 깊이 뜯기는 힘들다 
HLSL HLSL 코드로 새로 작성 PBR 기능 뿐만 아니라, 원하는걸 모두 만들어 넣을 수 있다.  전문적인 셰이더 코딩을 알아야 하기 때문에 초보자들에게는 힘들다 

 

 

 

 HLSL  맛보기를 해 봅시다.

사실 HLSL로 된  내장 Lit 셰이더는 그렇게 간단한 셰이더가 아닙니다. 서로 물려 있는것도 많고 공식도 간단하지 않은 편이예요. 
거기다가 인터페이스가 GUI 코드로 되어 있어서 , 그냥 아티스트 수준으로 수정하기는 더더욱 쉽지 않습니다. 
그러니 그냥 셰이더그래프로 하는게 사실 맞습니다. "아티스트 레벨에서는요." 

하지만 우리가 또 HLSL을 배우기로 했잖아요? 와 어쩌지? 상당히 곤란한데?

전혀 곤란하지 않은 표정

사실 이것때문에 고민 많이 했는데, 다시 말하지만 정말 내장된 Lit 셰이더를 뜯는건 엄청 큰 일이예요. 그러니 오늘은 HLSL 맛보기로, Lit 셰이더를 살짝 건드려 보는걸로 끝내기로 하겠습니다. 즉 '있는걸 불러와 보는' 정도로 실습해 보려는 거예요. 아, 물론 원하시면 맘대로 뜯어도 되긴 해요 ㅎ 엔진에 문제 생기지 않으니까 가지고 노는 것 부터 해 봅시다. 

가지고 놀다가 망칠 수 있는 단계(?) 까지는 가르쳐 드릴께 . 츄라이 츄라이
아니 사실은 하다가 모르겠다! 하고 던지셔도 돼요. 오늘만 좀 어려운거 함 건드려 보죠. 

오늘 이 느낌일거라니깐. 사실 보스중 최약체지만 크킄

 

내장된 Lit Shader 복사해오기 

옛날(레거시 때) 에는 홈페이지에서 내장 셰이더 코드를 다운로드 받아와야 했었습니다. 근데 URP 부터는 Project 안에 이미 셰이더 코드가 들어 있어요. 그걸 그대로 복사해서 만들면, 기존 셰이더의 복제본을 만들 수 있습니다. 

그럼 일단 간단히 해 보도록 하죠. 

자 셰이더가 어딨냐면.. 여깄어요. 

Packeges 폴더에서, Universal RP 폴더를 찾고 그 하위의 Shaders 폴더를 찾으세요. 
그러면  Lit 으로 시작하는 셰이더 덩어리들을 볼 수 있습니다. 

놀랍게도 저게 전부 Lit셰이더 하나예요

그리고 더 놀랍게도 Lit 셰이더가 저게 다가 아니예요 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 저게 그 고구마줄기처럼 엮여서 막 이러케 저러케 얽히고 섥히고 있음 ㅋㅋㅋ 

.. 뭐 그치만 오늘은 저거 다 쓸게 아니라 가지고 노는 정도만 할 거이므로, 
이렇게 하세요 

Lit 셰이더를 오늘클릭해서 익스플로러에서 보기를 선택하고, 

폴더에서 3가지 코드를 찾습니다. 
Lit.shader
LitForwardPass.hlsl
LitInput.hlsl
이 3개만 해도 대충 가지고 놀만 하니까요

(흠 이래놓고 쓰다보면 '에이 뭘 가지고 놀아요 여기까지 하다 맙시다' 이럴수도 있고.. ㅋㅋㅋ 와 정말 생각없이 쓴다. 

이제 요 3개를 복사하고 

다시 이걸 Assets 폴더 어딘가에 복사해 놓습니다. 
저는 Shaders 폴더를 만들고 그 안에 넣었어요. 

즉 Packages 에서 셰이더 찾아다가 Assets 폴더에 넣는게 핵심. 

이렇게 하면 일단 복사가 되는 겁니다. 전부 다 하진 않았지만 이것만 해도 일단 작동돼요

 

 

Lit 셰이더 뜯어보기 

그럼 일단 이름부터 바꿔보죠. 
복사한 Lit 셰이더를 더블클릭해서 열어보고, 이름을 Lit Study 같은걸로 바꿔보고 저장했습니다. 

그러면 오예 

Universal Render Pipeline / Lit 옆에 Lit Study라는 셰이더가 생기고, 그 셰이더를 선택해 보면 정말로 작동합니다!

자 그럼 일단 Lit 셰이더로 되어 있던 오브젝트를 지금 만든 Lit Study 셰이더로 바꿔보세요!  (꼭 해야 함)



Lit  Study 셰이더 장난하기 

에 그러면.. 이왕 열었으니까 장난질을 좀 해보죠. 
뭘할까...
 '알베도 텍스쳐 두 장을 받아서 서로 add 해보는 Lit셰이더' 를 만들어 볼까요? 간단하니까 . 

아마 그래요 이정도만 하면 되겠네요 음음. 이것만 해도 막 셰이더 다루는 것 같은데다가 이전에 배웠던 걸 복습하는 기회도 되니가. 

자 그럼 지금 만든 Lit Study 셰이더를 더블클릭해도 좋고, 
아래처럼 셰이더 부분을 오른클릭해서 에디트로 열어도 좋아요. 
어쨌건 복사한 셰이더를 열어요. 

 

Lit 셰이더는 수정하면 안돼요. 그건 내장 셰이더라서, 수정을 한다 하더라도 바로 다시 원본으로 덮어씌워집니다. 
그래서 반드시 우리가 복사해 놓은 셰이더를 수정해야 해요. 

 

인터페이스 추가 

우선 텍스쳐 한 장 더 받기로 했으니, 텍스쳐 한 장을 더 추가해 보죠. 

Properties가 좀 복잡하게 생기긴 해도, 알아보긴 어렵지 않을 겁니다. 

 [MainTexture] _BaseMap("Albedo", 2D) = "white" {}

가 상단에 있어서 쉽게 찾을 수 있으니 그 아래 하나 추가하는건 일도 아니죠 

 [MainTexture] _BaseMap("Albedo", 2D) = "white" {}
  _BaseMap2("Albedo2", 2D) = "white" {}

이런 식으로 하나 추가한 후 , 저장해 봅시다. 

 

...?????? 

텍스쳐를 하나 추가했는데 변하는게 없죠?

..어?

그게 당연합니다. 
지금 이 내장 셰이더들은 ShaderGUI 라고 하는 C#코드로 인터페이스가 만들어져 있어요. 그 파일은 따로 존재합니다. 
그래서 그 인터페이스 코드까지 바꿔줘야 진짜로 인터페이스가 생기는 겁니다. 지금은 이쁘게 가면을 쓰고 있는거랄까요? 이게 좀 어렵죠 허허허 야 웃지마

뭐야 그건

그러니 지금은 그 인터페이스 코드랑 연결을 잠시 끊고 본 모습을 보도록 합시다. 
이걸 끊는 법은 간단합니다. 

 

셰이더 코드 맨 아래에 
CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.LitShader"

라고 되어 있는 부분을 찾아 그 줄을 // 주석처리하면 돼요 

FallBack "Hidden/Universal Render Pipeline/FallbackError"
//CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.LitShader"

이렇게 하면 커스텀 에디터와 연결이 끊어지면서 본래의 우리가 알던 셰이더 인터페이스가 나오게 됩니다. 
그럼 저장하고 가서 볼까요?

오메 익숙한 인터페이스 화면과 함께 텍스쳐가 한 장 더 추가되었습니다. 

대충 이런걸 넣어보죠. 물론 지금 변화되는건 없겠죠. 인터페이스만 만든거니까. 
이제 본격적으로 시작해 보죠 

 

텍스쳐 연산해서 더해보기 

 

그럼 이제 본격적인 맛보기로, 이 텍스쳐를 받아서 연산해보도록 합시다. 어떻게 구성이 되어 있는지.. 

일단, 셰이더에서  Name "ForwardLit" 이라고 되어 있는 Pass를 찾아보세요. 바로 있죠?
아래처럼 생겼을거예요. 

Pass
        {
            // Lightmode matches the ShaderPassName set in UniversalRenderPipeline.cs. SRPDefaultUnlit and passes with
            // no LightMode tag are also rendered by Universal Render Pipeline
            Name "ForwardLit"
            Tags{"LightMode" = "UniversalForward"}

            Blend[_SrcBlend][_DstBlend]
            ZWrite[_ZWrite]
            Cull[_Cull]

            HLSLPROGRAM
            #pragma exclude_renderers gles gles3 glcore
            #pragma target 4.5

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local _NORMALMAP
            #pragma shader_feature_local _PARALLAXMAP
            #pragma shader_feature_local _RECEIVE_SHADOWS_OFF
            #pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
            #pragma shader_feature_local_fragment _SURFACE_TYPE_TRANSPARENT
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON
            #pragma shader_feature_local_fragment _EMISSION
            #pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature_local_fragment _OCCLUSIONMAP
            #pragma shader_feature_local_fragment _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature_local_fragment _ENVIRONMENTREFLECTIONS_OFF
            #pragma shader_feature_local_fragment _SPECULAR_SETUP

            // -------------------------------------
            // Universal Pipeline keywords
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
            #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
            #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
            #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING
            #pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION
            #pragma multi_compile_fragment _ _SHADOWS_SOFT
            #pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION
            #pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
            #pragma multi_compile_fragment _ _LIGHT_LAYERS
            #pragma multi_compile_fragment _ _LIGHT_COOKIES
            #pragma multi_compile _ _CLUSTERED_RENDERING

            // -------------------------------------
            // Unity defined keywords
            #pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
            #pragma multi_compile _ SHADOWS_SHADOWMASK
            #pragma multi_compile _ DIRLIGHTMAP_COMBINED
            #pragma multi_compile _ LIGHTMAP_ON
            #pragma multi_compile _ DYNAMICLIGHTMAP_ON
            #pragma multi_compile_fog
            #pragma multi_compile_fragment _ DEBUG_DISPLAY

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma instancing_options renderinglayer
            #pragma multi_compile _ DOTS_INSTANCING_ON

            #pragma vertex LitPassVertex
            #pragma fragment LitPassFragment

            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"
            ENDHLSL
        }

보시면 제대로 된 코드는 하나도 없고 전부 무슨 설정들만 있는데요, 사실 설정만인거 맞음 ㅇㅇ 

네, 내장 셰이더 코드는 이렇게 설정과 인클루드로 되어 있습니다. 
보시면 맨 아래쪽 두 줄이 핵심이예요 

#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"

저 두 hlsl코드에 제대로 된 셰이더 코드가 다 들어 있다.. 뭐 그런 뜻인데요 
저기 씌여진 경로는 유니티 내장 코드이고, 우리는 이 코드들을 복사해서, 셰이더와 같은 폴더에 넣어 놓았으므로 
이 놈을 찾아주면 됩니다. 

그러므로 이걸 이렇게 고쳐써 주면 돼요 . 같은 폴더에 있으니까, 경로를 써줄 필요가 없습니다! 그냥 이름만 써주면 되지요 

#include "LitInput.hlsl"
#include "LitForwardPass.hlsl"

자 그럼 이제 저 LitInput.hlsl 과 LitForwardPass.hlsl 안에 진짜 셰이더 코드가 들어 있으니, 그걸 뜯으러 가 볼까요?

우선은 LitInput.hlsl  부터! 열어봅시다 

LitInput 뜯어보기 

을 열어보면 다음과 같이 생겼을 겁니다. 좀 깁니다만 겁먹지 마세요 

#ifndef UNIVERSAL_LIT_INPUT_INCLUDED
#define UNIVERSAL_LIT_INPUT_INCLUDED

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ParallaxMapping.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl"

#if defined(_DETAIL_MULX2) || defined(_DETAIL_SCALED)
#define _DETAIL
#endif

// NOTE: Do not ifdef the properties here as SRP batcher can not handle different layouts.
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
float4 _DetailAlbedoMap_ST;
half4 _BaseColor;
half4 _SpecColor;
half4 _EmissionColor;
half _Cutoff;
half _Smoothness;
half _Metallic;
half _BumpScale;
half _Parallax;
half _OcclusionStrength;
half _ClearCoatMask;
half _ClearCoatSmoothness;
half _DetailAlbedoMapScale;
half _DetailNormalMapScale;
half _Surface;
CBUFFER_END

// NOTE: Do not ifdef the properties for dots instancing, but ifdef the actual usage.
// Otherwise you might break CPU-side as property constant-buffer offsets change per variant.
// NOTE: Dots instancing is orthogonal to the constant buffer above.
#ifdef UNITY_DOTS_INSTANCING_ENABLED
UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
    UNITY_DOTS_INSTANCED_PROP(float4, _BaseColor)
    UNITY_DOTS_INSTANCED_PROP(float4, _SpecColor)
    UNITY_DOTS_INSTANCED_PROP(float4, _EmissionColor)
    UNITY_DOTS_INSTANCED_PROP(float , _Cutoff)
    UNITY_DOTS_INSTANCED_PROP(float , _Smoothness)
    UNITY_DOTS_INSTANCED_PROP(float , _Metallic)
    UNITY_DOTS_INSTANCED_PROP(float , _BumpScale)
    UNITY_DOTS_INSTANCED_PROP(float , _Parallax)
    UNITY_DOTS_INSTANCED_PROP(float , _OcclusionStrength)
    UNITY_DOTS_INSTANCED_PROP(float , _ClearCoatMask)
    UNITY_DOTS_INSTANCED_PROP(float , _ClearCoatSmoothness)
    UNITY_DOTS_INSTANCED_PROP(float , _DetailAlbedoMapScale)
    UNITY_DOTS_INSTANCED_PROP(float , _DetailNormalMapScale)
    UNITY_DOTS_INSTANCED_PROP(float , _Surface)
UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)

#define _BaseColor              UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float4 , Metadata_BaseColor)
#define _SpecColor              UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float4 , Metadata_SpecColor)
#define _EmissionColor          UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float4 , Metadata_EmissionColor)
#define _Cutoff                 UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_Cutoff)
#define _Smoothness             UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_Smoothness)
#define _Metallic               UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_Metallic)
#define _BumpScale              UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_BumpScale)
#define _Parallax               UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_Parallax)
#define _OcclusionStrength      UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_OcclusionStrength)
#define _ClearCoatMask          UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_ClearCoatMask)
#define _ClearCoatSmoothness    UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_ClearCoatSmoothness)
#define _DetailAlbedoMapScale   UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_DetailAlbedoMapScale)
#define _DetailNormalMapScale   UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_DetailNormalMapScale)
#define _Surface                UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float  , Metadata_Surface)
#endif

TEXTURE2D(_ParallaxMap);        SAMPLER(sampler_ParallaxMap);
TEXTURE2D(_OcclusionMap);       SAMPLER(sampler_OcclusionMap);
TEXTURE2D(_DetailMask);         SAMPLER(sampler_DetailMask);
TEXTURE2D(_DetailAlbedoMap);    SAMPLER(sampler_DetailAlbedoMap);
TEXTURE2D(_DetailNormalMap);    SAMPLER(sampler_DetailNormalMap);
TEXTURE2D(_MetallicGlossMap);   SAMPLER(sampler_MetallicGlossMap);
TEXTURE2D(_SpecGlossMap);       SAMPLER(sampler_SpecGlossMap);
TEXTURE2D(_ClearCoatMap);       SAMPLER(sampler_ClearCoatMap);

#ifdef _SPECULAR_SETUP
    #define SAMPLE_METALLICSPECULAR(uv) SAMPLE_TEXTURE2D(_SpecGlossMap, sampler_SpecGlossMap, uv)
#else
    #define SAMPLE_METALLICSPECULAR(uv) SAMPLE_TEXTURE2D(_MetallicGlossMap, sampler_MetallicGlossMap, uv)
#endif

half4 SampleMetallicSpecGloss(float2 uv, half albedoAlpha)
{
    half4 specGloss;

#ifdef _METALLICSPECGLOSSMAP
    specGloss = half4(SAMPLE_METALLICSPECULAR(uv));
    #ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
        specGloss.a = albedoAlpha * _Smoothness;
    #else
        specGloss.a *= _Smoothness;
    #endif
#else // _METALLICSPECGLOSSMAP
    #if _SPECULAR_SETUP
        specGloss.rgb = _SpecColor.rgb;
    #else
        specGloss.rgb = _Metallic.rrr;
    #endif

    #ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
        specGloss.a = albedoAlpha * _Smoothness;
    #else
        specGloss.a = _Smoothness;
    #endif
#endif

    return specGloss;
}

half SampleOcclusion(float2 uv)
{
    #ifdef _OCCLUSIONMAP
        // TODO: Controls things like these by exposing SHADER_QUALITY levels (low, medium, high)
        #if defined(SHADER_API_GLES)
            return SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv).g;
        #else
            half occ = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv).g;
            return LerpWhiteTo(occ, _OcclusionStrength);
        #endif
    #else
        return half(1.0);
    #endif
}


// Returns clear coat parameters
// .x/.r == mask
// .y/.g == smoothness
half2 SampleClearCoat(float2 uv)
{
#if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
    half2 clearCoatMaskSmoothness = half2(_ClearCoatMask, _ClearCoatSmoothness);

#if defined(_CLEARCOATMAP)
    clearCoatMaskSmoothness *= SAMPLE_TEXTURE2D(_ClearCoatMap, sampler_ClearCoatMap, uv).rg;
#endif

    return clearCoatMaskSmoothness;
#else
    return half2(0.0, 1.0);
#endif  // _CLEARCOAT
}

void ApplyPerPixelDisplacement(half3 viewDirTS, inout float2 uv)
{
#if defined(_PARALLAXMAP)
    uv += ParallaxMapping(TEXTURE2D_ARGS(_ParallaxMap, sampler_ParallaxMap), viewDirTS, _Parallax, uv);
#endif
}

// Used for scaling detail albedo. Main features:
// - Depending if detailAlbedo brightens or darkens, scale magnifies effect.
// - No effect is applied if detailAlbedo is 0.5.
half3 ScaleDetailAlbedo(half3 detailAlbedo, half scale)
{
    // detailAlbedo = detailAlbedo * 2.0h - 1.0h;
    // detailAlbedo *= _DetailAlbedoMapScale;
    // detailAlbedo = detailAlbedo * 0.5h + 0.5h;
    // return detailAlbedo * 2.0f;

    // A bit more optimized
    return half(2.0) * detailAlbedo * scale - scale + half(1.0);
}

half3 ApplyDetailAlbedo(float2 detailUv, half3 albedo, half detailMask)
{
#if defined(_DETAIL)
    half3 detailAlbedo = SAMPLE_TEXTURE2D(_DetailAlbedoMap, sampler_DetailAlbedoMap, detailUv).rgb;

    // In order to have same performance as builtin, we do scaling only if scale is not 1.0 (Scaled version has 6 additional instructions)
#if defined(_DETAIL_SCALED)
    detailAlbedo = ScaleDetailAlbedo(detailAlbedo, _DetailAlbedoMapScale);
#else
    detailAlbedo = half(2.0) * detailAlbedo;
#endif

    return albedo * LerpWhiteTo(detailAlbedo, detailMask);
#else
    return albedo;
#endif
}

half3 ApplyDetailNormal(float2 detailUv, half3 normalTS, half detailMask)
{
#if defined(_DETAIL)
#if BUMP_SCALE_NOT_SUPPORTED
    half3 detailNormalTS = UnpackNormal(SAMPLE_TEXTURE2D(_DetailNormalMap, sampler_DetailNormalMap, detailUv));
#else
    half3 detailNormalTS = UnpackNormalScale(SAMPLE_TEXTURE2D(_DetailNormalMap, sampler_DetailNormalMap, detailUv), _DetailNormalMapScale);
#endif

    // With UNITY_NO_DXT5nm unpacked vector is not normalized for BlendNormalRNM
    // For visual consistancy we going to do in all cases
    detailNormalTS = normalize(detailNormalTS);

    return lerp(normalTS, BlendNormalRNM(normalTS, detailNormalTS), detailMask); // todo: detailMask should lerp the angle of the quaternion rotation, not the normals
#else
    return normalTS;
#endif
}

inline void InitializeStandardLitSurfaceData(float2 uv, out SurfaceData outSurfaceData)
{
    half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
    outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);

    half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
    outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb;

#if _SPECULAR_SETUP
    outSurfaceData.metallic = half(1.0);
    outSurfaceData.specular = specGloss.rgb;
#else
    outSurfaceData.metallic = specGloss.r;
    outSurfaceData.specular = half3(0.0, 0.0, 0.0);
#endif

    outSurfaceData.smoothness = specGloss.a;
    outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
    outSurfaceData.occlusion = SampleOcclusion(uv);
    outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));

#if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
    half2 clearCoat = SampleClearCoat(uv);
    outSurfaceData.clearCoatMask       = clearCoat.r;
    outSurfaceData.clearCoatSmoothness = clearCoat.g;
#else
    outSurfaceData.clearCoatMask       = half(0.0);
    outSurfaceData.clearCoatSmoothness = half(0.0);
#endif

#if defined(_DETAIL)
    half detailMask = SAMPLE_TEXTURE2D(_DetailMask, sampler_DetailMask, uv).a;
    float2 detailUv = uv * _DetailAlbedoMap_ST.xy + _DetailAlbedoMap_ST.zw;
    outSurfaceData.albedo = ApplyDetailAlbedo(detailUv, outSurfaceData.albedo, detailMask);
    outSurfaceData.normalTS = ApplyDetailNormal(detailUv, outSurfaceData.normalTS, detailMask);
#endif
}

#endif // UNIVERSAL_INPUT_SURFACE_PBR_INCLUDED

 

헐 딱봐도 엄청나게 많네요. 

C버퍼에 DOTS에 각종 초기 설정까지.. 잔뜩 들어 있는 코드입니다. 
당황하지말고 하나씩 추가해 보죠 

일단  새로 추가된 텍스쳐의  텍스쳐 2D 오브젝트와 샘플러를 추가해 봅시다 
아래처럼 샘플러들이 잔뜩 모여 있는 곳을 찾아서, 하나 추가해 주면 되겠죠. 이름은 프로퍼티에 있는 이름으로. 

TEXTURE2D(_ParallaxMap);        SAMPLER(sampler_ParallaxMap);
TEXTURE2D(_OcclusionMap);       SAMPLER(sampler_OcclusionMap);
TEXTURE2D(_DetailMask);         SAMPLER(sampler_DetailMask);
TEXTURE2D(_DetailAlbedoMap);    SAMPLER(sampler_DetailAlbedoMap);
TEXTURE2D(_DetailNormalMap);    SAMPLER(sampler_DetailNormalMap);
TEXTURE2D(_MetallicGlossMap);   SAMPLER(sampler_MetallicGlossMap);
TEXTURE2D(_SpecGlossMap);       SAMPLER(sampler_SpecGlossMap);
TEXTURE2D(_ClearCoatMap);       SAMPLER(sampler_ClearCoatMap);
TEXTURE2D(_BaseMap2);       SAMPLER(sampler_BaseMap2);

그리고 C 버퍼도 찾아서 추가해 줍니다. 대충 아래처럼 말이죠. 이거 뭐하는건지 모르면 이전 시간 복습 ㄱㄱ 


CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
float4 _BaseMap2_ST;
float4 _DetailAlbedoMap_ST;
half4 _BaseColor;
half4 _SpecColor;
half4 _EmissionColor;
half _Cutoff;
half _Smoothness;
half _Metallic;
half _BumpScale;
half _Parallax;
half _OcclusionStrength;
half _ClearCoatMask;
half _ClearCoatSmoothness;
half _DetailAlbedoMapScale;
half _DetailNormalMapScale;
half _Surface;
CBUFFER_END

자 이렇게만 해주면 Input에서 해줘야 할건 다 끝난 것 같습니다. 쉽죠?
다음은 LitForwardPass 파일에서 진짜 연산을 추가해 주면 되겠군요 

LitForwardPass 뜯어보기 

이 파일도 만만치 않네요. 여기도 역시 겁먹지 마세요 ㅋㅋㅋ 내가 고민했던 이유를 알겠지 ㅋㅋㅋ 
엄청 많아 보이고 실제로도 엄청 많은데, 텍스쳐 하나 추가하는 정도는 그렇게 힘든건 아닙니다 ㅋㅋ 하지만 영어가 길면 일단 짜증나잖앜ㅋㅋㅋ 

#ifndef UNIVERSAL_FORWARD_LIT_PASS_INCLUDED
#define UNIVERSAL_FORWARD_LIT_PASS_INCLUDED

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

// GLES2 has limited amount of interpolators
#if defined(_PARALLAXMAP) && !defined(SHADER_API_GLES)
#define REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR
#endif

#if (defined(_NORMALMAP) || (defined(_PARALLAXMAP) && !defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR))) || defined(_DETAIL)
#define REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR
#endif

// keep this file in sync with LitGBufferPass.hlsl

struct Attributes
{
    float4 positionOS   : POSITION;
    float3 normalOS     : NORMAL;
    float4 tangentOS    : TANGENT;
    float2 texcoord     : TEXCOORD0;
    float2 staticLightmapUV   : TEXCOORD1;
    float2 dynamicLightmapUV  : TEXCOORD2;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct Varyings
{
    float2 uv                       : TEXCOORD0;

#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
    float3 positionWS               : TEXCOORD1;
#endif

    half3 normalWS                 : TEXCOORD2;
#if defined(REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR)
    half4 tangentWS                : TEXCOORD3;    // xyz: tangent, w: sign
#endif
    float3 viewDirWS                : TEXCOORD4;

#ifdef _ADDITIONAL_LIGHTS_VERTEX
    half4 fogFactorAndVertexLight   : TEXCOORD5; // x: fogFactor, yzw: vertex light
#else
    half  fogFactor                 : TEXCOORD5;
#endif

#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    float4 shadowCoord              : TEXCOORD6;
#endif

#if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
    half3 viewDirTS                : TEXCOORD7;
#endif

    DECLARE_LIGHTMAP_OR_SH(staticLightmapUV, vertexSH, 8);
#ifdef DYNAMICLIGHTMAP_ON
    float2  dynamicLightmapUV : TEXCOORD9; // Dynamic lightmap UVs
#endif

    float4 positionCS               : SV_POSITION;
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};

void InitializeInputData(Varyings input, half3 normalTS, out InputData inputData)
{
    inputData = (InputData)0;

#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
    inputData.positionWS = input.positionWS;
#endif

    half3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
#if defined(_NORMALMAP) || defined(_DETAIL)
    float sgn = input.tangentWS.w;      // should be either +1 or -1
    float3 bitangent = sgn * cross(input.normalWS.xyz, input.tangentWS.xyz);
    half3x3 tangentToWorld = half3x3(input.tangentWS.xyz, bitangent.xyz, input.normalWS.xyz);

    #if defined(_NORMALMAP)
    inputData.tangentToWorld = tangentToWorld;
    #endif
    inputData.normalWS = TransformTangentToWorld(normalTS, tangentToWorld);
#else
    inputData.normalWS = input.normalWS;
#endif

    inputData.normalWS = NormalizeNormalPerPixel(inputData.normalWS);
    inputData.viewDirectionWS = viewDirWS;

#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    inputData.shadowCoord = input.shadowCoord;
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
    inputData.shadowCoord = TransformWorldToShadowCoord(inputData.positionWS);
#else
    inputData.shadowCoord = float4(0, 0, 0, 0);
#endif
#ifdef _ADDITIONAL_LIGHTS_VERTEX
    inputData.fogCoord = InitializeInputDataFog(float4(input.positionWS, 1.0), input.fogFactorAndVertexLight.x);
    inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
#else
    inputData.fogCoord = InitializeInputDataFog(float4(input.positionWS, 1.0), input.fogFactor);
#endif

#if defined(DYNAMICLIGHTMAP_ON)
    inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.dynamicLightmapUV, input.vertexSH, inputData.normalWS);
#else
    inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.vertexSH, inputData.normalWS);
#endif

    inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);
    inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV);

    #if defined(DEBUG_DISPLAY)
    #if defined(DYNAMICLIGHTMAP_ON)
    inputData.dynamicLightmapUV = input.dynamicLightmapUV;
    #endif
    #if defined(LIGHTMAP_ON)
    inputData.staticLightmapUV = input.staticLightmapUV;
    #else
    inputData.vertexSH = input.vertexSH;
    #endif
    #endif
}

///////////////////////////////////////////////////////////////////////////////
//                  Vertex and Fragment functions                            //
///////////////////////////////////////////////////////////////////////////////

// Used in Standard (Physically Based) shader
Varyings LitPassVertex(Attributes input)
{
    Varyings output = (Varyings)0;

    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_TRANSFER_INSTANCE_ID(input, output);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

    VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);

    // normalWS and tangentWS already normalize.
    // this is required to avoid skewing the direction during interpolation
    // also required for per-vertex lighting and SH evaluation
    VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);

    half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);

    half fogFactor = 0;
    #if !defined(_FOG_FRAGMENT)
        fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
    #endif

    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);

    // already normalized from normal transform to WS.
    output.normalWS = normalInput.normalWS;
#if defined(REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR) || defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
    real sign = input.tangentOS.w * GetOddNegativeScale();
    half4 tangentWS = half4(normalInput.tangentWS.xyz, sign);
#endif
#if defined(REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR)
    output.tangentWS = tangentWS;
#endif

#if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
    half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS);
    half3 viewDirTS = GetViewDirectionTangentSpace(tangentWS, output.normalWS, viewDirWS);
    output.viewDirTS = viewDirTS;
#endif

    OUTPUT_LIGHTMAP_UV(input.staticLightmapUV, unity_LightmapST, output.staticLightmapUV);
#ifdef DYNAMICLIGHTMAP_ON
    output.dynamicLightmapUV = input.dynamicLightmapUV.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#endif
    OUTPUT_SH(output.normalWS.xyz, output.vertexSH);
#ifdef _ADDITIONAL_LIGHTS_VERTEX
    output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
#else
    output.fogFactor = fogFactor;
#endif

#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
    output.positionWS = vertexInput.positionWS;
#endif

#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    output.shadowCoord = GetShadowCoord(vertexInput);
#endif

    output.positionCS = vertexInput.positionCS;

    return output;
}

// Used in Standard (Physically Based) shader
half4 LitPassFragment(Varyings input) : SV_Target
{
    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

#if defined(_PARALLAXMAP)
#if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
    half3 viewDirTS = input.viewDirTS;
#else
    half3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
    half3 viewDirTS = GetViewDirectionTangentSpace(input.tangentWS, input.normalWS, viewDirWS);
#endif
    ApplyPerPixelDisplacement(viewDirTS, input.uv);
#endif

    SurfaceData surfaceData;
    InitializeStandardLitSurfaceData(input.uv, surfaceData);

    InputData inputData;
    InitializeInputData(input, surfaceData.normalTS, inputData);
    SETUP_DEBUG_TEXTURE_DATA(inputData, input.uv, _BaseMap);

#ifdef _DBUFFER
    ApplyDecalToSurfaceData(input.positionCS, surfaceData, inputData);
#endif

    half4 color = UniversalFragmentPBR(inputData, surfaceData);

    color.rgb = MixFog(color.rgb, inputData.fogCoord);
    color.a = OutputAlpha(color.a, _Surface);

    return color;
}

#endif

자 그럼 이 코드를 잘 보면,  
Attributes 구조체가  LitPassVertex 라고 하는 버텍스 셰이더로 들어가고, 
그 출력이 Varyings 이라고 하는 구조체로 들어가서 LitPassFragment 라고 하는 픽셀셰이더로 들어가 연산되는걸 알 수 있습니다. 안 보인다고요? 정상이예요 ㅋ

으잌 정말 초보자들용은 아니라니까 ㅋ

그래도 해보실 분은 한 번 해봅시다. 사실 이글은 망했어 .. 여기까지 볼 인간이 어딨겠어...
하지만 만약 참고 보신 분들은 한 번 해보세요 ㅋㅋ

일단 코드에서 버텍스 셰이더를 찾아봅시다. 일케 생겼어요. 

///////////////////////////////////////////////////////////////////////////////
//                  Vertex and Fragment functions                            //
///////////////////////////////////////////////////////////////////////////////

// Used in Standard (Physically Based) shader
Varyings LitPassVertex(Attributes input)
{
    Varyings output = (Varyings)0;

    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_TRANSFER_INSTANCE_ID(input, output);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

    VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);

    // normalWS and tangentWS already normalize.
    // this is required to avoid skewing the direction during interpolation
    // also required for per-vertex lighting and SH evaluation
    VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);

    half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);

    half fogFactor = 0;
    #if !defined(_FOG_FRAGMENT)
        fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
    #endif

    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);

    // already normalized from normal transform to WS.
    output.normalWS = normalInput.normalWS;
#if defined(REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR) || defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
    real sign = input.tangentOS.w * GetOddNegativeScale();
    half4 tangentWS = half4(normalInput.tangentWS.xyz, sign);
#endif
#if defined(REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR)
    output.tangentWS = tangentWS;
#endif

#if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
    half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS);
    half3 viewDirTS = GetViewDirectionTangentSpace(tangentWS, output.normalWS, viewDirWS);
    output.viewDirTS = viewDirTS;
#endif

    OUTPUT_LIGHTMAP_UV(input.staticLightmapUV, unity_LightmapST, output.staticLightmapUV);
#ifdef DYNAMICLIGHTMAP_ON
    output.dynamicLightmapUV = input.dynamicLightmapUV.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#endif
    OUTPUT_SH(output.normalWS.xyz, output.vertexSH);
#ifdef _ADDITIONAL_LIGHTS_VERTEX
    output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
#else
    output.fogFactor = fogFactor;
#endif

#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
    output.positionWS = vertexInput.positionWS;
#endif

#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    output.shadowCoord = GetShadowCoord(vertexInput);
#endif

    output.positionCS = vertexInput.positionCS;

    return output;
}

오메 뭐가 많네.

하지만 대부분은 디파인 if 문입니다. 유니티의 다양한 기능들에 대해 분기된 거예요.
여기서는 사실 건드릴게 없습니다. 버텍스 셰이더에서 uv 연산을 하는데, 어차피 uv는 기존 텍스쳐와 같은걸 쓸 테니까 말이죠. 

그럼 우리가 볼 곳은 픽셀셰이더입니다. 이걸  찾아보세요 

// Used in Standard (Physically Based) shader
half4 LitPassFragment(Varyings input) : SV_Target
{
    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

#if defined(_PARALLAXMAP)
#if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
    half3 viewDirTS = input.viewDirTS;
#else
    half3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
    half3 viewDirTS = GetViewDirectionTangentSpace(input.tangentWS, input.normalWS, viewDirWS);
#endif
    ApplyPerPixelDisplacement(viewDirTS, input.uv);
#endif

    SurfaceData surfaceData;
    InitializeStandardLitSurfaceData(input.uv, surfaceData);

    InputData inputData;
    InitializeInputData(input, surfaceData.normalTS, inputData);
    SETUP_DEBUG_TEXTURE_DATA(inputData, input.uv, _BaseMap);

#ifdef _DBUFFER
    ApplyDecalToSurfaceData(input.positionCS, surfaceData, inputData);
#endif

    half4 color = UniversalFragmentPBR(inputData, surfaceData);

    color.rgb = MixFog(color.rgb, inputData.fogCoord);
    color.a = OutputAlpha(color.a, _Surface);

    return color;
}

찾으셨다면 이 부분이 진짜 주요 부분이죠. 자 그럼 여기 계산을 넣어 봅시다. 
이전에 _BaseColor 의 연산 부분이 있다면 그걸 복사해서 넣으면 간단할텐데, 
...없죠? 텍스쳐 연산이 하나도 안보여요. 

어딨냐면..

SurfaceData surfaceData;
    InitializeStandardLitSurfaceData(input.uv, surfaceData);

이 부분이 그 부분입니다. SurfaceData는 이렇게 구성된 구조체구요. 

struct SurfaceData
{
    half3 albedo;
    half3 specular;
    half  metallic;
    half  smoothness;
    half3 normalTS;
    half3 emission;
    half  occlusion;
    half  alpha;
    half  clearCoatMask;
    half  clearCoatSmoothness;
};

그럼 InitializeStandardLitSurfaceData 함수는 어디있느냐? 

웃기게도 아까 본 LitInput.hlsl안에 있습니다 ㅎ 결국 다시 돌아가야 함 
그리고 결국 텍스쳐 하나 추가하려면 LitForwardPass 에서는 아무것도 할 필요가 없다는 말이 되었습니다 ㅋㅋㅋㅋㅋㅋ

하하하하 낚였구나 

 

LitInput 으로 다시 돌아가기 

자 왠지 노가다 시키는 기분이지만, LitInput으로 돌아와서 InitializeStandardLitSurfaceData 함수를 찾아보세요 
저 아래 보입니다. 

와우! 텍스쳐 연산이 여기 다 있었구만!! 

inline void InitializeStandardLitSurfaceData(float2 uv, out SurfaceData outSurfaceData)
{
    half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
    outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);

    half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
    outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb;

#if _SPECULAR_SETUP
    outSurfaceData.metallic = half(1.0);
    outSurfaceData.specular = specGloss.rgb;
#else
    outSurfaceData.metallic = specGloss.r;
    outSurfaceData.specular = half3(0.0, 0.0, 0.0);
#endif

    outSurfaceData.smoothness = specGloss.a;
    outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
    outSurfaceData.occlusion = SampleOcclusion(uv);
    outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));

#if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
    half2 clearCoat = SampleClearCoat(uv);
    outSurfaceData.clearCoatMask       = clearCoat.r;
    outSurfaceData.clearCoatSmoothness = clearCoat.g;
#else
    outSurfaceData.clearCoatMask       = half(0.0);
    outSurfaceData.clearCoatSmoothness = half(0.0);
#endif

#if defined(_DETAIL)
    half detailMask = SAMPLE_TEXTURE2D(_DetailMask, sampler_DetailMask, uv).a;
    float2 detailUv = uv * _DetailAlbedoMap_ST.xy + _DetailAlbedoMap_ST.zw;
    outSurfaceData.albedo = ApplyDetailAlbedo(detailUv, outSurfaceData.albedo, detailMask);
    outSurfaceData.normalTS = ApplyDetailNormal(detailUv, outSurfaceData.normalTS, detailMask);
#endif
}

그럼 여기다가 넣으면 되지요. albedo 연산에다가 말이죠 

위 코드의 아래 부분을 

half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);

half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb;

 

이렇게 바꾸어 보세요 
이러면 두 텍스쳐를 단순하게 더하는게 되겠죠. 

half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);
half4 baseMap2 = SAMPLE_TEXTURE2D(_BaseMap2,sampler_BaseMap2,uv);

half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
outSurfaceData.albedo =  (albedoAlpha.rgb + baseMap2.rgb) * _BaseColor.rgb;

 

그리고 저장하면 ? 

짜잔. 

이제 배운대로 저걸 흘러가게 하던지 말던지 맘대로 하셔도 되고요. 

다른 텍스쳐들도 여기서 수정할 수 있을테니 맘대로 가지고 놀아 보세요. 

뭐야.. 결국 LitInput 파일만 건드리면 되는 거였잖아... 하시는 분은 .. 맞아요.. 맞는데
그게 왜 거깄는지 보여드리려고 LitForward도 보여드린 거라구요... (변명) 

룰루랄라 

수정된 최종 Lit 파일의 InitializeStandardLitSurfaceData 함수는 다음과 같습니다. 앞 부분만 달라졌죠. 

inline void InitializeStandardLitSurfaceData(float2 uv, out SurfaceData outSurfaceData)
{
    half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
    outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);
    half4 baseMap2 = SAMPLE_TEXTURE2D(_BaseMap2,sampler_BaseMap2,uv);

    half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
    outSurfaceData.albedo =  (albedoAlpha.rgb + baseMap2.rgb) * _BaseColor.rgb;

#if _SPECULAR_SETUP
    outSurfaceData.metallic = half(1.0);
    outSurfaceData.specular = specGloss.rgb;
#else
    outSurfaceData.metallic = specGloss.r;
    outSurfaceData.specular = half3(0.0, 0.0, 0.0);
#endif

    outSurfaceData.smoothness = specGloss.a;
    outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
    outSurfaceData.occlusion = SampleOcclusion(uv);
    outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));

#if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
    half2 clearCoat = SampleClearCoat(uv);
    outSurfaceData.clearCoatMask       = clearCoat.r;
    outSurfaceData.clearCoatSmoothness = clearCoat.g;
#else
    outSurfaceData.clearCoatMask       = half(0.0);
    outSurfaceData.clearCoatSmoothness = half(0.0);
#endif

#if defined(_DETAIL)
    half detailMask = SAMPLE_TEXTURE2D(_DetailMask, sampler_DetailMask, uv).a;
    float2 detailUv = uv * _DetailAlbedoMap_ST.xy + _DetailAlbedoMap_ST.zw;
    outSurfaceData.albedo = ApplyDetailAlbedo(detailUv, outSurfaceData.albedo, detailMask);
    outSurfaceData.normalTS = ApplyDetailNormal(detailUv, outSurfaceData.normalTS, detailMask);
#endif
}

 

자 뭐 이건 어려워서 못 따라하겠어! 해도 괜찮습니다. 
이건 '우와 나 그럼 막 다 뜯고 할 수 있는거야?!?' 라는 자신감을 가지셨을 뉴비분들한테 
'아니 아직은 아냐 나의 작은 고양이' 라고 말하려고 만든 단계였고, 

 
다음 시간부터는 다시 기초부터 차근차근히 할 거니까요 

자 그럼 오늘은 여기까지! 길었고 간만이었다!!

 

블로그 주인장에게 커피값을 후원할 수 있습니다! 

 

 

 

 

 

 

 

 

 

반응형

댓글