WinCNT

유니티에서 커스텀 셰이더의 머티리얼에 라이트 맵을 적용해보기 본문

Unity/URP or Shader 관련

유니티에서 커스텀 셰이더의 머티리얼에 라이트 맵을 적용해보기

WinCNT_SSS 2023. 6. 30. 13:49

서론

라이트 맵을 커스텀 셰이더에서 사용하는 것은 레퍼런스도 많고 Unity의 함수를 편하게 가져다 사용할 수도 있기에 이전에 삽질했던 것에 비해서 어렵지 않았다

물론 깊이 파면 어렵겠지만 이미 내 HP는 0이기 때문에 파지 않으려고 한다…


라이트 맵의 텍스처 채널

라이트 맵을 구우면 각각의 게임 오브젝트에 알맞은 라이트 맵이 할당된다

할당되는 UV의 채널은 UV1이며, Mesh 클래스의 프로퍼티로는 uv2인데(즉 Mesh.uv2),

아무튼 TEXCOORD1에 할당된다고만 알고 있으면 될 것 같다

Unity - Manual: Generating lightmap UVs

 

Unity - Manual: Generating lightmap UVs

Lightmap UVs introduction Generating lightmap UVs Unity can calculate the UVs for baked lightmapsA pre-rendered texture that contains the effects of light sources on static objects in the scene. Lightmaps are overlaid on top of scene geometry to create the

docs.unity3d.com

Unity - Scripting API: Mesh.uv2

 

Unity - Scripting API: Mesh.uv2

This channel is also commonly called "UV1". It maps to the shader semantic `TEXCOORD1`. When you call Mesh.HasVertexAttribute, this channel corresponds to VertexAttribute.TexCoord1. Unity can use this channel to store UVs for baked lightmaps, or input UVs

docs.unity3d.com

이건 Bakery 에셋도 마찬가지이며, 매뉴얼을 참고해서 라이트 맵을 구우면, 다음과 같이 오브젝트에 라이트맵이 할당된 것을 볼 수 있다


셰이더에서 라이트 맵의 정보 가져오기

우선 다음과 같이 LIGHTMAP_ON의 지시어를 추가하고, 특정 라이브러리를 include해야 한다

#pragma shader_feature LIGHTMAP_ON

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

 

라이트 맵은 TEXCOORD1에 할당된다고 하니 다음과 같은 느낌으로 시멘틱스를 지정해주면 된다

예시를 위해 다른 정보들도 포함시켰다

struct appdata
{
    float3 pos : POSITION;
    half3 normal: NORMAL;
    float2 uv: TEXCOORD0;
    float2 lightmapUV: TEXCOORD1;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct v2f
{
    float4 pos : SV_POSITION;
    float4 uv: TEXCOORD0;
    float2 uv2: TEXCOORD1;
    DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 2);
    float3 normal: TEXCOORD3;
    float3 tangent: TEXCOORD4;
    float3 bi_normal: TEXCOORD5;
    float fog_coord : TEXCOORD6;

    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};

 

버텍스 셰이더에서는 Lighting.hlsl의 매크로인 OUTPUT_LIGHTMAP_UV를 사용하면 된다

마찬가지로 예시를 위해 다른 정보들도 포함시켰다

예를 들어 OUTPUT_SH(Lighting.hlsl의 매크로)는 LIGHTMAP_ON 지시어에 따라서 이번에는 아무런 역할도 하지 않을테지만 예시를 위해 추가해두었다

v2f vert(const appdata v)
{
    v2f o;

    UNITY_SETUP_INSTANCE_ID(v);
    ZERO_INITIALIZE(v2f, o);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

    // 다른 내용은 생략
    o.normal = TransformObjectToWorldNormal(v.normal);
    OUTPUT_LIGHTMAP_UV(v.lightmapUV, unity_LightmapST, o.lightmapUV);
    OUTPUT_SH(o.normal, o.vertexSH);
    // 다른 내용은 생략

    o.pos = TransformObjectToHClip(v.pos);
    o.fog_coord = ComputeFogFactor(o.pos.z);

    return o;
}

