배경.
다음 다이어그램은 대부분의 픽셀이 완전히 불투명하고 뒤에 있는 개체가 가려지지만 주변 픽셀이 투명하다는 것을 나타냅니다.
여기서 PreZOff, PreZOn은 OpenGL ES 기반 패키지입니다. On은 Transparent DepthPre Pass를 켰다고, Off는 Transparent DepthPre Pass를 닫았다고 밝혔다.
기술 원리.
그런 다음 이러한 투명 오브젝트를 실제로 렌더링할 때 Early-Z 메커니즘으로 인해 계산이 필요하지 않은 픽셀을 조기에 제거하여 OverDraw를 줄일 수 있습니다.
예를 들어 다음 시나리오와 같이 두 개의 투명 오브젝트가 있습니다.
위의 예에서 볼 수 있듯이 Transparent DepthPre Pass를 켜면 녹색 투명 오브젝트를 그릴 때 많은 수의 픽셀이 Depth Test에서 제외됩니다.
이러한 재질은 모두 투명 재질이며 Early-Z를 파괴하지 않으므로 Early-Z를 사용하여 제거되므로 Pixel Shader가 실행되지 않았습니다.
Use Feature Level.
현재 Pipeline에서 ForwardRenderere.asset 를찾아 새 Renderer Features를 추가합니다.
Renderer Feature의 Queue를 Transparent로 변경합니다.
TransparentPreZ Pass에서 실제 렌더링 시 투명도가 계산된 방식에 따라 다시 계산한 다음 지정된 임계값에 따라 discard가 필요한지 여부를 결정합니다.
Shader "Custum/TransparentPreZ"
{
Properties
{
_BaseMap("Texture", 2D) = "white" {}
_BaseColor("Color", Color) = (1, 1, 1, 1)
_Cutoff("AlphaCutout", Range(0.0, 1.0)) = 0.5
// BlendMode
[HideInInspector] _Surface("__surface", Float) = 0.0
[HideInInspector] _Blend("__blend", Float) = 0.0
[HideInInspector] _AlphaClip("__clip", Float) = 0.0
[HideInInspector] _SrcBlend("Src", Float) = 1.0
[HideInInspector] _DstBlend("Dst", Float) = 0.0
[HideInInspector] _ZWrite("ZWrite", Float) = 1.0
[HideInInspector] _Cull("__cull", Float) = 2.0
// Editmode props
[HideInInspector] _QueueOffset("Queue offset", Float) = 0.0
// ObsoleteProperties
[HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
[HideInInspector] _Color("Base Color", Color) = (0.5, 0.5, 0.5, 1)
[HideInInspector] _SampleGI("SampleGI", float) = 0.0 // needed from bakedlit
}
SubShader
{
Tags { "RenderType" = "Opaque" "IgnoreProjector" = "True" "RenderPipeline" = "UniversalPipeline" }
LOD 100
Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]
Pass
{
Name "Unlit"
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex vert
#pragma fragment frag
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _ALPHAPREMULTIPLY_ON
// -------------------------------------
// Unity defined keywords
#pragma multi_compile_fog
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float2 uv : TEXCOORD0;
float fogCoord : TEXCOORD1;
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings vert(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);
output.vertex = vertexInput.positionCS;
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
output.fogCoord = ComputeFogFactor(vertexInput.positionCS.z);
return output;
}
half4 frag(Varyings input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
half2 uv = input.uv;
half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);
half3 color = texColor.rgb * _BaseColor.rgb;
half alpha = texColor.a * _BaseColor.a;
AlphaDiscard(alpha, _Cutoff);
#ifdef _ALPHAPREMULTIPLY_ON
color *= alpha;
#endif
color = MixFog(color, input.fogCoord);
return half4(color, alpha);
}
ENDHLSL
}
Pass
{
Tags{"LightMode" = "DepthOnly"}
ZWrite On
ColorMask 0
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma target 2.0
#pragma vertex DepthOnlyVertex
#pragma fragment DepthOnlyFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature _ALPHATEST_ON
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
ENDHLSL
}
// This pass it not used during regular rendering, only for lightmap baking.
Pass
{
Name "Meta"
Tags{"LightMode" = "Meta"}
Cull Off
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex UniversalVertexMeta
#pragma fragment UniversalFragmentMetaUnlit
#include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitMetaPass.hlsl"
ENDHLSL
}
Pass
{
Name "TransparentPreZ"
Tags{"LightMode" = "TransparentPreZ"}
ColorMask 0
ZWrite On
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex vert
#pragma fragment frag
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _ALPHAPREMULTIPLY_ON
// -------------------------------------
// Unity defined keywords
#pragma multi_compile_fog
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float2 uv : TEXCOORD0;
float fogCoord : TEXCOORD1;
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings vert(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);
output.vertex = vertexInput.positionCS;
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
output.fogCoord = ComputeFogFactor(vertexInput.positionCS.z);
return output;
}
half4 frag(Varyings input) : SV_Target
{
half2 uv = input.uv;
half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);
half3 color = texColor.rgb * _BaseColor.rgb;
half alpha = texColor.a * _BaseColor.a;
if (alpha < 0.95) discard;
return half4(color, alpha);
}
ENDHLSL
}
}
FallBack "Hidden/Universal Render Pipeline/FallbackError"
CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.UnlitShader"
}
뎁스 프리패스의 필수 요소
뎁스 프리패스
깊이 경로만 먼저 그려서 조각 처리를 가능하게 하는 기법으로, 조각(픽셀) 처리를 할 때 미리 처리된 깊이를 사용하는 것만으로도 알파 테스트의 문제를 해결할 수 있다고 합니다. 캐시의 무결성을 보장할 수 없다는 문제도 해결하셨나요...? 이 기능을 활성화하면 픽셀 처리 전에 섀도잉 후 깊이만 먼저 그리는 패스가 있습니다.
물론 이 만병통치약 같은 뎁스 프리패스는 기본적으로 켜져 있지 않습니다. 기본값이 꺼져 있기 때문에 이전 알파 테스트에서 여전히 문제가 발생할 수 있지만 일부 특이한 경우에는 뎁스 프리패스를 켤 수 있습니다. 이러한 특이한 경우 중 하나는 '뎁스 텍스처' 옵션으로, 부드러운 파티클, 리플 뎁스 페이드 기능 등에 사용해야 합니다.
💡 이 옵션을 활성화하면 불투명을 그리기 전에 뎁스만 먼저 그리기 때문에 뎁스 프리패스가 실행되어 알파 테스트가 무거워지는 문제를 해결할 수 있다고 합니다.
이 기능을 끄면 뎁스 프리패스가 사라지고 알파 테스트가 무거워지는 문제가 다시 발생합니다. 캐시를 사용할 수 없고 메모리에 계속 접근해서 원래의 깊이를 읽어야 합니다.
사양을 낮게 만들면 > 옵션을 끄고 무겁게 만들 수 있다고 합니다.
최신 기술 동향
URP가 등장한 이후 많은 변화가 있었습니다. 동시에 하드웨어의 세계도 바뀌었습니다.
💡 알파 테스트의 가장 큰 난제는 '깊이 처리' 문제와 'TBDR 깨기'였지만, 최근에는 TBDR 문제가 해결되고 있습니다.
PowerVR 칩만 이 기술을 사용하고 있었는데, 아이폰7 이후 주요 휴대폰 업체들이 더 이상 이 칩을 사용하지 않기 때문이죠... 즉, TBDR은 더 이상 하드웨어를 거의 사용하지 않습니다.
'GRAPHICS PROGRAMMING' 카테고리의 다른 글
[올드스쿨]VBO WITH RE STRUCTURE EDGE. (0) | 2024.07.12 |
---|---|
[최적화]IPC with GPU. (1) | 2024.07.12 |
캐릭터 렌더링 표현 기법 통합본 연재 계획. (0) | 2023.09.06 |
Essentials of Mobile hardware (0) | 2023.08.29 |
[번역]GPU DRIVEN RENDERING OVERVIE (0) | 2023.07.14 |