티스토리 뷰

Unity/Shader

PBR Lighting

연홍 2021. 1. 25. 10:37
728x90

PBR (물리 기반 렌더링)은 Unity의 "Standard"셰이더가 사용하는 셰이딩/조명모델이며 셰이더 그래프의 URP "Lit"셰이더 및 PBR 마스터 노드입니다.

 

이전 섹션에서 언급했듯이 기본 제공 파이프 라인의 음영/조명은 일반적으로 "Standard"옵션이 PBR모델인 surface셰이더에 의해 처리되었습니다. 이들은 Albedo, Normal, Emission, Smoothness, Occlusion, Alpha 및 Metallic (또는 "StandardSpecular"워크 플로우를 사용하는 경우 Specular)을 출력하는 surface함수를 사용했습니다. Unity는 이를 가져와 scene뒤에서 vertex 및 fragment셰이더를 생성하여 PBR 셰이딩/조명 및 그림자와 같은 특정 계산을 처리합니다

 

Universal RP는 surface셰이더를 지원하지 않지만 ShaderLibrary는 많은 조명 계산을 처리하는 데 도움이되는 함수를 제공합니다. 이것들은 Lighting.hlsl에 포함되어 있습니다. 이 섹션에서는 UniversalFragmentPBR에 초점을 맞출 것입니다.

half4 UniversalFragmentPBR(InputData inputData, half3 albedo,
   half metallic, half3 specular, half smoothness, half occlusion, 
   half3 emission, half alpha)
 
// v10.x.x에 추가 된 SurfaceData 구조체가있는 버전도 있습니다.
// 이전 버전의 경우 대신 위 버전을 사용해야합니다.
// (하지만 여전히 SurfaceData 구조체를 사용하여 데이터를 구성 / 보유 할 수 있습니다.)
half4 UniversalFragmentPBR(InputData inputData, SurfaceData surfaceData)
 
// There is also :
half4 UniversalFragmentBlinnPhong(InputData inputData, half3 diffuse, half4 specularGloss, half smoothness, half3 emission, half alpha)
// Unity v4 이전의 "이전"표면 셰이더를 복제합니다.
// URP의 "SimpleLit"셰이더에서 사용됩니다.
// Lambert (확산) 및 BlinnPhong (스페 큘러) 조명 모델 사용

먼저 PBR 조명 모델이 사용하는 몇 가지 속성을 추가해야합니다.

 

metallic/스페큘러 맵과 오클루전 맵은 제외하고 있습니다. 주로 샘플링을 처리할 수 있는 좋은 기능이 없기 때문입니다. (LitInput.hlsl에서 복사하지 않는 한 실제 ShaderLibrary가 아닌 Lit 셰이더 URP의 일부입니다.)이 섹션은 이미 꽤 길고 복잡합니다. 주로 어디에서 사용할 기능을 아는 것이기 때문에 실제로 설명 할 수 있는 것은 거의 없습니다. 나중에 LitInput을 예로 사용하여 언제든지 추가 할 수 있습니다.

Properties {
    _BaseMap ("Base Texture", 2D) = "white" {}
    _BaseColor ("Example Colour", Color) = (0, 0.66, 0.73, 1)
    _Smoothness ("Smoothness", Float) = 0.5
 
    [Toggle(_ALPHATEST_ON)] _EnableAlphaTest("Enable Alpha Cutoff", Float) = 0.0
    _Cutoff ("Alpha Cutoff", Float) = 0.5
 
    [Toggle(_NORMALMAP)] _EnableBumpMap("Enable Normal/Bump Map", Float) = 0.0
    _BumpMap ("Normal/Bump Texture", 2D) = "bump" {}
    _BumpScale ("Bump Scale", Float) = 1
 
    [Toggle(_EMISSION)] _EnableEmission("Enable Emission", Float) = 0.0
    _EmissionMap ("Emission Texture", 2D) = "white" {}
    _EmissionColor ("Emission Colour", Color) = (0, 0, 0, 0)
    }
