
Bloom 소개
Bloom은 밝은 영역에서 빛이 번지는 효과를 시뮬레이션하는 포스트 프로세싱이다. HDR 렌더링 환경에서 시각적 품질을 크게 끌어올리는 핵심 이펙트로, 현대 게임에서 거의 필수적으로 사용된다.
일반적인 Bloom의 처리 흐름은 다음과 같다:
Camera Color → Prefilter(밝기 추출) → DownSample(반복 축소) → Blur(블러) → UpSample(반복 확대) → Composite(합성)
밝은 픽셀을 추출하고, 해상도를 단계적으로 낮추며 블러를 적용한 뒤, 다시 단계적으로 원본 해상도까지 합성해 올라가는 구조다. Unity URP의 기본 Bloom도 이 흐름을 따르며, Progressive DownSample/UpSample 방식으로 최대 16개의 렌더 패스를 사용한다.
모바일에서의 문제: SetRenderTarget의 실제 비용
Bloom은 화면 전체를 대상으로 하는 풀스크린 이펙트다. 해상도에 비례해 비용이 증가하고, 여러 단계의 DownSample/UpSample 과정에서 매 패스마다 SetRenderTarget(SRT)이 호출된다.
SetRenderTarget이란
SetRenderTarget은 GPU가 렌더링 결과를 기록할 대상 텍스처를 전환하는 명령이다. CPU 관점에서는 단순한 상태 변경처럼 보이지만, GPU 내부에서는 훨씬 복잡한 동작이 일어난다.
TBDR 아키텍처에서 SRT의 동작
모바일 GPU(Apple GPU, Mali, Adreno 등)는 Tile-Based Deferred Rendering(TBDR) 아키텍처를 사용한다. TBDR에서 렌더링은 화면을 작은 타일(보통 16×16 ~ 32×32 픽셀)로 분할하여 처리한다. 각 타일은 GPU 코어 내부의 타일 메모리(On-Chip Memory) 에서 렌더링되며, 이 메모리는 시스템 메모리(DRAM)보다 수십 배 빠르고 전력 소모가 적다.
SRT가 호출되면 GPU는 다음 과정을 수행한다:
[현재 렌더 타겟의 타일 메모리]
↓ Store (On-Chip → DRAM)
[시스템 메모리에 기록]
↓ 렌더 타겟 전환
[새 렌더 타겟의 이전 내용]
↓ Load (DRAM → On-Chip)
[새 렌더 타겟의 타일 메모리에서 렌더링 시작]
- Store: 현재 렌더 타겟의 타일 메모리 내용을 시스템 메모리(DRAM)로 기록한다
- Load: 새 렌더 타겟의 기존 내용을 시스템 메모리에서 타일 메모리로 읽어온다
이 Store/Load 사이클이 디바이스 메모리 대역폭을 소비하는 핵심 원인이다. 타일 메모리 내에서의 읽기/쓰기는 거의 무비용이지만, DRAM과의 데이터 전송은 높은 전력을 소모하고 대역폭 병목을 유발한다.
왜 대역폭이 중요한가
데스크톱 GPU에서는 대역폭에 여유가 있어 SRT 전환의 비용이 상대적으로 작다. 그러나 모바일에서는 사정이 다르다:
- 전력 제한: DRAM 접근은 ALU 연산 대비 수십 배의 에너지를 소모한다. 대역폭 증가는 곧 발열과 배터리 소모 증가를 의미한다
- 서멀 스로틀링: 발열이 임계값을 넘으면 GPU 클럭이 강제로 낮아져 전체 프레임 레이트가 떨어진다
- 공유 대역폭: 모바일 SoC에서 CPU와 GPU는 동일한 시스템 메모리와 대역폭을 공유한다
URP 기본 Bloom은 DownSample과 UpSample을 합쳐 최대 16번의 SRT 호출이 발생한다. 매 호출마다 Store → Load 사이클이 반복되며, 이 과정에서 소비되는 대역폭은 프레임당 누적된다.
Bloom 최적화가 어려운 이유
Bloom 최적화를 논의할 때 가장 먼저 떠오르는 접근은 블러 알고리즘 자체를 변경하는 것이다. Dual Filter, Kawase Blur 등이 대표적이다. 이들은 적은 샘플 수로 넓은 범위의 블러를 근사하여 연산 비용을 줄인다.
그러나 실제 프로젝트에서 이 접근을 채택하기는 쉽지 않다.
품질 차이.
URP 기본 Bloom은 Progressive Gaussian 기반의 블러를 사용하며, 이미 검증된 시각적 품질을 제공한다. Dual Filter나 Kawase Blur로 전환하면 블러의 특성(falloff, 형태)이 달라지고, 특히 밝은 광원 주변의 번짐 패턴이 눈에 띄게 변한다. 아트 디렉션 관점에서 이 차이를 수용하기 어려운 경우가 많다.
프로젝트 전반에 미치는 영향.
Bloom은 게임의 전체 비주얼 톤을 결정하는 요소 중 하나다. 이미 Bloom 기반으로 라이팅, 이펙트, UI 연출이 세팅된 프로젝트에서 블러 알고리즘을 변경하면 모든 씬의 룩이 달라진다. 프로젝트 초반이 아니라면 사실상 선택지에서 제외된다.
결국 필요한 것은 기존과 동일한 블러 품질을 유지하면서, 렌더링 파이프라인 레벨에서 비용을 줄이는 접근이다.

