TECHARTNOMAD | TECHARTFLOWIO.COM

UNREAL ENGINE

CommonViewUniformBuffer 에 대해서...

jplee 2025. 3. 23. 23:55

CommonViewUniformBuffer.ush

// Copyright Epic Games, Inc. All Rights Reserved.

/*=============================================================================
	CommonViewUniformBuffer.usf: Common view uniform buffer specifics
=============================================================================*/

#pragma once

/*
* @return tan(View.FieldOfViewWideAngles * .5)
*/
float2 GetTanHalfFieldOfView()
{
	return View.TanAndInvTanHalfFOV.xy;
}

float2 GetPrevTanHalfFieldOfView()
{
	return View.PrevTanAndInvTanHalfFOV.xy;
}

// might be used by Custom material expressions (still best to wrap the custom node in a material function)
// @return 1 / tan(View.FieldOfViewWideAngles * .5)
float2 GetCotanHalfFieldOfView()
{
	return View.TanAndInvTanHalfFOV.zw;
}

// might be used by Custom material expressions (still best to wrap the custom node in a material function)
// @return previous 1 / tan(View.FieldOfViewWideAngles * .5)
float2 GetPrevCotanHalfFieldOfView()
{
	return View.PrevTanAndInvTanHalfFOV.zw;
}

// Return the index of the frame.
uint GetPowerOfTwoModulatedFrameIndex(uint Pow2Modulus)
{
	// Bit masking of an uniform parameter is a scalar operation on modern hardware.
	return View.StateFrameIndex & uint(Pow2Modulus - 1);
}

CommonViewUniformBuffer.usf는 Unreal Engine의 렌더링 파이프라인에서 공통적으로 사용되는 뷰(View) 데이터를 관리하며, 이를 통해 다양한 렌더링 연산을 효율적으로 처리합니다. 이 파일에 정의된 함수는 카메라의 시야각(Field of View)프레임 간 데이터를 처리하기 위한 목적으로 설계되어 있습니다.
return 되는 값들은 SceneView.cpp 에서 실제 그현 되어 있습니다.

FVector4f TanAndInvTanFOV = InViewMatrices.GetTanAndInvTanHalfFOV();
ViewUniformShaderParameters.TanAndInvTanHalfFOV = TanAndInvTanFOV;

이런 식으로.... 


float2 GetTanHalfFieldOfView()
{
	return View.TanAndInvTanHalfFOV.xy;
}

카메라의 수평(horizontal)수직(vertical) 시야각(Field of View)의 절반 값에 대한 탄젠트(tan)를 반환합니다.

  • 반환값:
    • tan(View.FieldOfViewWideAngles.x * 0.5) (수평 FOV)
    • tan(View.FieldOfViewWideAngles.y * 0.5) (수직 FOV)

실제 계산부를 볼까요?
SceneView.h

//Used for initializing the View Uniform Buffer TanAndInvTanHalfFOV variable without repeated calls to IsPerspectiveProjection
inline FVector4f GetTanAndInvTanHalfFOV() const
{
    //No concept of FOV for orthographic projection so only return perspective related values or 1.0f
    if (IsPerspectiveProjection())
    {
       return FVector4f(static_cast<float>(InvProjectionMatrix.M[0][0]), //ClipToView[0][0] - X axis
                    static_cast<float>(InvProjectionMatrix.M[1][1]), //ClipToView[1][1] - Y axis
                    static_cast<float>(ProjectionMatrix.M[0][0]), //ViewToClip[0][0] - 1/X axis
                    static_cast<float>(ProjectionMatrix.M[1][1])); //ViewToClip[1][1] - 1/Y axis
    }
    return FVector4f(1.0f, 1.0f, 1.0f, 1.0f);
}

 
직교투영이 아닐때 즉 원근투영일 경우 InvProjectionMatrix와 ProjectionMatrix를 활용하여 FOV와 관련된 값을 계산합니다. 카메라의 X 축과 Y 축 값에 대해 각각 tan(FOV/2)와 1/tan(FOV/2) 데이터를 포함합니다. 
X, Y: Clip 공간(뷰 공간으로 변환 내재)에서 축별 tan(FOV/2) 값 : InvProjectionMatrix.M[0][0] 및 InvProjectionMatrix.M[1][1]은 뷰 프로젝션 역행렬에서 추출 합니다.
Z, W: View 공간(클립으로 변환 내재)에서 축별 1/tan(FOV/2) 값 : ProjectionMatrix.M[0][0] 및 ProjectionMatrix.M[1][1]은 프로젝션 행렬에서 추출 합니다.