...
// And need to adjust the CBUFFER to include these too
CBUFFER_START(UnityPerMaterial)
    float4 _BaseMap_ST; // Texture tiling & offset inspector values
    float4 _BaseColor;
    float _BumpScale;
    float4 _EmissionColor;
    float _Smoothness;
    float _Cutoff;
CBUFFER_END

일부 multi_compile 및 shader_features 추가 및 Attributes 및 Varyings 구조체에 대한 조정을 포함하여 Unlit 셰이더 코드를 여러 번 변경해야합니다. 조명 계산에 사용하기 위해 메시의 normals 및 탄젠트 데이터가 필요하므로 fragment으로 보냅니다.

 

Properties 블록의 이러한 TOGGLE 부분을 사용하면 머티리얼 인스펙터에서 shader_feature 키워드를 활성화 / 비활성화 할 수 있습니다. (또는 셰이더용 사용자정의 editor/inspector GUI를 작성하거나 디버그 검사기를 사용할 수 있습니다.)

 

베이크 된 라이트 맵을 지원하려면 TEXCOORD1 채널에서 전달되는 라이트 맵 UV도 필요합니다.

 

또한 ShaderLibrary의 SurfaceInput.hlsl을 사용하여 몇 가지를 지원하고 있습니다. SurfaceData 구조체가 PBR에 필요한 데이터와 알베도, 노멀 및 emission맵을 샘플링하는 데 필요한 일부 함수를 보유하는 데 도움이됩니다. (참고 : 해당 구조체가 이동 한 것 같습니다. URPv10에서는 SurfaceData.hlsl.로, SurfaceInput.hlsl에 의해 자동으로 포함됩니다)

// Material Keywords
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
//#pragma shader_feature _METALLICSPECGLOSSMAP
//#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
//#pragma shader_feature _OCCLUSIONMAP
 
//#pragma shader_feature _SPECULARHIGHLIGHTS_OFF
//#pragma shader_feature _ENVIRONMENTREFLECTIONS_OFF
//#pragma shader_feature _SPECULAR_SETUP
#pragma shader_feature _RECEIVE_SHADOWS_OFF
 
// URP Keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile _ _SHADOWS_SOFT
#pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE
 
// Unity defined keywords
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile_fog
 
// Some added includes, required to use the Lighting functions
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
// And this one for the SurfaceData struct and albedo/normal/emission sampling functions.
// Note : It also defines the _BaseMap, _BumpMap and _EmissionMap textures for us, so we should use these as Shaderlab Properties too.
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl"
 
struct Attributes {
    float4 positionOS   : POSITION;
    float3 normalOS     : NORMAL;
    float4 tangentOS    : TANGENT;
    float4 color        : COLOR;
    float2 uv           : TEXCOORD0;
    float2 lightmapUV   : TEXCOORD1;
};
 
struct Varyings {
    float4 positionCS               : SV_POSITION;
    float4 color                    : COLOR;
    float2 uv                       : TEXCOORD0;
    DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 1);
    // Note this macro is using TEXCOORD1
#ifdef REQUIRES_WORLD_SPACE_POS_INTERPOLATOR
    float3 positionWS               : TEXCOORD2;
#endif
    float3 normalWS                 : TEXCOORD3;
#ifdef _NORMALMAP
    float4 tangentWS                : TEXCOORD4;
#endif
    float3 viewDirWS                : TEXCOORD5;
    half4 fogFactorAndVertexLight   : TEXCOORD6;
    // x: fogFactor, yzw: vertex light
#ifdef REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR
    float4 shadowCoord              : TEXCOORD7;
#endif
};
 
//TEXTURE2D(_BaseMap);
//SAMPLER(sampler_BaseMap);
// Removed, since SurfaceInput.hlsl now defines the _BaseMap for us

