2. Unity 렌더링 구조
로직
유저가 만든 렌더링
커맨드들
MeshRender,
Graphics.Draw,
등등~
CommandBuffer
생성
렌더 파이프
현재 환경과 설정된
렌더 파이프에
맞추어 실제 실행할
렌더커맨드 생성
LWRP, HDRP,
Built-in Legacy
RenderCommandB
uffer 생성
렌더 Context
렌더커맨드를 API
명령어로 호출
RenderingComma
ndBuffer 실행
실제 그래픽 API
함수 호출
Graphic API
DirectX, openGL
드라이버를 통해
GPU에 실제그리기
메인 스레드( C# ) 렌더 스레드( C++ )
3. 렌더파이프의 역활
유저가 그린
오브젝트들
CommandBuffer
월드 정보들
( 카메라 정보,
라이팅 정보)
렌더 환경 정보들
( 모션벡터,
후처리, API 정보)
랜더파이프
● Culling
● Sorting
● Data Updating
● Environment
Setting
● Etc….
RanderCommand
Buffer 생성
5. Unity 로직에서 커맨드 만드는 방법들
1. Renderer 컴포넌트 사용 ( MeshRenderer, SkinnedMeshRenderer )
2. 코드에서 직접 그리기 ( Graphics.Draw 명령어들 DrawMesh )
3. Graphics API 로 그리기
• 카메라 GameObject에 붙힌 컴포넌트의 특정 함수에서만 동작
OnPostRender, OnPreRender
• OpenGL을 랩핑한 UnityEngine.GL 을 명령어 사용
• SRP에서 사용불가
8. Normal Draw Call
Vertex Shader
버텍스
버퍼
인덱스
버퍼
버텍스
정보
SetTransform
머터리얼 정보
유니폼 데이터
( 모든 쉐이더가 같이 공유하는 데이터 )
CPU GPU
SetIB
SetVB
Draw
9. Instancing Draw Call
Vertex Shader
버텍스
버퍼
인덱스
버퍼
버텍스
정보
SetTransformList
머터리얼 정보
유니폼 데이터
SetIB
SetVB
인스터스
ID리스트 ID 인스턴스
마다 다른
Transform
DrawInstancing
10. Indirect Draw 는 두 단계이다.
Indirect는 그릴때 데이터를 보내지 않는다. 그리기 명령어와 데이터를 GPU에 보내는 것이 분리된다.
1단계 - 데이터 올리기
2단계 - 그리기
옵션 - 필요하면 데이터 Update하기 ( compute shader )
11. Instancing Draw Call - Data 올리기
Compute Shader
그리기 위한 모든 정보
컴퓨트 버퍼
( 일정간격마다 유니폼 데이터 )
버퍼 상태 업데이트
( ex: 컬링여부, 데이타
업데이트 )
Dispatch(optional)
유니폼
데이터
SetIBList
SetVBList
인덱스
버퍼들
버텍스
버퍼들
12. Instancing Draw Call - 2단계 그리기
Vertex Shader
컴퓨트 버퍼
( 일정간격마다 유니폼 데이터 )
DrawIndirect
유니폼
데이터
인덱스
버퍼들
버텍스
버퍼들
유니폼
데이터
인덱스
버퍼
버텍스
버퍼
컴퓨트 버퍼의 일정간격 마다(Stride) 하나의 Draw Call 이 된다.
13. Draw VS DrawInstance VS DrawIndirect
동일 shader로 10개 Material을 만들고 10개 Mesh를 이용해 10x10 = 100종의 오브젝트를 만든다
오브젝트를 100개씩 그린다. ( 1 x 10 x 10 x 100 = 10000 개의 인스턴스 )
for( material ){
for( texture ){
for ( mesh ){
for ( Instance ){
SetUniformData()
Draw();
}}}}
Once
10 = 10 setTexture
Per Frame
10x10x100 = 10000 SetData
10x10x100 = 10000 draw call
for( material ){
for( texture ){
for ( mesh ){
SetUniformDataList()
InstancingDraw();
}}}
Once
10 = 10 setTexture
Per frame
10x10 = 100 SetData
10x10 = 100 draw call
for( shader ){
if( needUpdateBuffer )
UpdateBuffer();
IndirectDraw();
}
Once
10 = 10 setTexture
1 = 1 SetData
Per frame
1 = 1 draw call
14. Indirect Draw 사용법
Indirect Draw는 아직은 직접 명령어로만 가능 ( 아마 앞으로도.. )
DX11에 상응하는 API 필요 ( ES3.1, METAL, VULKAN )
실질적으로 엔진 렌더파이프라인을 건너 뜀 ( culling, sorting, updating X )
직접 모든 데이타를 compute shader를 이용해서 관리해야함
15. Indirect Draw 사용법
실제코드는 굉장히 간단, 5분이면 테스트 가능
( 데이터 올리고 그리는 10줄, 쉐이더에 10줄 추가로 끝~ )
예제:
https://docs.unity3d.com/2019.1/Documentation/ScriptReference/Graphics.Draw
MeshInstancedIndirect.html
16. Indirect Draw 실제 사용
기존 쉐이더마다 Indirect용으로 컨버팅하기 번거로워 유니티에서 실용적인
Indirect 쉐이더 디자인패턴을 제공
#pragma instancing_options procedural:FunctionName 옵션을 이용해서
인스턴스마다 유니폼버퍼들을 재설정하는 방식의 코드를 추가하고 이전쉐이더
상태 그대로 사용하는 것이 보통임
이전쉐이더에 위의 전처리 한줄만 추가하면 됨 ( 유니티 짱! )
17. Indirect Draw 실제 사용
인스턴스마다의 정보를 업데이트 한다.
void IndirectSetupInstance()
{
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
// 인스턴스에 맞는 global 변수 업데이트
// unity_ObjectToWorld,unity_WorldToObject,그 외 버퍼들
#endif
}
#pragma instancing_options procedural:IndirectSetupInstance