GetTanAndInvTanHalfFOV 함수는 화면 공간에서 3D 작업을 위한 기본 투영 데이터를 제공하며, 카메라가 원근 투영일 경우 tan(FOV/2)와 1/tan(FOV/2) 값을 반환해 향후 렌더링 계산의 기초를 제공합니다. 이는 화면 왜곡 처리, 객체 깊이 계산 및 특정 렌더링 최적화 작업에 활용되는 중요한 함수입니다.

렌더링 공간 변환: tan(FOV)는 3D 공간의 뷰 프로젝션 매트릭스를 생성하기 위한 필수 요소로, 화면 크기와 카메라의 FOV를 계산하는 데 사용됩니다. 포스트 프로세싱 쉐이더: 화면 공간(screen space)의 요소를 월드 공간으로 복원하거나 리프로젝션 시 사용됩니다. 광선 추적(ray tracing): 특정 픽셀에서 렌더링된 광선의 방향 계산 시 활용됩니다.

메타휴먼 눈물셈 가우시안 블러처리 중에서...
메타휴먼 눈물셈 가우시안 블러처리 중에서...

이 부분을 HLSL 로 보면.

MaterialFloat2 Local7 = (MaterialFloat2(0.40360001,-0.79350001).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local8 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local7);
MaterialFloat4 Local9 = DecodeSceneColorAndAlpharForMaterialNode(Local8);
MaterialFloat2 Local10 = (MaterialFloat2(0.37300000,-0.27300000).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local11 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local10);
MaterialFloat4 Local12 = DecodeSceneColorAndAlpharForMaterialNode(Local11);
MaterialFloat3 Local13 = (Local9.rgb + Local12.rgb);
MaterialFloat2 Local14 = (MaterialFloat2(-0.04100000,-0.59700000).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local15 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local14);
MaterialFloat4 Local16 = DecodeSceneColorAndAlpharForMaterialNode(Local15);
MaterialFloat3 Local17 = (Local13 + Local16.rgb);
MaterialFloat2 Local18 = (MaterialFloat2(-0.37092999,-0.91799998).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local19 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local18);
MaterialFloat4 Local20 = DecodeSceneColorAndAlpharForMaterialNode(Local19);
MaterialFloat3 Local21 = (Local17 + Local20.rgb);
MaterialFloat2 Local22 = (MaterialFloat2(-0.37900001,-0.33500001).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local23 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local22);
MaterialFloat4 Local24 = DecodeSceneColorAndAlpharForMaterialNode(Local23);
MaterialFloat3 Local25 = (Local21 + Local24.rgb);
MaterialFloat2 Local26 = (MaterialFloat2(-0.31540000,0.10700000).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local27 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local26);
MaterialFloat4 Local28 = DecodeSceneColorAndAlpharForMaterialNode(Local27);
MaterialFloat3 Local29 = (Local25 + Local28.rgb);
MaterialFloat2 Local30 = (MaterialFloat2(-0.76700002,-0.61400002).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local31 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local30);
MaterialFloat4 Local32 = DecodeSceneColorAndAlpharForMaterialNode(Local31);
MaterialFloat3 Local33 = (Local29 + Local32.rgb);
MaterialFloat2 Local34 = (MaterialFloat2(-0.48699999,0.63900000).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local35 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local34);
MaterialFloat4 Local36 = DecodeSceneColorAndAlpharForMaterialNode(Local35);
MaterialFloat3 Local37 = (Local33 + Local36.rgb);
MaterialFloat2 Local38 = (MaterialFloat2(0.23600000,0.15099999).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local39 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local38);
MaterialFloat4 Local40 = DecodeSceneColorAndAlpharForMaterialNode(Local39);
MaterialFloat3 Local41 = (Local37 + Local40.rgb);
MaterialFloat2 Local42 = (MaterialFloat2(0.03100000,0.73100001).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local43 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local42);
MaterialFloat4 Local44 = DecodeSceneColorAndAlpharForMaterialNode(Local43);
MaterialFloat3 Local45 = (Local41 + Local44.rgb);
MaterialFloat2 Local46 = (MaterialFloat2(-0.82300001,-0.10600000).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local47 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local46);
MaterialFloat4 Local48 = DecodeSceneColorAndAlpharForMaterialNode(Local47);
MaterialFloat3 Local49 = (Local45 + Local48.rgb);
MaterialFloat2 Local50 = (MaterialFloat2(-0.87320000,0.30300000).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local51 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local50);
MaterialFloat4 Local52 = DecodeSceneColorAndAlpharForMaterialNode(Local51);
MaterialFloat3 Local53 = (Local49 + Local52.rgb);
MaterialFloat2 Local54 = (MaterialFloat2(0.92699999,-0.11600000).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local55 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local54);
MaterialFloat4 Local56 = DecodeSceneColorAndAlpharForMaterialNode(Local55);
MaterialFloat3 Local57 = (Local53 + Local56.rgb);
MaterialFloat2 Local58 = (MaterialFloat2(0.69800001,0.37700000).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local59 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local58);
MaterialFloat4 Local60 = DecodeSceneColorAndAlpharForMaterialNode(Local59);
MaterialFloat3 Local61 = (Local57 + Local60.rgb);
MaterialFloat2 Local62 = (MaterialFloat2(0.82400000,-0.51499999).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local63 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local62);
MaterialFloat4 Local64 = DecodeSceneColorAndAlpharForMaterialNode(Local63);
MaterialFloat3 Local65 = (Local61 + Local64.rgb);
MaterialFloat2 Local66 = (MaterialFloat2(0.50800002,0.75300002).rg * ((MaterialFloat2)Local6));
MaterialFloat2 Local67 = CalcScreenUVFromOffsetFraction(GetScreenPosition(Parameters), Local66);
MaterialFloat4 Local68 = DecodeSceneColorAndAlpharForMaterialNode(Local67);