이제 Varyings에는 통과되는 라이트 맵 UV, 법선 및 접선도 포함되어 있지만 조명 계산에 필요한 뷰 방향, 안개, 정점 조명 지원 및 그림자 수신을위한 그림자 좌표도 추가했습니다.

이제 이러한 모든 변경 사항을 처리하기 위해 버텍스 셰이더를 업데이트해야합니다. 이는 주로 사용할 함수를 아는 것입니다.

#if SHADER_LIBRARY_VERSION_MAJOR < 9
    // This function was added in URP v9.x.x versions
    // If we want to support URP versions before, we need to handle it instead.
    // Computes the world space view direction (pointing towards the viewer).
    float3 GetWorldSpaceViewDir(float3 positionWS) {
        if (unity_OrthoParams.w == 0) {
            // Perspective
            return _WorldSpaceCameraPos - positionWS;
        } else {
            // Orthographic
            float4x4 viewMat = GetWorldToViewMatrix();
            return viewMat[2].xyz;
        }
    }
#endif
 
Varyings vert(Attributes IN) {
    Varyings OUT;
 
    // Vertex Position
    VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
    OUT.positionCS = positionInputs.positionCS;
#ifdef REQUIRES_WORLD_SPACE_POS_INTERPOLATOR
    OUT.positionWS = positionInputs.positionWS;
#endif
    // UVs & Vertex Colour
    OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
    OUT.color = IN.color;
 
    // View Direction
    OUT.viewDirWS = GetWorldSpaceViewDir(positionInputs.positionWS);
 
    // Normals & Tangents
    VertexNormalInputs normalInputs = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);
    OUT.normalWS =  normalInputs.normalWS;
#ifdef _NORMALMAP
    real sign = IN.tangentOS.w * GetOddNegativeScale();
    OUT.tangentWS = half4(normalInputs.tangentWS.xyz, sign);
#endif
 
    // Vertex Lighting & Fog
    half3 vertexLight = VertexLighting(positionInputs.positionWS, normalInputs.normalWS);
    half fogFactor = ComputeFogFactor(positionInputs.positionCS.z);
    OUT.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
 
    // Baked Lighting & SH (used for Ambient if there is no baked)
    OUTPUT_LIGHTMAP_UV(IN.lightmapUV, unity_LightmapST, OUT.lightmapUV);
    OUTPUT_SH(OUT.normalWS.xyz, OUT.vertexSH);
 
    // Shadow Coord
#ifdef REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR
    OUT.shadowCoord = GetShadowCoord(positionInputs);
#endif
    return OUT;
}

이제 실제로 UniversalFragmentPBR 함수를 사용하도록 조각을 업데이트 할 수도 있습니다. InputData 구조체 입력이 필요하므로이를 만들고 설정해야합니다. fragment셰이더에서 작업을 수행하는 대신 항목을 구성하는 데 도움이되는 다른 함수를 만들 것입니다.

마찬가지로 모든 Albedo, Metallic, Specular, Smoothness, Occlusion, Emission 및 Alpha 입력을 처리하기 위해 SurfaceData 구조체 (앞에 포함 된 SurfaceInput.hlsl에서 제공)를 사용하고 이를 처리 할 다른 함수를 만듭니다.