참고로 DECLARE_LIGHTMAP_OR_SH하고 OUTPUT_LIGHTMAP_UV는 Lighting.hlsl의 매크로이며, unity_LightmapST는 UnityInput.hlsl의 변수이다

 

그 다음으로 프래그먼트 셰이더에서는 다음과 같은 매크로로 라이트 맵의 정보를 취득하면 된다

float3 frag(v2f i) : SV_Target
{
    UNITY_SETUP_INSTANCE_ID(i);

    float3 color; // 메인 컬러
    // 메인 컬러에 대한 계산은 생략(알베도 텍스처 취득 등)

    // 라이트 맵의 정보를 취득
    float3 light_col = SAMPLE_GI(i.lightmapUV, i.vertexSH, normal);
    //return light_col;

    // 메인 라이트와 라이트 맵의 정보를 취합
    Light mainLight = GetMainLight();
    light_col.rgb += mainLight.color * mainLight.distanceAttenuation * max(0, ld);
    color = color * light_col;

    // 라이트 맵과 관계 없는 안개 처리
    color = MixFog(color, i.fog_coord);

    return color;
}

SAMPLE_GI는 GlobalIllumination.hlsl의 매크로이지만, GlobalIllumination.hlsl는 이미 Lighting.hlsl에 포함되어 있기 때문에 따로 include할 필요는 없다


결과

참고로 다음과 같이 프래그먼트 셰이더를 살짝 수정하면 라이트 맵 정보만을 확인할 수 있다

float3 frag(v2f i) : SV_Target
{
    UNITY_SETUP_INSTANCE_ID(i);

    // 라이트 맵의 정보를 취득
    float3 light_col = SAMPLE_GI(i.lightmapUV, i.vertexSH, normal);
    return light_col;
}

return SAMPLE_GI(좌) / Baked Lightmap 씬 뷰 드로우 모드(우)

SAMPLE_GI를 return한 결과가 Baked Lightmap 씬 뷰 드로우 모드와 똑같다면 제대로 라이트 맵 정보를 취득했다고 할 수 있다


마무리

예제도 많았고 이미 유니티에 구현되어 있는 매크로가 많아서 앞선 작업들에 비해서 어렵지 않게 끝낼 수 있었다


참고 사이트

UPR 셰이더 코딩 튜토리얼 : 제 3편 - 커스텀 라이트 맵, 에디셔널 라이트, fake Shadowmask (feat : TS Normal)

 

UPR 셰이더 코딩 튜토리얼 : 제 3편 - 커스텀 라이트 맵, 에디셔널 라이트, fake Shadowmask (feat : TS Norma

안녕하세요 마둠파입니다. URP 코딩 튜토리얼도 이 글로 벌써 3편이나 작성하게 되었네요. 2편까지의 코...

blog.naver.com

URP ShaderLab Pass tags | Universal RP | 12.0.0

 

URP ShaderLab Pass tags | Universal RP | 12.0.0

This section contains descriptions of URP-specific ShaderLab Pass tags. NOTE: URP does not support the following LightMode tags: Always, ForwardAdd, PrepassBase, PrepassFinal, Vertex, VertexLMRGBM, VertexLM. LightMode The value of this tag lets the pipelin

docs.unity3d.com

Unity のシェーダー開発方法のまとめと備忘録・頂点アニメーションテクスチャ(VAT)シェーダー - Qiita

 

Unity のシェーダー開発方法のまとめと備忘録・頂点アニメーションテクスチャ(VAT)シェーダ

Unity のみで完結できる頂点アニメーションテクスチャー(VAT・Vertex Animation Texture)のエクスポーターとシェーダーを作りました。 Vertex Animation Texture shader,...

qiita.com

Global Illumination

 

Global Illumination

A Unity Scriptable Render Pipeline tutorial about supporting both static and dynamic global illumination.

catlikecoding.com