이렇게 구성되는데.... 위 머티리얼 노드 일부를 해석해보면 tan(View.FieldOfViewWideAngles.xy * 0.5) 에 씬뎁스를 곱한 후 0.2로 나눕니다. tan(FOV) * Depth는 FOV를 기반으로 픽셀의 월드 공간 상대 위치를 결정하기 위한 데이터입니다.예를 들어, 픽셀이 화면 상의 (x,y) 위치에 있고, SceneDepth.R이 이 픽셀의 z-depth라면, tan(FOV)로 변환된 값과 곱해 뷰 공간(Matrix 계산)에서 이 픽셀이 월드 좌표계에서 얼마만큼 떨어져 있는지를 확인할 수 있습니다.월드 메시와 픽셀 위치의 정확한 상관관계를 계산할 수 있다는 뜻으로 볼 수 있으며 0.2라는 상수 값은 데이터를 정규화(또는 조정)하여 쉐이더 파이프라인에서 정의된 범위 내에서 작업할 수 있도록 작동하는 수단일 뿐만 아니라, 시각적 효과의 강도 범위를 상수를 사용하여 적합해 보이는 값으로 처리 해 논 것일 수 있죠.
매타휴먼 눈물셈 세이더에서 목표로 하고 있는 이 부분의 차리는 가져온 씬 칼라를 16번 블러오프셋 최적화의 좋은 방법이다.
 

float2 GetPrevTanHalfFieldOfView()
{
	return View.PrevTanAndInvTanHalfFOV.xy;
}

이전 프레임에서의 수평 및 수직 시야각의 탄젠트 값을 반환합니다.

  • 반환값: 이전 프레임의 탄젠트 값. 카메라가 움직였거나 FOV가 변경된 경우 값이 달라질 수 있습니다.

모션 블러(Motion Blur): 현재 프레임과 이전 프레임의 카메라 변화(FOV 차이 등)를 분석하여 화려한 모션 블러 효과를 생성하는 데 사용됩니다. Temporal Anti-Aliasing (TAA): 이전 프레임의 정보를 바탕으로 현재 프레임의 이미지를 보정하거나 품질을 향상시키는 데 활용됩니다.

float2 GetCotanHalfFieldOfView()
{
	return View.TanAndInvTanHalfFOV.zw;
}