Mipmap Bloom 접근
Mipmap Bloom의 핵심 아이디어는 단순하다: 여러 해상도의 블러 결과를 별도의 렌더 타겟으로 분리하지 않고, 하나의 렌더 타겟 안에 영역을 나눠 배치하는 것이다.
이 기법의 이름을 "Mipmap Bloom"이라 했지만, 엄밀히 말하면 GPU의 하드웨어 Mipmap(텍스처 LOD 체인)을 사용하는 것은 아니다. 실제 동작은 여러 해상도의 블러 결과를 "하나의 렌더 타겟에 아틀라스"처럼 패킹하는 방식이므로, "Atlas Bloom"이 더 정확한 표현이다. 다만 각 해상도 단계가 점진적으로 축소되는 구조가 Mipmap 체인과 개념적으로 유사하고, DownSample로 만든 여러 해상도 레벨을 다루는 패스를 코드에서 "MipmapBlur"로 명명했기 때문에 Mipmap Bloom이라는 이름을 그대로 사용한다.

URP Bloom vs Mipmap Bloom 구조 비교
URP 기본 Bloom (URP 버전마다 차이 있음) :
[SRT] DownSample 0 → [SRT] DownSample 1 → ... → [SRT] DownSample 7
↓
[SRT] UpSample 7 ← [SRT] UpSample 6 ← ... ← [SRT] UpSample 0
- 총 16개 패스, 16번의 SetRenderTarget 호출
- 매 패스마다 Store → Load 사이클 발생

Mipmap Bloom:
[SRT] Prefilter → [SRT] DownSample ×5 → [SRT] MipmapBlur H → [SRT] MipmapBlur V → [SRT] Composite
- 총 9개 패스, 9번의 SetRenderTarget 호출
- MipmapBlur H/V는 하나의 렌더 타겟 안에서 4개 Mip Level을 SetViewport로 영역 분할하여 렌더링

아틀라스 기반 Mipmap 패킹
핵심은 MipmapBlur 단계에 있다. 4개 Mip Level의 블러를 각각 별도 RT에 그리는 대신, 하나의 RT를 아틀라스처럼 사용한다.
┌─────────────┬───────┐
│ │ │
│ Mip 0 │ Mip 1 │
│ (가장 큼) │ │
│ ├───┬───┤
│ │M2 │ │
├─────────────┤───┤M3 │
│ │ │ │
└─────────────┴───┴───┘
각 Mip Level은 SetViewport로 렌더링 영역을 제한하여 같은 RT 내 다른 영역에 그린다. 이렇게 하면:
- MipmapBlur Horizontal: 1번의 SRT → 4개 Mip Level을 순차적으로 SetViewport로 영역을 바꿔가며 블러
- MipmapBlur Vertical: 1번의 SRT → 동일하게 4개 Mip Level을 영역별로 블러
- Composite: 아틀라스에서 각 Mip Level의 UV 영역을 계산하여 한 번에 샘플링 & 합산
기존에 Mip Level 수 × 2(H+V) = 8번의 SRT가 필요했던 블러 단계가 단 2번의 SRT로 줄어든다.
URP Bloom vs Mipmap Bloom 비주얼 비교

