블러처리 하나 만들다가 렌더그래프 추가되었다고 소스코드도 터지고 멘탈도 터진 상태에서
결국 공식문서를 보기로 함.
그러면..
public class BlurRendererFeature : ScriptableRendererFeature
{
public override void Create()
{
}
public override void AddRenderPasses(ScriptableRenderer renderer,
ref RenderingData renderingData)
{
}
}
----
렌더피쳐는 일단 요 2개의 함수가 기본적으로 필요하다.
일단 scriptableRendererFeature는 원형이되는 클래스이고 이걸 상속하는건 렌더피쳐임을 말하는 것과 동시에
렌더 패스 만들고 언제 넣을지 정한다는 의미
(After Post Processing등등..)
(대충 렌더링 파이프라인을 내 입맛대로 뜯어고쳐서 원하는 효과를 필터처럼 넣다 뺏다 할 수 있는 거)
작성자는 블러효과를 위해서 이걸 사용하려 한다.(에셋 쓰는게 젤 편한듯)
랜더패스는 렌더 피쳐의 하위 작업으로 생각하면됨.
실제 수행되는 연산이라고 생각하면되고 얘가 렌더링 주체임. 실제 렌더링작업을 수행함.
블러를 하기 위해서 카메라 끌어오고 텍스쳐 blit하고 쉐이더 실행함.
이제 랜더 패스를 만들어보자
기존 URP와의 차이점이 렌더패스에서 드러난다.
바로 최신버전부터 적용된 RenderGraph함수 이다.
렌더그래프를 사용하면 기존대비 성능상 더 이점이 있다고 공식문서에 적혀있지만
안 쓸 사람은 설정에서 끄고 기존 코드 쓸 수 있는거 같음(확실하지 않다!)
나는 이미 흑백 필터링을 렌더그래프를 써서 반강제로 이걸 써야했음.(되게 블루프린트 같았음)
public class BlurRenderPass : ScriptableRenderPass
{
public override void RecordRenderGraph(RenderGraph renderGraph,
ContextContainer frameData)
{
}
}
일단 렌더 패스의 초안이다.
오버라이드 된 저 함수가 매 프레임마다 저 함수를 호출한다고 기술되어 있다.
그러면 기존 버전의 코드를 잠깐 살펴보자.(작동 과정을 보기 위해)
public class BlurPass : ScriptableRenderPass
{
Material blurMaterial;// 매터리얼
RenderTargetIdentifier source;
RenderTargetHandle tempTexture;
public BlurPass(Material material)
{
this.blurMaterial = material;
tempTexture.Init("_TempBlurTexture");
}
public void Setup(RenderTargetIdentifier source)
{
this.source = source;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get("Gaussian Blur");
RenderTextureDescriptor desc = renderingData.cameraData.cameraTargetDescriptor;
cmd.GetTemporaryRT(tempTexture.id, desc);
// 블러 쉐이더로 블릿
Blit(cmd, source, tempTexture.Identifier(), blurMaterial);
Blit(cmd, tempTexture.Identifier(), source); // 다시 원본에 씌움
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
public override void FrameCleanup(CommandBuffer cmd)
{
cmd.ReleaseTemporaryRT(tempTexture.id);
}
}
---
execute는 실제 렌더링 동작일어나는 함수,
blit은 화면에 쉐이더를 적용해서 출력하는 유틸함수
카메라 타겟은 렌더타겟임.
일단 렌더피쳐에서 create()로 렌더 패스 만듦 한개
1회만 호출임.
그리고 addRenderPasses는 매 프레임 호출됨 렌더러에 내가만든 패스를 등록하는거임.
그러면 이후 내부의 execute가 실행되는 구조.(렌더링 되면서)
그러면 내부는 어떻게 돌아가느냐..
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get("BlurPass");
RenderTargetIdentifier source = renderingData.cameraData.renderer.cameraColorTarget;
Blit(cmd, source, source, blurMaterial); // 블러 머티리얼 적용
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
blit의 형태를 기억해두자.
일단 렌더러가 작동하는 방식은 큐의 형태라고 생각되어진다.(큐에 명령을 모은 후 한번에 처리)
따라서 커맨드 버퍼(명령 보관소) 를 만들어 GPU에 보낼 명령(렌더링 명령) 을 모으는 것 같음.
쉐이더는 코드의 매터리얼과 연결되어있어서 참조가 되는거고
blit(cmd,src,dest,mat)의 형태로 보아 커맨드버퍼를 참조해 원점->도착지로 쉐이더를 적용하라
라는 구문 같음.
그리고 src가 카메라에 렌더링된 텍스쳐 즉 렌더타겟이 들어오므로
렌더타겟에 기록된 것을 쉐이더 적용해서 다시 거기(소스)에 저장해 (함수보면 src와 dest가 같은 변수로 되어 있음, 둘다 (src,src)임)
context.ExecuteCommandBuffer(cmd);
는 최종 처리임.
결산과 같은 것이다. GPU로 이것들 전달해줘! 임
(현재 문맥에서 커맨드(명령)모은거 실행해)
그러면 최신 코드들을 보자
public class BlurRenderPass : ScriptableRenderPass
{
private static readonly int horizontalBlurId = Shader.PropertyToID("_HorizontalBlur");// 쉐이더 속성 ID
private static readonly int verticalBlurId = Shader.PropertyToID("_VerticalBlur");
private const string k_BlurTextureName = "_BlurTexture";// 블러 텍스쳐 이름
private const string k_VerticalPassName = "VerticalBlurRenderPass";// 렌더 그래프의 블러 패스 이름(디버깅용이라는데.?)
private const string k_HorizontalPassName = "HorizontalBlurRenderPass";
private BlurSettings defaultSettings;// 볼륨 세팅 된거 여기선 2pass니까 수직 수평값
private Material material;//쉐이더 들어있는 매터리얼
private RenderTextureDescriptor blurTextureDescriptor;// 텍스쳐 정보 가지고 있는 애(해상도나 포맷)
public BlurRenderPass(Material material, BlurSettings defaultSettings)// 생성자: 세팅과 쉐이더 받아오는거 + 텍스쳐 정보 세팅
{
this.material = material;
this.defaultSettings = defaultSettings;
blurTextureDescriptor = new RenderTextureDescriptor(Screen.width, Screen.height,
RenderTextureFormat.Default, 0);
}
private void UpdateBlurSettings()// 볼륨값 (커스텀 볼륨) 가져오기 2pass에 적용키 위한값임.
{
if (material == null) return;
// Use the Volume settings or the default settings if no Volume is set.
var volumeComponent =
VolumeManager.instance.stack.GetComponent<CustomVolumeComponent>();// 볼륨 컴포넌트 끌어와서
float horizontalBlur = volumeComponent.horizontalBlur.overrideState ?
volumeComponent.horizontalBlur.value : defaultSettings.horizontalBlur;
float verticalBlur = volumeComponent.verticalBlur.overrideState ?
volumeComponent.verticalBlur.value : defaultSettings.verticalBlur;// 3항연산자 볼륨값 있으면 대입 아니면 기본값.
material.SetFloat(horizontalBlurId, horizontalBlur);
material.SetFloat(verticalBlurId, verticalBlur);// 매터리얼 속성 반영(값부여?)
}
public override void RecordRenderGraph(RenderGraph renderGraph,
ContextContainer frameData)// ㅈㄴ 중요한 함수 신규 변경점임******** 렌더링 명령 등록하는 곳 기존과 달리 priority queue를 생각하면 비슷한거 같다. 알아서 렌더 패스 순서를 지//정한다고함
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();//현 프레임에서 어떤 텍스쳐 사용하는지 가져옴
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();//카메라 해상도+ 렌더타겟 정보
// The following line ensures that the render pass doesn't blit
// from the back buffer.
if (resourceData.isActiveTargetBackBuffer)// GPU 백버퍼 사용중이면 정지-> GPU백버퍼에 블러걸면 안된다 카더라..
return;
// Set the blur texture size to be the same as the camera target size.
blurTextureDescriptor.width = cameraData.cameraTargetDescriptor.width;// 카메라와 같은 해상도의 텍스쳐 준비
blurTextureDescriptor.height = cameraData.cameraTargetDescriptor.height;
blurTextureDescriptor.depthBufferBits = 0;//깊이 버퍼 0,1 아시죠?
TextureHandle srcCamColor = resourceData.activeColorTexture;// 현재까지 카메라가 렌더링한 텍스쳐 (기존버전의 src와 같은 종류라 생각됨)
TextureHandle dst = UniversalRenderer.CreateRenderGraphTexture(renderGraph,
blurTextureDescriptor, k_BlurTextureName, false);// 중간 단계용 텍스쳐라는데 버퍼로 생각하시면 될듯?
// Update the blur settings in the material
UpdateBlurSettings();// 볼륨 받아와서 세팅 갱신+ 쉐이더에 값 넣기 (위에 있음!)
// This check is to avoid an error from the material preview in the scene
if (!srcCamColor.IsValid() || !dst.IsValid())// 유효성 검사
return;
// The AddBlitPass method adds a vertical blur render graph pass that blits from the source texture (camera color in this case) to the destination texture using the first shader pass (the shader pass is defined in the last parameter).
RenderGraphUtils.BlitMaterialParameters paraVertical = new(srcCamColor, dst, material, 0);//매터리얼 0번 패스 적용해서 src->dst로
renderGraph.AddBlitPass(paraVertical, k_VerticalPassName);// 이 코드에서는 패스에 수직 블러 연산이 있음
// The AddBlitPass method adds a horizontal blur render graph pass that blits from the texture written by the vertical blur pass to the camera color texture. The method uses the second shader pass.
RenderGraphUtils.BlitMaterialParameters paraHorizontal = new(dst, srcCamColor, material, 1);// 버퍼에 다시 뒤집어 씌우기
renderGraph.AddBlitPass(paraHorizontal, k_HorizontalPassName);// 이코드에서는 1번패스가 수평블러임
}
}
[Serializable]
public class CustomVolumeComponent : VolumeComponent//그냥 슬라이더인데 실시간 참조가 되는.. 걸로만 알아두려함.
{
public ClampedFloatParameter horizontalBlur =
new ClampedFloatParameter(0.05f, 0, 0.5f);
public ClampedFloatParameter verticalBlur =
new ClampedFloatParameter(0.05f, 0, 0.5f);
}
되게 길다.
RenderGraphUtils.BlitMaterialParameters paraHorizontal = new(dst, srcCamColor, material, 1);// 버퍼에 다시 뒤집어 씌우기
renderGraph.AddBlitPass(paraHorizontal, k_HorizontalPassName);// 이코드에서는 1번패스가 수평블러임
특히 이부분은 dx11의 desc작성하는 것과 유사함.
구조체에 정보 입력 후 넣는 점?
일단 요정도로 정리해봤음.
블러가 근데 실행되는 중의 볼륨이 아니라..
왜 글로벌 볼륨>박스 볼륨->커스텀볼륨 으로 해야 블러가되는지 모르겠음.
기술문서에는 실시간으로 렌더패스 볼륨 조절하면 블러가 된다고 해놓은거 같은데..
여튼 최신버전 urp로 고생중인 분들은 gpt한테 묻지말고 그냥 기술문서 보십쇼
갓고수추