시야각 절반에 대한 코탄젠트(cot, 1/tan) 값을 반환합니다.

  • 반환값:
  • 1 / tan(View.FieldOfViewWideAngles.x * 0.5) (수평 FOV)
  • 1 / tan(View.FieldOfViewWideAngles.y * 0.5) (수직 FOV)

광선 투영 계산(ray projection): 카메라의 시야 내 특정 방향으로 광선을 빠르게 투사하기 위해 코탄젠트 값을 활용합니다.뷰-투-스크린 변환: 3D 뷰 벡터를 2D 스크린 좌표로 변환할 때 사용됩니다. 커스텀 쉐이더 최적화: 특정 계산에서 1/tan 값이 필요한 경우, 별도의 연산 없이 빠르게 접근할 수 있음.

float2 GetPrevCotanHalfFieldOfView()
{
	return View.PrevTanAndInvTanHalfFOV.zw;
}

이전 프레임의 코탄젠트(cot) 값 반환.

  • 반환값: 이전 프레임의 시야각 1/tan 값.

Temporal 렌더링: 이전 프레임의 코탄젠트 데이터를 활용해, 현재 프레임의 정확도 높은 화면 좌표 계산 등에서 활용.
쉐이더 기반 효과 강화: 모션 블러, Temporal Anti-Aliasing (TAA) 등의 쉐이더에서 과거 뷰 데이터와 연계시키기 위한 필수 기능.

uint GetPowerOfTwoModulatedFrameIndex(uint Pow2Modulus)
{
	return View.StateFrameIndex & uint(Pow2Modulus - 1);
}

현재 프레임(View.StateFrameIndex)을 2의 제곱수(Modulus)로 나눈 나머지를 계산합니다. 내부적으로는 비트 마스킹 연산을 통해 이를 매우 효율적으로 수행합니다.

  • 반환값: (View.StateFrameIndex % Pow2Modulus)와 동일한 결과.

프레임 기반 순환 연산: 유한한 자원(예: 텍스처, 셰이더 변수 등)이 반복적으로 사용될 때, 프레임 인덱스를 모듈로 연산해 순환적으로 관리할 수 있음.
이벤트 타이밍: 특정 프레임 간격마다 이벤트를 발생시키는 로직 구현.
성능 최적화: 정수 나눗셈 대신 비트 연산을 사용하여 성능 최적화.
 

활용 방안들

1. 포스트 프로세싱

  • 특징: GetTanHalfFieldOfView()와 GetCotanHalfFieldOfView()는 카메라 시야를 기반으로 화면 공간(Screen Space) 재구성 작업에 필수적입니다.
  • 적용:
    • 블룸(Bloom), 화면 왜곡(Distortion), 폭발 효과 구현 시 활용.
    • GPU에서 카메라의 공간 좌표 계산 최적화.

2. Temporal 렌더링 효과

  • 특징: GetPrevTanHalfFieldOfView()와 GetPrevCotanHalfFieldOfView()는 과거 프레임 데이터를 활용해 Temporal 효과를 계산합니다.
  • 적용:
    • TAA (Temporal Anti-Aliasing): 이전 프레임의 데이터를 사용해 현재 이미지를 보정.
    • 모션 블러(Motion Blur): 카메라 이동 및 회전으로 인한 픽셀의 속도를 계산.

3. 최적화된 순환 로직

  • 특징: GetPowerOfTwoModulatedFrameIndex()는 순환적 이벤트 처리를 매우 효율적으로 구현할 수 있게 합니다.
  • 적용:
    • GPU 메모리를 절약하기 위해 프레임별로 자원을 순환적으로 처리.
    • 타이머 기반의 순환 업데이트 이벤트 구현.
렌더링의 핵심 데이터인 뷰(View) 정보를 효율적으로 처리하는 함수들입니다. 카메라 기반 계산(예: FOV)과 프레임 데이터(이전/현재) 활용, 그리고 효율적인 비트 마스킹 연산으로 최적화된 작업과 그래픽 특수 효과를 가능하게 합니다. 특히 TAA, 모션 블러, 광선 투사와 같은 고급 효과 구현 시 매우 중요한 역할을 합니다. Unreal Engine의 렌더링 파이프라인을 확장하거나 커스터마이징할 때 참고할 중요한 함수들입니다.