UV Tiling/Offset과 Constant Buffer 설계
아틀라스 내에서 각 Mip Level의 정확한 위치를 참조하려면 UV 변환 데이터가 필요하다. 이 데이터를 셰이더에 상수로 하드코딩하지 않고 CPU에서 계산하여 Constant Buffer로 전달하는 이유가 있다.
왜 셰이더 상수가 아닌 CPU 전송인가
아틀라스의 UV 레이아웃은 화면 해상도와 품질 설정에 따라 달라진다. Mip Level별 샘플 카운트를 조절하면 블러 품질과 성능 사이의 트레이드오프를 런타임에 제어할 수 있다:
// 품질 설정에 따라 Mip Level별 샘플 수가 달라진다
private static readonly int[] MipmapSampleCounts = { 4, 4, 8, 12 }; // Normal
private static readonly int[] MipmapSampleCounts_High = { 8, 12, 16, 32 }; // High Quality
해상도 변경, 품질 옵션 전환, 디바이스별 설정 분기 등에 대응하려면 이 데이터가 동적이어야 한다. 셰이더에 상수로 고정하면 이 유연성을 잃는다.
Burst 컴파일을 통한 CPU 연산 최적화
UV Tiling/Offset, Clamp 값, Gaussian 블러의 offset/weight는 매 프레임 계산되는 것은 아니지만, 해상도나 설정이 바뀔 때 재계산이 필요하다. 이 연산을 효율적으로 수행하기 위해 Unity Burst Compiler를 활용한다:
[BurstCompile]
internal static unsafe void GetGaussianUVOffsets(
float4* refBlurDatas, int startIndex, int sampleCount, int width, int height)
{
float dx = 1.0f / width;
float dy = 1.0f / height;
float half = (sampleCount - 1) * 0.5f;
for (int i = 0; i < sampleCount; i++)
{
float offset = i - half;
float4 data = refBlurDatas[startIndex + i];
refBlurDatas[startIndex + i] = new float4(offset * dx, offset * dy, data.z, data.w);
}
}
[BurstCompile]
internal static unsafe void GetGaussianWeights(
float4* refBlurDatas, int startIndex, int sampleCount, float sigma)
{
// Gaussian weight 계산 — Burst로 네이티브 수준 성능
float twoSigmaSq = 2f * sigma * sigma;
float sum = 0f;
float center = (sampleCount - 1) * 0.5f;
for (int i = 0; i < sampleCount; i++)
{
float x = i - center;
float w = math.exp(-(x * x) / twoSigmaSq);
float4 data = refBlurDatas[startIndex + i];
refBlurDatas[startIndex + i] = new float4(data.x, data.y, w, data.w);
sum += w;
}
// 정규화
for (int i = 0; i < sampleCount; i++)
{
float4 data = refBlurDatas[startIndex + i];
refBlurDatas[startIndex + i] = new float4(data.x, data.y, data.z / sum, data.w);
}
}
Burst 컴파일은 C# 코드를 LLVM 기반 네이티브 코드로 변환하여, NativeArray를 통한 unsafe 포인터 연산을 C/C++ 수준의 성능으로 처리한다. 계산된 모든 데이터(샘플 카운트, blur offset/weight, UV tiling/offset, UV clamp)는 하나의 ComputeBuffer에 패킹되어 단일 SetGlobalConstantBuffer 호출로 GPU에 전송된다:
cmd.SetGlobalConstantBuffer(
_mipmapData.ConstantsBuffer,
ShaderPropertyId.MipmapDataConstants,
0, _mipmapData.ConstantsBufferSize);
GPU 셰이더에서는 이 Constant Buffer를 그대로 참조한다:
CBUFFER_START(MipmapDataConstants)
uint4 _BloomMipmapSampleCount; // Mip Level별 샘플 수
float4 _BloomMipmapBlurData[MIPMAP_COUNT * 32u]; // Gaussian offset/weight
float4 _BloomMipmapTilingOffset[MIPMAP_COUNT]; // UV Tiling & Offset
float4 _BloomMipmapUVClamp[MIPMAP_COUNT]; // UV Clamp 범위
CBUFFER_END
- _BloomMipmapTilingOffset: Mip Level 영역의 UV 스케일과 오프셋. Composite 및 Vertical Blur 패스에서 전체 화면 UV를 해당 Mip 영역의 UV로 변환하는 데 사용한다.
- _BloomMipmapUVClamp: 텍스처 샘플링 시 인접 Mip 영역의 픽셀을 침범하지 않도록 UV를 제한한다. 아틀라스 패킹의 필수 요소다.
이 구조의 장점은 CPU 연산은 Burst로 최적화, GPU 전송은 단일 Constant Buffer로 최소화, 런타임 품질 조절은 유연하게 유지하는 세 가지를 동시에 달성한다는 점이다.
SetRenderTarget 제거의 효과
이 접근이 효과적인 이유는 앞서 설명한 TBDR 아키텍처의 Store/Load 사이클과 직결된다.
URP BloomMipmap Bloom변화
| SetRenderTarget 호출 | 16회 | 9회 | -43.8% |
| 디바이스 메모리 Read | 5.10 MiB | 4.30 MiB | -15.7% |
| 디바이스 메모리 Write | 2.05 MiB | 0.93 MiB | -54.5% |
| 디바이스 메모리 총량 (R+W) | 7.15 MiB | 5.23 MiB | -26.9% |
| 렌더 패스 수 | 16 | 9 | -43.8% |
측정 환경: Xcode GPU Frame Capture, iPhone 12 Pro Max 2778x1284
SRT 호출이 줄어들면 타일 Store/Load 사이클이 줄고, 이는 디바이스 메모리 Write의 54.5% 감소로 직접 확인된다. 모바일에서 메모리 대역폭은 전력 소모의 주요 원인이므로, 이 감소는 동일한 프레임 예산 내에서 다른 렌더링 작업에 더 많은 여유를 확보해준다.
마이크로 최적화: UV 연산을 Vertex Stage에서 처리
풀스크린 포스트 프로세싱에서 흔히 간과되는 최적화가 있다. Fragment Shader에서 수행하는 UV 관련 연산을 Vertex Shader로 옮기는 것이다.
풀스크린 삼각형은 화면 전체를 덮는 3개의 정점만 사용한다. Fragment Shader는 화면의 모든 픽셀(수십만~수백만 회)에 대해 호출되지만, Vertex Shader는 단 3번만 호출된다. GPU 하드웨어는 Vertex Shader 출력을 Fragment 단계로 넘길 때 자동으로 보간(interpolation)해주므로, 선형 변환(Tiling/Offset 적용 등)은 Vertex에서 처리해도 결과가 동일하다.
Composite 패스 예시:
// Vertex Shader — 3회 호출
Varyings FullscreenVert(Attributes input)
{
// ...
// 4개 Mip Level의 UV를 미리 계산하여 varying으로 전달
output.uv_0.xy = uv * _BloomMipmapTilingOffset[0].xy + _BloomMipmapTilingOffset[0].zw;
output.uv_0.zw = uv * _BloomMipmapTilingOffset[1].xy + _BloomMipmapTilingOffset[1].zw;
output.uv_1.xy = uv * _BloomMipmapTilingOffset[2].xy + _BloomMipmapTilingOffset[2].zw;
output.uv_1.zw = uv * _BloomMipmapTilingOffset[3].xy + _BloomMipmapTilingOffset[3].zw;
return output;
}
만약 이 연산을 Fragment Shader에서 수행했다면 해상도에 비례하는 횟수(예: 189,120회)만큼 곱셈-덧셈이 반복된다. Vertex로 옮기면 이 연산은 3회로 줄고, 나머지는 하드웨어 보간기가 처리한다.
동일한 패턴이 MipmapBlur Vertical 패스에서도 적용된다:
Varyings FullscreenVert(Attributes input)
{
// ...
// Mip Level 영역의 UV Tiling/Offset 적용
const float4 tilingOffset = _BloomMipmapTilingOffset[_MipmapCount];
output.uv.xy = output.uv.xy * tilingOffset.xy + tilingOffset.zw;
// UV Clamp 값도 varying으로 전달
output.offsetClamp = _BloomMipmapUVClamp[_MipmapCount];
return output;
}
Bloom 같은 풀스크린 이펙트에서 이 패턴은 Fragment에서 상수 버퍼 접근 + 산술 연산 수십만 회를 Vertex 3회 + 하드웨어 보간으로 대체하는 것이므로, ALU 부하와 버퍼 대역폭 양쪽 모두에서 이득이다.
성능 측정
Xcode GPU Frame Capture를 통해 URP 기본 Bloom과 Mipmap Bloom의 GPU 성능을 비교 측정했다. 주요 지표 두 가지 — GPU 연산 비용(Execution Cost, ALU) 과 디바이스 메모리 대역폭 — 을 중심으로 분석한다.
측정 환경
- 디바이스: iPhone 12 Pro Max
- 해상도: 2778×1284 (Native)
- 측정 도구: Xcode GPU Frame Capture
- 비교 범위: Bloom + UberPostProcess 전체 (포스트 프로세싱 파이프라인 단위)
측정 지표 설명
- Total GPU Time: 해당 구간의 절대 GPU 소요 시간
- Execution Cost: 전체 프레임 GPU 시간에서 차지하는 비율
- FS ALU Instructions: Fragment Shader에서 실행된 ALU(산술 논리 연산) 명령어 수. 셰이더의 연산 복잡도를 나타낸다
종합 성능 비교 (Bloom + UberPostProcess)
지표URP BloomMipmap Bloom차이
| Total GPU Time | 3.33 ms | 3.01 ms | −0.32 ms (−9.61%) |
| Execution Cost | 34.74% | 32.70% | −5.87% |
| FS ALU Instructions | 385,469,696 | 337,791,232 | −47,678,464 (−12.37%) |
디바이스 메모리 대역폭 비교
대역폭 비교에는 전송률(GiB/s)이 아닌 DRAM 전송 총량(Bytes Read + Written) 을 사용한다. 전송률은 패스의 실행 시간에 의존하여 합산 시 의미가 왜곡되지만, 총량은 시간과 무관하게 DRAM에 가한 실제 부담을 보여주며 전력 소모와 직접 비례한다.
Bloom 패스만 비교 (SRT 최적화의 직접 효과):
구분URP Bloom (16 pass)Mipmap Bloom (9 pass)차이
| Device Memory Read | 5.10 MiB | 4.30 MiB | −0.80 MiB (−15.7%) |
| Device Memory Write | 2.05 MiB | 0.93 MiB | −1.12 MiB (−54.5%) |
| 총 전송량 (R+W) | 7.15 MiB | 5.23 MiB | −1.92 MiB (−26.9%) |
Write 감소가 두드러진다. SRT 호출 16회 → 9회 감소로 타일 Store 횟수가 줄어든 직접적인 결과다.
분석
GPU Time −9.61%. Bloom + UberPostProcess 전체 구간에서 0.32ms가 감소했다. 60fps 기준 프레임 예산 16.67ms에서 약 2%에 해당하는 여유를 확보한 셈이다.
ALU Instructions −12.37%. 약 4,768만 명령어가 줄었다. Mipmap Bloom은 UpSample 체인(8패스)을 제거하고 Composite 1회로 대체했기 때문에, 해당 구간의 연산이 통째로 사라진다. UberPostProcess에서 MipmapBloom 텍스처를 단일 샘플링하는 방식도 ALU 감소에 기여한다.
Bloom Device Memory Write −54.5%. SRT 호출 감소의 가장 직접적인 성과다. TBDR에서 SRT마다 발생하는 타일 Store가 7회분 줄어들면서, Bloom 구간의 DRAM 쓰기 부담이 절반 이하로 감소했다. 모바일에서 DRAM 접근은 전력 소모의 주요 원인이므로, 이 감소는 서멀 마진 확보에 직접 기여한다.
요약
Mipmap Bloom은 GPU Time 9.61% 감소, ALU 12.37% 감소를 달성했다. 대역폭 관점에서는 Bloom 파이프라인 자체의 DRAM 전송량이 26.9% 감소(특히 Write −54.5%)되었으며, 이는 모바일 환경에서 장시간 플레이 시 발열 감소와 배터리 수명 연장에 기여한다.
DrawInstance 테스트
MipmapBlur 패스에서 4개 Mip Level을 SetViewport로 순차 렌더링하는 대신, GPU Instancing으로 1 draw call에 처리하는 방식도 테스트했다.
MipmapBlur 패스에서 SetRenderTarget이 변동되지 않기 때문에 시도할 수 있는 방법이다.