InputData InitializeInputData(Varyings IN, half3 normalTS){
    InputData inputData = (InputData)0;
 
#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
    inputData.positionWS = IN.positionWS;
#endif
                 
    half3 viewDirWS = SafeNormalize(IN.viewDirWS);
#ifdef _NORMALMAP
    float sgn = IN.tangentWS.w; // should be either +1 or -1
    float3 bitangent = sgn * cross(IN.normalWS.xyz, IN.tangentWS.xyz);
    inputData.normalWS = TransformTangentToWorld(normalTS, half3x3(IN.tangentWS.xyz, bitangent.xyz, IN.normalWS.xyz));
#else
    inputData.normalWS = IN.normalWS;
#endif
 
    inputData.normalWS = NormalizeNormalPerPixel(inputData.normalWS);
    inputData.viewDirectionWS = viewDirWS;
 
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    inputData.shadowCoord = IN.shadowCoord;
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
    inputData.shadowCoord = TransformWorldToShadowCoord(inputData.positionWS);
#else
    inputData.shadowCoord = float4(0, 0, 0, 0);
#endif
 
    inputData.fogCoord = IN.fogFactorAndVertexLight.x;
    inputData.vertexLighting = IN.fogFactorAndVertexLight.yzw;
    inputData.bakedGI = SAMPLE_GI(IN.lightmapUV, IN.vertexSH, inputData.normalWS);
    return inputData;
}
 
SurfaceData InitializeSurfaceData(Varyings IN){
    SurfaceData surfaceData = (SurfaceData)0;
    // Note, we can just use SurfaceData surfaceData; here and not set it.
    // However we then need to ensure all values in the struct are set before returning.
    // By casting 0 to SurfaceData, we automatically set all the contents to 0.
         
    half4 albedoAlpha = SampleAlbedoAlpha(IN.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
    surfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);
    surfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb * IN.color.rgb;
 
    // Not supporting the metallic/specular map or occlusion map
    // for an example of that see : https://github.com/Unity-Technologies/Graphics/blob/master/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl
 
    surfaceData.smoothness = _Smoothness;
    surfaceData.normalTS = SampleNormal(IN.uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
    surfaceData.emission = SampleEmission(IN.uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));
    surfaceData.occlusion = 1;
    return surfaceData;
}
 
half4 frag(Varyings IN) : SV_Target {
    SurfaceData surfaceData = InitializeSurfaceData(IN);
    InputData inputData = InitializeInputData(IN, surfaceData.normalTS);
                 
    // In URP v10+ versions we could use this :
    // half4 color = UniversalFragmentPBR(inputData, surfaceData);
 
    // But for other versions, we need to use this instead.
    // We could also avoid using the SurfaceData struct completely, but it helps to organise things.
    half4 color = UniversalFragmentPBR(inputData, surfaceData.albedo, surfaceData.metallic, 
      surfaceData.specular, surfaceData.smoothness, surfaceData.occlusion, 
      surfaceData.emission, surfaceData.alpha);
                 
    color.rgb = MixFog(color.rgb, inputData.fogCoord);
 
    // color.a = OutputAlpha(color.a);
    // Not sure if this is important really. It's implemented as :
    // saturate(outputAlpha + _DrawObjectPassData.a);
    // Where _DrawObjectPassData.a is 1 for opaque objects and 0 for alpha blended.
    // But it was added in URP v8, and versions before just didn't have it.
    // And I'm writing thing for v7.3.1 currently
    // We could still saturate the alpha to ensure it doesn't go outside the 0-1 range though :
    color.a = saturate(color.a);
 
    return color;
}

이것이 바로 PBR 조명입니다. 상당히 많은 코드였습니다! 이것이 어떻게 작동하는지 보는 것이 너무 복잡하지 않기를 바랍니다. 앞서 언급했듯이 주로 어디에서 사용할 기능을 아는 것이므로 설명 할 수있는 내용이 많지 않습니다.

그러나 현재 셰이더는 그림자를받을 수 있지만 ShadowCaster 패스를 포함하지 않으므로 그림자를 드리 우지 않습니다. 이것은 다음 섹션에서 다룰 것입니다.

'Unity > Shader' 카테고리의 다른 글

Summary of Built-in vs URP differences  (0) 2021.01.25
ShadowCaster & DepthOnly Passes  (0) 2021.01.25
Lighting Introduction  (0) 2021.01.22
Keywords & Shader Variants  (0) 2021.01.22
Fragment Shader  (0) 2021.01.21
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30