Feat: SSCC Render Graph 지원 추가 (Unity 6.3 호환)
- RecordRenderGraph() 메서드 구현으로 Unity 6.3 Render Graph API 지원 - PassData 클래스 추가 (패스 간 데이터 전달용) - SetupRenderPasses() 오버라이드 추가 - 기존 Execute() 메서드는 Compatibility Mode용으로 유지 - UnsafePass 사용하여 기존 렌더링 로직 보존 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6393b12072
commit
79a18adfe5
@ -6,6 +6,7 @@ using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.Rendering.RenderGraphModule;
|
||||
|
||||
namespace ScreenSpaceCavityCurvature.Universal
|
||||
{
|
||||
@ -148,6 +149,7 @@ namespace ScreenSpaceCavityCurvature.Universal
|
||||
#endif
|
||||
}
|
||||
|
||||
[System.Obsolete("This rendering path is for compatibility mode only (render graph disabled). Use RecordRenderGraph with RenderGraph.")]
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (mat == null)
|
||||
@ -306,6 +308,163 @@ namespace ScreenSpaceCavityCurvature.Universal
|
||||
cmd.DrawMesh(fullscreenMesh, Matrix4x4.identity, material, 0, passIndex);
|
||||
}
|
||||
|
||||
// ===== Render Graph Implementation =====
|
||||
|
||||
private class PassData
|
||||
{
|
||||
public Material material;
|
||||
public TextureHandle cameraColorTarget;
|
||||
public TextureHandle cavityTex;
|
||||
public TextureHandle tempTex;
|
||||
public TextureHandle ssccOutputTex;
|
||||
}
|
||||
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
||||
{
|
||||
if (mat == null)
|
||||
{
|
||||
Debug.LogError("SSCC material has not been correctly initialized...");
|
||||
return;
|
||||
}
|
||||
|
||||
FetchVolumeComponent();
|
||||
if (sscc == null || !sscc.IsActive()) return;
|
||||
|
||||
var resourceData = frameData.Get<UniversalResourceData>();
|
||||
var cameraData = frameData.Get<UniversalCameraData>();
|
||||
|
||||
var sourceWidth = cameraData.cameraTargetDescriptor.width;
|
||||
var sourceHeight = cameraData.cameraTargetDescriptor.height;
|
||||
int div = sscc.cavityResolution.value == SSCC.CavityResolution.Full ? 1 : 2;
|
||||
bool outputToTexture = Output == SSCC.OutputEffectTo._SSCCTexture;
|
||||
|
||||
// Update material properties
|
||||
mat.SetVector(ShaderProperties.inputTexelSize, new Vector4(1f / sourceWidth, 1f / sourceHeight, sourceWidth, sourceHeight));
|
||||
mat.SetVector(ShaderProperties.cavityTexTexelSize, new Vector4(1f / (sourceWidth / div), 1f / (sourceHeight / div), sourceWidth / div, sourceHeight / div));
|
||||
mat.SetMatrix(ShaderProperties.worldToCameraMatrix, cameraData.camera.worldToCameraMatrix);
|
||||
mat.SetFloat(ShaderProperties.effectIntensity, sscc.effectIntensity.value);
|
||||
mat.SetFloat(ShaderProperties.distanceFade, sscc.distanceFade.value);
|
||||
mat.SetFloat(ShaderProperties.curvaturePixelRadius, new float[] { 0f, 0.5f, 1f, 1.5f, 2.5f }[sscc.curvaturePixelRadius.value]);
|
||||
mat.SetFloat(ShaderProperties.curvatureRidge, sscc.curvatureBrights.value == 0f ? 999f : (5f - sscc.curvatureBrights.value));
|
||||
mat.SetFloat(ShaderProperties.curvatureValley, sscc.curvatureDarks.value == 0f ? 999f : (5f - sscc.curvatureDarks.value));
|
||||
mat.SetFloat(ShaderProperties.cavityWorldRadius, sscc.cavityRadius.value);
|
||||
mat.SetFloat(ShaderProperties.cavityRidge, sscc.cavityBrights.value * 2f);
|
||||
mat.SetFloat(ShaderProperties.cavityValley, sscc.cavityDarks.value * 2f);
|
||||
|
||||
// Update keywords
|
||||
bool debugEffect = sscc.debugMode.value == SSCC.DebugMode.EffectOnly;
|
||||
bool debugNormals = sscc.debugMode.value == SSCC.DebugMode.ViewNormals;
|
||||
CoreUtils.SetKeyword(mat, "DEBUG_EFFECT", debugEffect);
|
||||
CoreUtils.SetKeyword(mat, "DEBUG_NORMALS", debugNormals);
|
||||
CoreUtils.SetKeyword(mat, "CHARACTER_MASKING", sscc.enableNiloToonMasking.value);
|
||||
CoreUtils.SetKeyword(mat, "ORTHOGRAPHIC_PROJECTION", cameraData.camera.orthographic);
|
||||
CoreUtils.SetKeyword(mat, "NORMALS_RECONSTRUCT", sscc.normalsSource.value == SSCC.PerPixelNormals.ReconstructedFromDepth);
|
||||
CoreUtils.SetKeyword(mat, "SATURATE_CAVITY", sscc.saturateCavity.value);
|
||||
CoreUtils.SetKeyword(mat, "OUTPUT_TO_TEXTURE", outputToTexture);
|
||||
CoreUtils.SetKeyword(mat, "UPSCALE_CAVITY", sscc.cavityResolution.value == SSCC.CavityResolution.HalfUpscaled);
|
||||
CoreUtils.SetKeyword(mat, "CAVITY_SAMPLES_6", sscc.cavitySamples.value == SSCC.CavitySamples.Low6);
|
||||
CoreUtils.SetKeyword(mat, "CAVITY_SAMPLES_8", sscc.cavitySamples.value == SSCC.CavitySamples.Medium8);
|
||||
CoreUtils.SetKeyword(mat, "CAVITY_SAMPLES_12", sscc.cavitySamples.value == SSCC.CavitySamples.High12);
|
||||
CoreUtils.SetKeyword(mat, "CAVITY_SAMPLES_20", sscc.cavitySamples.value == SSCC.CavitySamples.VeryHigh20);
|
||||
|
||||
// Create texture descriptors
|
||||
var cavityDesc = new TextureDesc(sourceWidth / div, sourceHeight / div)
|
||||
{
|
||||
colorFormat = GraphicsFormat.R32G32B32A32_SFloat,
|
||||
depthBufferBits = DepthBits.None,
|
||||
filterMode = FilterMode.Bilinear,
|
||||
name = "SSCC_CavityTex"
|
||||
};
|
||||
|
||||
// Create cavity texture handle (shared between passes)
|
||||
TextureHandle cavityTexHandle = renderGraph.CreateTexture(cavityDesc);
|
||||
|
||||
// Pass 1: Generate Cavity
|
||||
using (var builder = renderGraph.AddUnsafePass<PassData>("SSCC Generate Cavity", out var passData))
|
||||
{
|
||||
passData.material = mat;
|
||||
passData.cavityTex = cavityTexHandle;
|
||||
|
||||
builder.UseTexture(passData.cavityTex, AccessFlags.Write);
|
||||
|
||||
builder.SetRenderFunc((PassData data, UnsafeGraphContext context) =>
|
||||
{
|
||||
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
||||
cmd.SetRenderTarget(data.cavityTex, 0, CubemapFace.Unknown, -1);
|
||||
cmd.DrawMesh(fullscreenMesh, Matrix4x4.identity, data.material, 0, Pass.GenerateCavity);
|
||||
});
|
||||
}
|
||||
|
||||
// Pass 2: Final composite
|
||||
if (outputToTexture)
|
||||
{
|
||||
var outputDesc = new TextureDesc(sourceWidth, sourceHeight)
|
||||
{
|
||||
colorFormat = GraphicsFormat.R16G16B16A16_SFloat,
|
||||
depthBufferBits = DepthBits.None,
|
||||
filterMode = FilterMode.Bilinear,
|
||||
name = "_SSCCTexture"
|
||||
};
|
||||
|
||||
using (var builder2 = renderGraph.AddUnsafePass<PassData>("SSCC Final Output", out var passData2))
|
||||
{
|
||||
passData2.material = mat;
|
||||
passData2.cavityTex = cavityTexHandle;
|
||||
passData2.ssccOutputTex = renderGraph.CreateTexture(outputDesc);
|
||||
|
||||
builder2.UseTexture(passData2.cavityTex, AccessFlags.Read);
|
||||
builder2.UseTexture(passData2.ssccOutputTex, AccessFlags.Write);
|
||||
|
||||
builder2.SetRenderFunc((PassData data, UnsafeGraphContext context) =>
|
||||
{
|
||||
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
||||
cmd.SetGlobalTexture(ShaderProperties.cavityTex, data.cavityTex);
|
||||
cmd.SetRenderTarget(data.ssccOutputTex, 0, CubemapFace.Unknown, -1);
|
||||
cmd.DrawMesh(fullscreenMesh, Matrix4x4.identity, data.material, 0, Pass.Final);
|
||||
cmd.SetGlobalTexture(ShaderProperties.globalSSCCTexture, data.ssccOutputTex);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var tempDesc = new TextureDesc(sourceWidth, sourceHeight)
|
||||
{
|
||||
colorFormat = cameraData.cameraTargetDescriptor.graphicsFormat,
|
||||
depthBufferBits = DepthBits.None,
|
||||
filterMode = FilterMode.Bilinear,
|
||||
name = "SSCC_TempTex"
|
||||
};
|
||||
|
||||
using (var builder2 = renderGraph.AddUnsafePass<PassData>("SSCC Final Screen", out var passData2))
|
||||
{
|
||||
passData2.material = mat;
|
||||
passData2.cavityTex = cavityTexHandle;
|
||||
passData2.cameraColorTarget = resourceData.activeColorTexture;
|
||||
passData2.tempTex = renderGraph.CreateTexture(tempDesc);
|
||||
|
||||
builder2.UseTexture(passData2.cavityTex, AccessFlags.Read);
|
||||
builder2.UseTexture(passData2.cameraColorTarget, AccessFlags.ReadWrite);
|
||||
builder2.UseTexture(passData2.tempTex, AccessFlags.ReadWrite);
|
||||
|
||||
builder2.SetRenderFunc((PassData data, UnsafeGraphContext context) =>
|
||||
{
|
||||
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
||||
|
||||
// Copy camera color to temp
|
||||
cmd.SetGlobalTexture(ShaderProperties.mainTex, data.cameraColorTarget);
|
||||
cmd.SetRenderTarget(data.tempTex, 0, CubemapFace.Unknown, -1);
|
||||
cmd.DrawMesh(fullscreenMesh, Matrix4x4.identity, data.material, 0, Pass.Copy);
|
||||
|
||||
// Final composite from temp to camera color
|
||||
cmd.SetGlobalTexture(ShaderProperties.mainTex, data.tempTex);
|
||||
cmd.SetGlobalTexture(ShaderProperties.cavityTex, data.cavityTex);
|
||||
cmd.SetRenderTarget(data.cameraColorTarget, 0, CubemapFace.Unknown, -1);
|
||||
cmd.DrawMesh(fullscreenMesh, Matrix4x4.identity, data.material, 0, Pass.Final);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
@ -356,5 +515,19 @@ namespace ScreenSpaceCavityCurvature.Universal
|
||||
renderer.EnqueuePass(renderPass);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
|
||||
{
|
||||
if (renderPass == null) return;
|
||||
|
||||
shader = Shader.Find("Hidden/Universal Render Pipeline/SSCC");
|
||||
if (shader == null) return;
|
||||
|
||||
renderPass.Setup(shader, renderer, renderingData);
|
||||
renderPass.ConfigureInput(ScriptableRenderPassInput.Depth | ScriptableRenderPassInput.Normal);
|
||||
|
||||
// Render Graph requires intermediate texture for post-processing effects
|
||||
renderPass.requiresIntermediateTexture = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user