study/first_study/Library/PackageCache/com.unity.render-pipelines.universal@d10049dfa479/Runtime/RendererFeatures/OnTilePostProcessPass.cs
jh04010421 739d49f1a0 Unity | 2026.01.20
수업 실습 파일
2026-01-20 11:01:57 +09:00

443 lines
19 KiB
C#

using UnityEngine;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.Universal.Internal;
/// <summary>
/// Renders the on-tile post-processing stack.
/// </summary>
public class OnTilePostProcessPass : ScriptableRenderPass
{
/// <summary>
/// The override shader to use.
/// </summary>
internal bool m_UseMultisampleShaderResolve = false;
internal bool m_UseTextureReadFallback = false;
RTHandle m_UserLut;
Material m_OnTileUberMaterial;
static readonly int s_BlitScaleBias = Shader.PropertyToID("_BlitScaleBias");
static readonly int s_BlitTexture = Shader.PropertyToID("_BlitTexture");
int m_DitheringTextureIndex;
PostProcessData m_PostProcessData;
const string m_PassName = "On Tile Post Processing";
const string m_FallbackPassName = "On Tile Post Processing (sampling fallback) ";
internal OnTilePostProcessPass(PostProcessData postProcessData)
{
m_PostProcessData = postProcessData;
#if ENABLE_VR && ENABLE_XR_MODULE
m_UseMultisampleShaderResolve = SystemInfo.supportsMultisampledShaderResolve;
#endif
}
internal void Setup(ref Material onTileUberMaterial)
{
m_OnTileUberMaterial = onTileUberMaterial;
}
/// <summary>
/// Disposes used resources.
/// </summary>
public void Dispose()
{
m_UserLut?.Release();
CoreUtils.Destroy(m_OnTileUberMaterial);
}
/// <inheritdoc cref="IRenderGraphRecorder.RecordRenderGraph"/>
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (m_OnTileUberMaterial == null) return;
var resourceData = frameData.Get<UniversalResourceData>();
var renderingData = frameData.Get<UniversalRenderingData>();
var cameraData = frameData.Get<UniversalCameraData>();
var postProcessingData = frameData.Get<UniversalPostProcessingData>();
if (SystemInfo.graphicsShaderLevel < 30)
{
Debug.LogError("DrawProcedural is required for the On-Tile post processing feature but it is not supported by the platform. Pass will not execute.");
return;
}
int lutSize = postProcessingData.lutSize;
var stack = VolumeManager.instance.stack;
var vignette = stack.GetComponent<Vignette>();
var colorLookup = stack.GetComponent<ColorLookup>();
var colorAdjustments = stack.GetComponent<ColorAdjustments>();
var tonemapping = stack.GetComponent<Tonemapping>();
var filmgrain = stack.GetComponent<FilmGrain>();
#if ENABLE_VR && ENABLE_XR_MODULE
bool useVisibilityMesh = cameraData.xr.enabled && cameraData.xr.hasValidVisibleMesh;
#else
const bool useVisibilityMesh = false;
#endif
TextureHandle source = resourceData.activeColorTexture;
TextureDesc srcDesc = renderGraph.GetTextureDesc(source); ;
TextureHandle destination = resourceData.backBufferColor;
SetupVignette(m_OnTileUberMaterial, cameraData.xr, srcDesc.width, srcDesc.height, vignette);
SetupLut(m_OnTileUberMaterial, colorLookup, colorAdjustments, lutSize);
SetupTonemapping(m_OnTileUberMaterial, tonemapping, isHdrGrading: postProcessingData.gradingMode == ColorGradingMode.HighDynamicRange);
SetupGrain(m_OnTileUberMaterial, cameraData, filmgrain, m_PostProcessData);
SetupDithering(m_OnTileUberMaterial, cameraData, m_PostProcessData);
CoreUtils.SetKeyword(m_OnTileUberMaterial, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, cameraData.isAlphaOutputEnabled);
UberShaderPasses shaderPass = useVisibilityMesh ? UberShaderPasses.NormalVisMesh : UberShaderPasses.Normal;
#if ENABLE_VR && ENABLE_XR_MODULE
bool setMultisamplesShaderResolveFeatureFlag = false;
#endif
if (srcDesc.msaaSamples != MSAASamples.None)
{
if (srcDesc.msaaSamples == MSAASamples.MSAA8x)
{
Debug.LogError("MSAA8x is enabled in Universal Render Pipeline Asset but it is not supported by the on-tile post-processing feature yet. Please use MSAA4x or MSAA2x instead.");
return;
}
var destInfo = renderGraph.GetRenderTargetInfo(destination);
#if ENABLE_VR && ENABLE_XR_MODULE
if (m_UseMultisampleShaderResolve)
{
// If we have support for msaa shader resolve we can do MSAA -> non MSAA in our render pass.
shaderPass = useVisibilityMesh ? UberShaderPasses.MSAASoftwareResolveVisMesh : UberShaderPasses.MSAASoftwareResolve;
// When rendering into the backbuffer, we could enable the shader resolve extension to resolve into the msaa1x surface directly on platforms that support auto resolve.
// For platforms that don't support auto resolve, the backbuffer is a multisampled surface and we don't need to enable the extension. This is to maximize the pass merging because shader resolve enabled pass has to be the last subpass.
if (SystemInfo.supportsMultisampleAutoResolve)
{
setMultisamplesShaderResolveFeatureFlag = true;
}
}
else
#endif
{
if (destInfo.msaaSamples == (int)srcDesc.msaaSamples)
{
// If we have MSAA -> MSAA we can still resolve in the shader running at fragmnet rate
// and the hardware resolve will sometimes be optimized as a result.
shaderPass = useVisibilityMesh ? UberShaderPasses.MSAASoftwareResolveVisMesh : UberShaderPasses.MSAASoftwareResolve;
}
else
{
// We are going MSAA -> Non MSAA without shader resolve support which is not a valid render pass
// configuration. So we need to force a resolve before running our shader which will cause us not
// to be on tile anymore.
shaderPass = useVisibilityMesh ? UberShaderPasses.TextureReadVisMesh : UberShaderPasses.TextureRead;
}
}
}
// Fallback to texture read mode when requested.
if (m_UseTextureReadFallback)
{
shaderPass = useVisibilityMesh ? UberShaderPasses.TextureReadVisMesh : UberShaderPasses.TextureRead;
#if ENABLE_VR && ENABLE_XR_MODULE
setMultisamplesShaderResolveFeatureFlag = false;
#endif
}
// Fallback logic to handle the case where Frame Buffer Fetch is not compatible with the pass setup.
TextureDesc destDesc;
var info = renderGraph.GetRenderTargetInfo(destination);
destDesc = new TextureDesc(info.width, info.height);
destDesc.format = info.format;
destDesc.msaaSamples = (MSAASamples)info.msaaSamples;
destDesc.bindTextureMS = info.bindMS;
destDesc.slices = info.volumeDepth;
destDesc.dimension = info.volumeDepth > 1 ? TextureDimension.Tex2DArray : TextureDimension.Tex2D;
// Falls back to texture read mode if texture dimension does not match between source and destination (invalid frame buffer fetch setup).
if (srcDesc.width != destDesc.width || srcDesc.height != destDesc.height || srcDesc.slices != destDesc.slices)
{
shaderPass = useVisibilityMesh ? UberShaderPasses.TextureReadVisMesh : UberShaderPasses.TextureRead;
#if ENABLE_VR && ENABLE_XR_MODULE
setMultisamplesShaderResolveFeatureFlag = false;
#endif
}
var lutTexture = resourceData.internalColorLut;
var passName = m_UseTextureReadFallback ? m_FallbackPassName : m_PassName;
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData))
{
passData.source = source;
passData.destination = destination;
passData.material = m_OnTileUberMaterial;
passData.shaderPass = shaderPass;
if (shaderPass == UberShaderPasses.TextureRead || shaderPass == UberShaderPasses.TextureReadVisMesh)
{
builder.UseTexture(source, AccessFlags.Read);
}
else
{
builder.SetInputAttachment(source, 0, AccessFlags.Read);
// MSAA shader resolve keywords require global state modification
builder.AllowGlobalStateModification(true);
}
builder.UseTexture(lutTexture, AccessFlags.Read);
passData.lutTexture = lutTexture;
var userLutTexture = TryGetCachedUserLutTextureHandle(colorLookup, renderGraph);
passData.userLutTexture = userLutTexture;
if (userLutTexture.IsValid())
{
builder.UseTexture(userLutTexture, AccessFlags.Read);
}
builder.SetRenderAttachment(destination, 0, AccessFlags.WriteAll);
builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecuteFBFetchPass(data, context));
passData.useXRVisibilityMesh = false;
passData.msaaSamples = (int)srcDesc.msaaSamples;
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled)
{
ExtendedFeatureFlags xrFeatureFlag = ExtendedFeatureFlags.MultiviewRenderRegionsCompatible;
if (setMultisamplesShaderResolveFeatureFlag)
{
xrFeatureFlag |= ExtendedFeatureFlags.MultisampledShaderResolve;
}
builder.SetExtendedFeatureFlags(xrFeatureFlag);
// We want our foveation logic to match other geometry passes(eg. Opaque, Transparent, Skybox) because we want to merge with previous passes.
bool passSupportsFoveation = cameraData.xrUniversal.canFoveateIntermediatePasses || resourceData.isActiveTargetBackBuffer;
builder.EnableFoveatedRasterization(
cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
passData.useXRVisibilityMesh = useVisibilityMesh;
passData.xr = cameraData.xr; // Need to pass this down for the method call RenderVisibleMeshCustomMaterial()
}
#endif
}
//This will prevent the final blit pass from being added/needed (still internal API in trunk)
resourceData.activeColorID = UniversalResourceData.ActiveID.BackBuffer;
resourceData.activeDepthID = UniversalResourceData.ActiveID.BackBuffer;
}
enum UberShaderPasses
{
Normal,
MSAASoftwareResolve,
TextureRead,
NormalVisMesh,
MSAASoftwareResolveVisMesh,
TextureReadVisMesh,
};
// This static method is used to execute the pass and passed as the RenderFunc delegate to the RenderGraph render pass
static void ExecuteFBFetchPass(PassData data, RasterGraphContext context)
{
var cmd = context.cmd;
data.material.SetTexture(ShaderConstants._InternalLut, data.lutTexture);
if (data.userLutTexture.IsValid())
data.material.SetTexture(ShaderConstants._UserLut, data.userLutTexture);
bool IsHandleYFlipped = RenderingUtils.IsHandleYFlipped(context, in data.destination);
// We need to set the "_BlitScaleBias" uniform for user materials with shaders relying on core Blit.hlsl to work
data.material.SetVector(s_BlitScaleBias, !IsHandleYFlipped ? new Vector4(1, -1, 0, 1) : new Vector4(1, 1, 0, 0));
if (data.shaderPass == UberShaderPasses.TextureRead || data.shaderPass == UberShaderPasses.TextureReadVisMesh)
{
data.material.SetTexture(s_BlitTexture, data.source);
}
else if (data.shaderPass == UberShaderPasses.MSAASoftwareResolve || data.shaderPass == UberShaderPasses.MSAASoftwareResolveVisMesh)
{
// Setup MSAA samples
switch (data.msaaSamples)
{
case 4:
CoreUtils.SetKeyword(data.material, ShaderKeywordStrings.Msaa2, false);
CoreUtils.SetKeyword(data.material, ShaderKeywordStrings.Msaa4, true);
break;
case 2:
CoreUtils.SetKeyword(data.material, ShaderKeywordStrings.Msaa2, true);
CoreUtils.SetKeyword(data.material, ShaderKeywordStrings.Msaa4, false);
break;
// MSAA disabled, auto resolve supported, resolve texture requested, or ms textures not supported
default:
CoreUtils.SetKeyword(data.material, ShaderKeywordStrings.Msaa2, false);
CoreUtils.SetKeyword(data.material, ShaderKeywordStrings.Msaa4, false);
break;
}
}
#if ENABLE_VR && ENABLE_XR_MODULE
if (data.useXRVisibilityMesh)
{
MaterialPropertyBlock xrPropertyBlock = XRSystemUniversal.GetMaterialPropertyBlock();
data.xr.RenderVisibleMeshCustomMaterial(cmd, data.xr.occlusionMeshScale, data.material, xrPropertyBlock, (int)(data.shaderPass), false);
}
else
#endif
{
cmd.DrawProcedural(Matrix4x4.identity, data.material, (int)(data.shaderPass),
MeshTopology.Triangles, 3, 1);
}
}
private class PassData
{
internal TextureHandle source;
internal TextureHandle destination;
internal TextureHandle lutTexture;
internal TextureHandle userLutTexture;
internal Material material;
internal UberShaderPasses shaderPass;
internal Vector4 scaleBias;
internal bool useXRVisibilityMesh;
internal XRPass xr;
internal int msaaSamples;
}
TextureHandle TryGetCachedUserLutTextureHandle(ColorLookup colorLookup, RenderGraph renderGraph)
{
if (colorLookup.texture.value == null)
{
if (m_UserLut != null)
{
m_UserLut.Release();
m_UserLut = null;
}
}
else
{
if (m_UserLut == null || m_UserLut.externalTexture != colorLookup.texture.value)
{
m_UserLut?.Release();
m_UserLut = RTHandles.Alloc(colorLookup.texture.value);
}
}
return m_UserLut != null ? renderGraph.ImportTexture(m_UserLut) : TextureHandle.nullHandle;
}
void SetupLut(Material material, ColorLookup colorLookup, ColorAdjustments colorAdjustments, int lutSize)
{
int lutHeight = lutSize;
int lutWidth = lutHeight * lutHeight;
float postExposureLinear = Mathf.Pow(2f, colorAdjustments.postExposure.value);
Vector4 lutParams = new Vector4(1f / lutWidth, 1f / lutHeight, lutHeight - 1f, postExposureLinear);
Vector4 userLutParams = !colorLookup.IsActive()
? Vector4.zero
: new Vector4(1f / colorLookup.texture.value.width,
1f / colorLookup.texture.value.height,
colorLookup.texture.value.height - 1f,
colorLookup.contribution.value);
material.SetVector(ShaderConstants._Lut_Params, lutParams);
material.SetVector(ShaderConstants._UserLut_Params, userLutParams);
}
#region Vignette
//these methods should be publicly available for user features
void SetupVignette(Material material, XRPass xrPass, int width, int height, Vignette vignette)
{
var color = vignette.color.value;
var center = vignette.center.value;
var aspectRatio = width / (float)height;
#if ENABLE_VR
if (xrPass != null && xrPass.enabled)
{
if (xrPass.singlePassEnabled)
material.SetVector(ShaderConstants._Vignette_ParamsXR, xrPass.ApplyXRViewCenterOffset(center));
else
// In multi-pass mode we need to modify the eye center with the values from .xy of the corrected
// center since the version of the shader that is not single-pass will use the value in _Vignette_Params2
center = xrPass.ApplyXRViewCenterOffset(center);
}
#endif
var v1 = new Vector4(
color.r, color.g, color.b,
vignette.rounded.value ? aspectRatio : 1f
);
var v2 = new Vector4(
center.x, center.y,
vignette.intensity.value * 3f,
vignette.smoothness.value * 5f
);
material.SetVector(ShaderConstants._Vignette_Params1, v1);
material.SetVector(ShaderConstants._Vignette_Params2, v2);
}
#endregion
private void SetupTonemapping(Material onTileUberMaterial, Tonemapping tonemapping, bool isHdrGrading)
{
if (isHdrGrading)
{
CoreUtils.SetKeyword(m_OnTileUberMaterial, ShaderKeywordStrings.HDRGrading, isHdrGrading);
}
else
{
CoreUtils.SetKeyword(m_OnTileUberMaterial, ShaderKeywordStrings.TonemapNeutral,
tonemapping.mode.value == TonemappingMode.Neutral);
CoreUtils.SetKeyword(m_OnTileUberMaterial, ShaderKeywordStrings.TonemapACES,
tonemapping.mode.value == TonemappingMode.ACES);
}
}
void SetupGrain(Material onTileUberMaterial, UniversalCameraData cameraData, FilmGrain filmgrain, PostProcessData data)
{
if (filmgrain.IsActive())
{
onTileUberMaterial.EnableKeyword(ShaderKeywordStrings.FilmGrain);
PostProcessUtils.ConfigureFilmGrain(
data,
filmgrain,
cameraData.pixelWidth, cameraData.pixelHeight,
onTileUberMaterial
);
}
}
void SetupDithering(Material onTileUberMaterial, UniversalCameraData cameraData, PostProcessData data)
{
if (cameraData.isDitheringEnabled)
{
onTileUberMaterial.EnableKeyword(ShaderKeywordStrings.Dithering);
m_DitheringTextureIndex = PostProcessUtils.ConfigureDithering(
data,
m_DitheringTextureIndex,
cameraData.pixelWidth, cameraData.pixelHeight,
onTileUberMaterial
);
}
}
static class ShaderConstants
{
public static readonly int _Vignette_Params1 = Shader.PropertyToID("_Vignette_Params1");
public static readonly int _Vignette_Params2 = Shader.PropertyToID("_Vignette_Params2");
public static readonly int _Vignette_ParamsXR = Shader.PropertyToID("_Vignette_ParamsXR");
public static readonly int _Lut_Params = Shader.PropertyToID("_Lut_Params");
public static readonly int _UserLut_Params = Shader.PropertyToID("_UserLut_Params");
public static readonly int _InternalLut = Shader.PropertyToID("_InternalLut");
public static readonly int _UserLut = Shader.PropertyToID("_UserLut");
}
}