구현 방식
Instanced 모드에서는 DrawProcedural의 인스턴스 카운트를 4로 설정하여 Mip Level마다 하나의 인스턴스를 할당한다:
using (new ProfilingScope(cmd, ProfilingSamplers.MipmapBlurH))
{
cmd.SetRenderTarget(data.mipmapHoriTexture, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
cmd.ClearRenderTarget(false, true, Color.black);
cmd.SetGlobalTexture(ShaderPropertyId.MipmapSampleTexture0, data.downSampleTextures[1]);
cmd.SetGlobalTexture(ShaderPropertyId.MipmapSampleTexture1, data.downSampleTextures[2]);
cmd.SetGlobalTexture(ShaderPropertyId.MipmapSampleTexture2, data.downSampleTextures[3]);
cmd.SetGlobalTexture(ShaderPropertyId.MipmapSampleTexture3, data.downSampleTextures[4]);
cmd.DrawProcedural(Matrix4x4.identity, data.material, PassId.MipmapBlurHori_Instanced, MeshTopology.Quads, 4, ConstParams.MipmapLevel);
}
Vertex Shader에서 SV_InstanceID를 통해 각 인스턴스가 어떤 Mip Level에 해당하는지 판별하고, UV Tiling/Offset을 적용하여 NDC 좌표를 영역별로 배치한다:
Varyings FullscreenVert_Instance(Attributes input, uint instanceID : SV_InstanceID)
{
const uint mipmapLevel = instanceID;
const float4 viewportRect = _BloomMipmapTilingOffset[mipmapLevel];
output.positionCS.xy = output.positionCS.xy * viewportRect.xy + viewportRect.zw;
output.positionCS.xy = output.positionCS.xy * float2(2.0, -2.0) + float2(-1.0, 1.0);
// ...
}
추가된 비용
Instanced 모드에서는 SetViewport 대신 셰이더 내에서 영역을 결정하므로, 두 가지 추가 비용이 발생한다:
1. 텍스처 선택 분기문.
SetViewport 방식에서는 C# 코드에서 cmd.SetGlobalTexture로 해당 Mip Level의 DownSample 텍스처를 직접 바인딩한다. Instanced 방식에서는 4개 텍스처를 모두 바인딩해두고 Fragment Shader에서 SV_InstanceID에 따라 선택해야 한다:
half3 SampleInputTexture(uint mipmapLevel, float2 uv)
{
[branch]
if (mipmapLevel == 0) return SAMPLE_MIPMAP_TEXTURE(_MipmapSampleTexture0);
else if (mipmapLevel == 1) return SAMPLE_MIPMAP_TEXTURE(_MipmapSampleTexture1);
else if (mipmapLevel == 2) return SAMPLE_MIPMAP_TEXTURE(_MipmapSampleTexture2);
else return SAMPLE_MIPMAP_TEXTURE(_MipmapSampleTexture3);
}
이 분기문은 Fragment Shader의 모든 픽셀에서 실행된다. GPU에서 동적 분기는 warp/SIMD 그룹 내 divergence를 유발할 수 있고, 사용하지 않는 텍스처 슬롯까지 바인딩 상태를 유지해야 한다.
2. Viewport 대체 연산.
하드웨어 SetViewport가 무비용으로 처리하던 클리핑과 좌표 변환을 셰이더에서 직접 수행해야 한다.
측정 결과
Instanced 모드를 적용한 별도 캡처에서 Bloom Execution Cost가 SetViewport 방식 대비 상승하는 것을 확인했다. 텍스처 선택 분기와 좌표 변환의 추가 연산이 드로우콜 감소(4→1)의 이점을 상쇄하고도 남는다.
결론: 안 하는 게 낫다
Bloom의 주요 비용 구성을 보면, 드로우콜 오버헤드보다 블러 연산 자체의 ALU 및 텍스처 샘플링 비용이 지배적이다. MipmapBlur 패스의 드로우콜은 SetViewport 방식에서도 Mip Level당 1회씩 총 4회에 불과하다.
GPU Instancing으로 4 draw call → 1 draw call로 줄여봐야 절감되는 드로우콜 오버헤드는 미미한 반면, 텍스처 선택 분기와 좌표 변환 연산이 모든 픽셀에서 추가되어 오히려 전체 비용이 증가한다.
Bloom과 같이 draw call 수가 적고 픽셀당 연산 비용이 높은 풀스크린 이펙트에서는 GPU Instancing의 이점이 거의 없다. 드로우콜 최적화는 수백~수천 개의 메시를 그리는 지오메트리 패스에서 효과적이지, 패스당 풀스크린 삼각형 1장을 그리는 포스트 프로세싱에서는 적용 대상이 아니다.
'Unity > TA' 카테고리의 다른 글
| Unity Light Probe Group Volume(Multiple Light Probe Group) (1) | 2024.11.28 |
|---|---|
| Unity Light Probe with out Lightmap (1) | 2024.07.22 |
| Unreal VertexAnimationTool.ms Unity 호환 버전 (0) | 2024.06.05 |
| URP GPU Instancing와 SRP Batcher 성능 비교 (1) | 2024.04.24 |
| 단일 Material 공유가 SRPBatcher 성능에 영향을 주는가? (2) | 2024.04.15 |
WRITTEN BY
- CatDarkGame
Technical Artist dhwlgn12@gmail.com


