From 9abe20302e29b692711252a55babd8cd08df139b Mon Sep 17 00:00:00 2001 From: Oikawa Date: Sun, 6 Sep 2026 15:40:16 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=E9=99=BD=E7=82=8E(HeatDistortion)=E3=81=AE?= =?UTF-8?q?=E3=83=9D=E3=82=B9=E3=83=88=E3=82=A8=E3=83=95=E3=82=A7=E3=82=AF?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Volume / RenderPass / Shader の3層と SiriusPostProcessingFeature への AllowFlag 配線を既存3エフェクトと同型で実装する。 - 深度から求めたカメラ距離を StartDistance〜FadeDistance で減衰カーブに 変換し、遠景ほど強く歪ませる - 3Dノイズ(Texture2DArray, 64スライス)の隣接2スライスを時間で lerp して 揺らぎパターンを生成し、スクリーンUVをオフセットする - R/G/B それぞれ独立したノイズスライスをサンプルして色収差を作る。 ChromaticSeparation = 0 で3チャンネルが同一スライスに収束し色ずれが消える - 非正方形画面でも歪みが円形に見えるようアスペクト比補正を入れる - IsActive() は Blend / Intensity / FadeDistance で判定し、無効時は RecordRenderGraph を早期 return してゼロコストにする - _intensity の既定値は既存 Volume の _strength と揃えて 0.0f とし、 Volume の Weight が 0 のとき(既定値へ補間される場合)も無効化されるようにする - NoiseTexture 未設定時は SetTexture を呼ばない。次元の異なる Texture2D.whiteTexture をフォールバックに使えないため Co-Authored-By: Claude Opus 5 --- .../Passes/HeatDistortionRenderPass.cs | 125 ++++++++++++++++++ .../Passes/HeatDistortionRenderPass.cs.meta | 2 + .../Features/SiriusPostProcessingFeature.cs | 13 ++ .../Scripts/Volumes/HeatDistortionVolume.cs | 105 +++++++++++++++ .../Volumes/HeatDistortionVolume.cs.meta | 2 + .../Runtime/Shaders/HeatDistortion.shader | 113 +++++++++++++++- 6 files changed, 353 insertions(+), 7 deletions(-) create mode 100644 SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs create mode 100644 SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs.meta create mode 100644 SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs create mode 100644 SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs.meta diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs new file mode 100644 index 0000000..acc7d77 --- /dev/null +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs @@ -0,0 +1,125 @@ +using Sirius.PostProcessing.Runtime.Scripts.Volumes; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.RenderGraphModule; +using UnityEngine.Rendering.Universal; + +namespace Sirius.PostProcessing.Runtime.Scripts.Features.Passes +{ + /// + /// HeatDistortion描画パス + /// + public sealed class HeatDistortionRenderPass : ScriptableRenderPass, IAllowExecute + { + public static string[] UsingShaderNameList { get; } = + { + "Hidden/Sirius/HeatDistortion" + }; + + public bool AllowExecute { get; set; } + + private static string ShaderName => UsingShaderNameList[0]; + private readonly ProfilingSampler _profilingSampler; + private Material _postProcessMaterial; + + /// + /// コンストラクタ + /// + public HeatDistortionRenderPass() + { + _profilingSampler = new ProfilingSampler("Sirius.HeatDistortion"); + } + + private void UpdateMaterialProperties(HeatDistortionVolume heatDistortionVolume) + { + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionIntensity, heatDistortionVolume.Intensity); + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionBlend, heatDistortionVolume.Blend); + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionStartDistance, heatDistortionVolume.StartDistance); + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionFadeDistance, heatDistortionVolume.FadeDistance); + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionSpeed, heatDistortionVolume.Speed); + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionChromaticSeparation, heatDistortionVolume.ChromaticSeparation); + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionZenithMask, heatDistortionVolume.ZenithMask ? 1.0f : 0.0f); + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionHorizonMask, heatDistortionVolume.HorizonMask ? 1.0f : 0.0f); + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionHorizonExponent, heatDistortionVolume.HorizonExponent); + _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionNoiseScale, heatDistortionVolume.NoiseScale); + // NoiseTextureはnullableなTexture2DArrayのため、未設定時はSetTextureを呼ばずシェーダー側のデフォルト値に委ねる + // (Texture2D.whiteTextureのような次元の異なるフォールバックを渡すと次元不一致になるため) + if (heatDistortionVolume.NoiseTexture != null) + { + _postProcessMaterial.SetTexture(ShaderPropertyIDs.HeatDistortionNoiseTex, heatDistortionVolume.NoiseTexture); + } + } + + public void Cleanup() + { + CoreUtils.Destroy(_postProcessMaterial); + _postProcessMaterial = null; + } + + /// + /// PropertyID + /// + private static class ShaderPropertyIDs + { + public static readonly int HeatDistortionIntensity = Shader.PropertyToID("_HeatDistortionIntensity"); + public static readonly int HeatDistortionBlend = Shader.PropertyToID("_HeatDistortionBlend"); + public static readonly int HeatDistortionStartDistance = Shader.PropertyToID("_HeatDistortionStartDistance"); + public static readonly int HeatDistortionFadeDistance = Shader.PropertyToID("_HeatDistortionFadeDistance"); + public static readonly int HeatDistortionSpeed = Shader.PropertyToID("_HeatDistortionSpeed"); + public static readonly int HeatDistortionChromaticSeparation = Shader.PropertyToID("_HeatDistortionChromaticSeparation"); + public static readonly int HeatDistortionZenithMask = Shader.PropertyToID("_HeatDistortionZenithMask"); + public static readonly int HeatDistortionHorizonMask = Shader.PropertyToID("_HeatDistortionHorizonMask"); + public static readonly int HeatDistortionHorizonExponent = Shader.PropertyToID("_HeatDistortionHorizonExponent"); + public static readonly int HeatDistortionNoiseScale = Shader.PropertyToID("_HeatDistortionNoiseScale"); + public static readonly int HeatDistortionNoiseTex = Shader.PropertyToID("_HeatDistortionNoiseTex"); + } + + private class PassData + { + public Material Mat; + public TextureHandle Source; + } + + public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) + { + var cameraData = frameData.Get(); + if (cameraData.camera.cameraType == CameraType.Preview) + { + return; + } + + if (cameraData.postProcessEnabled == false) + { + return; + } + if (_postProcessMaterial == null) + { + _postProcessMaterial = CoreUtils.CreateEngineMaterial(ShaderName); + } + + if (!VolumeManager.instance.IsComponentActiveInMask(cameraData.volumeLayerMask)) return; + + var heatDistortionVolume = VolumeManager.instance.stack.GetComponent(); + + if (!heatDistortionVolume.IsActive()) return; + + UpdateMaterialProperties(heatDistortionVolume); + + var resourceData = frameData.Get(); + var desc = renderGraph.GetTextureDesc(resourceData.activeColorTexture); + var destTextureHandle = renderGraph.CreateTexture(desc); + using (var builder = renderGraph.AddRasterRenderPass("Sirius.HeatDistortion", out var passData)) + { + builder.SetRenderAttachment(destTextureHandle, 0); + builder.UseTexture(resourceData.activeColorTexture); + passData.Source = resourceData.activeColorTexture; + passData.Mat = _postProcessMaterial; + builder.SetRenderFunc((PassData data, RasterGraphContext context) => + { + Blitter.BlitTexture(context.cmd, data.Source, Vector2.one, data.Mat, 0); + }); + } + resourceData.cameraColor = destTextureHandle; + } + } +} diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs.meta b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs.meta new file mode 100644 index 0000000..26d395a --- /dev/null +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: bcbd92daccc3b8a41b4f8a1a79d4303f \ No newline at end of file diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/SiriusPostProcessingFeature.cs b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/SiriusPostProcessingFeature.cs index 5fbaeb1..f39aefe 100644 --- a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/SiriusPostProcessingFeature.cs +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/SiriusPostProcessingFeature.cs @@ -26,6 +26,7 @@ public sealed partial class SiriusPostProcessingFeature : ScriptableRendererFeat { [SerializeField] private bool allowRadialBlurPostProcess; [SerializeField] private bool allowDirectionalBlurPostProcess; + [SerializeField] private bool allowHeatDistortionPostProcess; [AllowFlag("allowDirectionalBlurPostProcess")] private DirectionalBlurRenderPass _directionalBlurRenderPass; @@ -33,10 +34,14 @@ public sealed partial class SiriusPostProcessingFeature : ScriptableRendererFeat [AllowFlag("allowRadialBlurPostProcess")] private RadialBlurRenderPass _radialBlurRenderPass; + [AllowFlag("allowHeatDistortionPostProcess")] + private HeatDistortionRenderPass _heatDistortionRenderPass; + public override void Create() { _radialBlurRenderPass = new RadialBlurRenderPass(); _directionalBlurRenderPass = new DirectionalBlurRenderPass(); + _heatDistortionRenderPass = new HeatDistortionRenderPass(); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) @@ -66,12 +71,20 @@ public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingD _directionalBlurRenderPass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; renderer.EnqueuePass(_directionalBlurRenderPass); } + + // HeatDistortion + if (allowHeatDistortionPostProcess) + { + _heatDistortionRenderPass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; + renderer.EnqueuePass(_heatDistortionRenderPass); + } } protected override void Dispose(bool disposing) { _directionalBlurRenderPass.Cleanup(); _radialBlurRenderPass.Cleanup(); + _heatDistortionRenderPass.Cleanup(); } } } diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs new file mode 100644 index 0000000..e49f12d --- /dev/null +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs @@ -0,0 +1,105 @@ +using UnityEngine; +using UnityEngine.Rendering; + +namespace Sirius.PostProcessing.Runtime.Scripts.Volumes +{ + [VolumeComponentMenu("Sirius/HeatDistortion")] + public class HeatDistortionVolume : VolumeComponent, IPostProcessComponent + { + [SerializeField, Tooltip("歪みの強度")] + private ClampedFloatParameter _intensity = new(0.0f, 0.0f, 10.0f); + [SerializeField, Tooltip("歪み結果を元画像へ合成する割合(lerpのt)。0で元画像そのまま")] + private ClampedFloatParameter _blend = new(1.0f, 0.0f, 1.0f); + [SerializeField, Tooltip("効果が発生し始めるカメラからの距離")] + private ClampedFloatParameter _startDistance = new(20.0f, 0.0f, 1000.0f); + [SerializeField, Tooltip("効果が最大強度に達するカメラからの距離")] + private ClampedFloatParameter _fadeDistance = new(150.0f, 0.0f, 1000.0f); + [SerializeField, Tooltip("揺らぎパターンが流れる速さ")] + private ClampedFloatParameter _speed = new(0.1369f, 0.0f, 5.0f); + [SerializeField, Tooltip("色収差の強さ")] + private ClampedFloatParameter _chromaticSeparation = new(0.5f, 0.0f, 1.0f); + [SerializeField, Tooltip("天頂方向をマスクするかどうか")] + private BoolParameter _zenithMask = new(false); + [SerializeField, Tooltip("水平線方向をマスクするかどうか")] + private BoolParameter _horizonMask = new(true); + [SerializeField, Tooltip("水平線/天頂マスクの減衰カーブ")] + private ClampedFloatParameter _horizonExponent = new(2.5f, 0.1f, 10.0f); + [SerializeField, Tooltip("揺らぎノイズの座標スケール")] + private ClampedFloatParameter _noiseScale = new(1.7f, 0.0f, 10.0f); + [SerializeField, Tooltip("揺らぎパターン生成用の3Dノイズテクスチャ(Texture2DArray)")] + private TextureParameter _noiseTexture = new(null); + + public float Intensity + { + get => _intensity.value; + set => _intensity.value = value; + } + + public float Blend + { + get => _blend.value; + set => _blend.value = value; + } + + public float StartDistance + { + get => _startDistance.value; + set => _startDistance.value = value; + } + + public float FadeDistance + { + get => _fadeDistance.value; + set => _fadeDistance.value = value; + } + + public float Speed + { + get => _speed.value; + set => _speed.value = value; + } + + public float ChromaticSeparation + { + get => _chromaticSeparation.value; + set => _chromaticSeparation.value = value; + } + + public bool ZenithMask + { + get => _zenithMask.value; + set => _zenithMask.value = value; + } + + public bool HorizonMask + { + get => _horizonMask.value; + set => _horizonMask.value = value; + } + + public float HorizonExponent + { + get => _horizonExponent.value; + set => _horizonExponent.value = value; + } + + public float NoiseScale + { + get => _noiseScale.value; + set => _noiseScale.value = value; + } + + public Texture NoiseTexture + { + get => _noiseTexture.value; + set => _noiseTexture.value = value; + } + + public bool IsActive() + { + return Blend > 0.0f && Intensity > 0.0f && FadeDistance > 0.0f; + } + + public bool IsTileCompatible() => false; + } +} diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs.meta b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs.meta new file mode 100644 index 0000000..fccf370 --- /dev/null +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 19a67b886d9240d43bab0e4aee471186 \ No newline at end of file diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader b/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader index 4998836..39e51a1 100644 --- a/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader @@ -1,5 +1,19 @@ Shader "Hidden/Sirius/HeatDistortion" { + Properties + { + _HeatDistortionIntensity("Intensity", Float) = 1.0 + _HeatDistortionBlend("Blend", Float) = 1.0 + _HeatDistortionStartDistance("Start Distance", Float) = 20.0 + _HeatDistortionFadeDistance("Fade Distance", Float) = 150.0 + _HeatDistortionSpeed("Speed", Float) = 0.1369 + _HeatDistortionChromaticSeparation("Chromatic Separation", Float) = 0.5 + _HeatDistortionZenithMask("Zenith Mask", Float) = 0 + _HeatDistortionHorizonMask("Horizon Mask", Float) = 1 + _HeatDistortionHorizonExponent("Horizon Exponent", Float) = 2.5 + _HeatDistortionNoiseScale("Noise Scale", Float) = 1.7 + _HeatDistortionNoiseTex("Noise Texture (Texture2DArray)", 2DArray) = "" {} + } SubShader { Tags @@ -23,16 +37,101 @@ Shader "Hidden/Sirius/HeatDistortion" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl" + #include "Packages/jp.co.cyberagent.sirius.core/Runtime/Shaders/ScreenSpaceUtil.hlsl" + + uniform half _HeatDistortionIntensity; + uniform half _HeatDistortionBlend; + uniform half _HeatDistortionStartDistance; + uniform half _HeatDistortionFadeDistance; + uniform half _HeatDistortionSpeed; + uniform half _HeatDistortionChromaticSeparation; + uniform half _HeatDistortionZenithMask; + uniform half _HeatDistortionHorizonMask; + uniform half _HeatDistortionHorizonExponent; + uniform half _HeatDistortionNoiseScale; + + TEXTURE2D_ARRAY(_HeatDistortionNoiseTex); + SAMPLER(sampler_HeatDistortionNoiseTex); + + #define HEAT_DISTORTION_NOISE_SLICE_COUNT 64.0h + #define HEAT_DISTORTION_EDGE_TOLERANCE_RATIO 0.1h // エッジガードの許容深度差(カメラ距離に対する比率) + #define HEAT_DISTORTION_UV_OFFSET_SCALE 0.01h + #define HEAT_DISTORTION_CHROMA_SLICE_OFFSET 0.05h // 色収差のチャンネル間スライスずらし量(1.0 = 64スライス分) + + half SampleNoise(const half2 uv, const half sliceT) + { + const half sliceF = sliceT * HEAT_DISTORTION_NOISE_SLICE_COUNT; + const half sliceLo = floor(sliceF); + const half sliceFrac = sliceF - sliceLo; + const half sliceHi = fmod(sliceLo + 1.0h, HEAT_DISTORTION_NOISE_SLICE_COUNT); + const half noiseLo = SAMPLE_TEXTURE2D_ARRAY_LOD(_HeatDistortionNoiseTex, sampler_HeatDistortionNoiseTex, uv, sliceLo, 0).r; + const half noiseHi = SAMPLE_TEXTURE2D_ARRAY_LOD(_HeatDistortionNoiseTex, sampler_HeatDistortionNoiseTex, uv, sliceHi, 0).r; + return lerp(noiseLo, noiseHi, sliceFrac); + } half4 Frag(const Varyings input) : SV_Target { - // ワークショップ ワーク④: 陽炎(HeatDistortion) - // /ct-ai-dlc 陽炎(HeatDistortion)を実装 を実行してここを実装してください - // - // 画面の深度・3Dノイズ・視線方向マスクを使った歪みは自分で設計します。 - // 座標復元のヘルパは Sirius.Core に用意してあります: - // #include "Packages/jp.co.cyberagent.sirius.core/Runtime/Shaders/ScreenSpaceUtil.hlsl" - return half4(1.0h, 0.0h, 0.0h, 1.0h); + const half2 uv = input.texcoord; + const half depth = SampleSceneDepth(uv); + const half cameraDistance = (half)GetCameraDistance(uv, depth); + + // 距離減衰: StartDistance未満は0、FadeDistance以上は1 + const half distanceMask = saturate((cameraDistance - _HeatDistortionStartDistance) * + rcp(max(HALF_EPS, _HeatDistortionFadeDistance - _HeatDistortionStartDistance))); + + if (distanceMask <= 0.0h) + { + return SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv); + } + + // スクリーン座標をノイズUV、時間をスライス(Z軸)に対応させて3Dノイズ(Texture2DArray)をサンプルする。 + // ワールド座標xz基準だと遠方ほど1ピクセルあたりの座標変化が大きくなりノイズが高密度に潰れるため、 + // 距離減衰で歪みが最強になる遠方でかえって揺らぎが視認しにくくなる + const half2 noiseAspect = half2(_ScreenParams.x * rcp(_ScreenParams.y), 1.0h); + const half2 noiseUV = uv * noiseAspect * _HeatDistortionNoiseScale; + const half sliceT = frac(_Time.y * _HeatDistortionSpeed); + + // 水平線/天頂マスク: 視線の上下方向成分から重みを計算 + const half3 viewDir = normalize(GetCameraVector(uv)); + const half verticalDot = saturate(abs(viewDir.y)); + half angleMask = 1.0h; + if (_HeatDistortionHorizonMask > 0.5h) + { + angleMask *= pow(saturate(1.0h - verticalDot), _HeatDistortionHorizonExponent); + } + if (_HeatDistortionZenithMask > 0.5h) + { + angleMask *= pow(verticalDot, _HeatDistortionHorizonExponent); + } + + const half strength = _HeatDistortionIntensity * distanceMask * angleMask * HEAT_DISTORTION_UV_OFFSET_SCALE; + // アスペクト比補正(非正方形画面でも歪みが円形に見えるようにする) + const half2 aspectCorrection = half2(_ScreenParams.y * rcp(_ScreenParams.x), 1.0h); + // 色収差: R/G/Bそれぞれに独立したノイズをサンプルし、チャンネルごとに別のオフセットを作る。 + // 従来は単一ノイズから作った1本のオフセットを(1+c)/1/(1-c)倍していたため、 + // 3チャンネルのオフセットが常に同一直線上に並び、分離量の比が固定されていた + // ノイズのスライス(時間軸)をチャンネルごとにずらすことで、それぞれが異なる屈折量を持つ。 + // ChromaticSeparation=0 のとき3チャンネルが同一スライスに収束し、色ずれが消える + const half chromaSlice = _HeatDistortionChromaticSeparation * HEAT_DISTORTION_CHROMA_SLICE_OFFSET; + const half noiseR = SampleNoise(noiseUV, frac(sliceT + chromaSlice * 1.123456h)) * 2.0h - 1.0h; + const half noiseG = SampleNoise(noiseUV, sliceT) * 2.0h - 1.0h; + const half noiseB = SampleNoise(noiseUV, frac(sliceT - chromaSlice * 1.23456h + 1.0h)) * 2.0h - 1.0h; + + const half2 offsetR = noiseR * strength * aspectCorrection; + const half2 offsetG = noiseG * strength * aspectCorrection; + const half2 offsetB = noiseB * strength * aspectCorrection; + + const half1 colorR = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv + offsetR).r; + const half1 colorG = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv + offsetG).g; + const half1 colorB = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv + offsetB).b; + const half4 color = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv); + + // 合成係数: 距離フェードを「オフセット量」だけでなく「合成側」でも効かせる。 + // Blendは Volume から与える手動の合成率で、0なら元画像をそのまま返す + // (angleMaskはstrength側に留める。HorizonExponentが最大10のため二重掛けすると過剰に潰れる) + const half t = saturate(distanceMask * _HeatDistortionBlend); + + return lerp(color, half4(colorR, colorG, colorB, color.a), t); } ENDHLSL From 558be93c06ac97cc820ba692ac1614bc5088f7c8 Mon Sep 17 00:00:00 2001 From: Oikawa Date: Sun, 6 Sep 2026 15:40:29 +0900 Subject: [PATCH 2/7] =?UTF-8?q?Sirius.PostProcessing=20=E3=81=AE=E3=83=91?= =?UTF-8?q?=E3=83=83=E3=82=B1=E3=83=BC=E3=82=B8=E3=83=89=E3=82=AD=E3=83=A5?= =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E3=81=AB=20HeatDistortion=20?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit パス一覧へ HeatDistortionRenderPass を追加し、深度テクスチャ依存・ ノイズテクスチャの次元(Texture2DArray)・null 許容・既定値0でのゼロコスト 無効化といった、このパス固有の注意点を記載する。 Co-Authored-By: Claude Opus 5 --- .../skills/ct-pkg-sirius-postprocessing/SKILL.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/SiriusPackages/Sirius.PostProcessing/.claude/skills/ct-pkg-sirius-postprocessing/SKILL.md b/SiriusPackages/Sirius.PostProcessing/.claude/skills/ct-pkg-sirius-postprocessing/SKILL.md index 967f242..944eb01 100644 --- a/SiriusPackages/Sirius.PostProcessing/.claude/skills/ct-pkg-sirius-postprocessing/SKILL.md +++ b/SiriusPackages/Sirius.PostProcessing/.claude/skills/ct-pkg-sirius-postprocessing/SKILL.md @@ -19,6 +19,14 @@ description: "Sirius.PostProcessingパッケージの設計詳細。Use when: (1 |------|------------|--------|---------| | RadialBlurRenderPass | `allowRadialBlurPostProcess` | RadialBlurVolume | - | | DirectionalBlurRenderPass | `allowDirectionalBlurPostProcess` | DirectionalBlurVolume | - | +| HeatDistortionRenderPass | `allowHeatDistortionPostProcess` | HeatDistortionVolume | `_CameraDepthTexture` が必要 | -シェーダーは `Runtime/Shaders/`(`DirectionalBlur.shader` / `RadialBlur.shader` / `RotationBlur.shader`)。 +シェーダーは `Runtime/Shaders/`(`DirectionalBlur.shader` / `RadialBlur.shader` / `RotationBlur.shader` / `HeatDistortion.shader`)。 `Editor/Scripts/ShaderIncluder.cs` がリフレクションで各パスの `UsingShaderNameList` を収集し、ビルドの AlwaysIncludedShaders へ登録する。 + +## HeatDistortion(陽炎)固有の注意点 + +- **深度依存**: カメラ距離を `StartDistance`〜`FadeDistance` で減衰カーブに変換して歪み強度を決めるため、URP Renderer の `m_RequireDepthTexture` が有効である必要がある。深度は `Sirius.Core` の `ScreenSpaceUtil.hlsl`(`GetCameraDistance` / `GetCameraVector`)経由で参照しており、RenderGraph 側での `builder.UseTexture` 宣言は不要(`_CameraDepthTexture` は URP のグローバルテクスチャ)。 +- **ノイズテクスチャは Texture2DArray**: `_HeatDistortionNoiseTex` は `TEXTURE2D_ARRAY` で宣言している。プリセットの `3DCells64Sheet`(64 スライス)を想定し、時間をスライス番号に対応させて隣接 2 スライスを lerp する。**Texture3D や Texture2D を割り当てると次元不一致になる**ので、インポート設定の Texture Shape を `2D Array` にすること。 +- **null 許容**: `NoiseTexture` 未設定時は `RenderPass` 側で `SetTexture` を呼ばない。`Texture2D.whiteTexture` のような次元の異なるフォールバックを渡すと次元不一致になるため、他パスの `Mask` パターン(`mask ? mask : whiteTexture`)は流用できない。 +- **既定値 0 でゼロコスト**: `IsActive()` は `Blend > 0 && Intensity > 0 && FadeDistance > 0`。`_intensity` の既定値は他 Volume の `_strength` と同じく **0.0f** にしてある。これにより Volume の Weight を 0 にした場合(=既定値へ補間される場合)も `IsActive()` が false になり、パスがスキップされる。 From a6944eaed6b23fb55bf8917a70c3ca9278573124 Mon Sep 17 00:00:00 2001 From: Oikawa Date: Sun, 6 Sep 2026 15:40:42 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=E9=99=BD=E7=82=8E=E3=81=AE=E3=83=87?= =?UTF-8?q?=E3=83=A2=E3=82=B7=E3=83=BC=E3=83=B3=E3=82=92=E9=85=8D=E7=B7=9A?= =?UTF-8?q?=E3=81=97=E3=83=8E=E3=82=A4=E3=82=BA=E3=83=86=E3=82=AF=E3=82=B9?= =?UTF-8?q?=E3=83=81=E3=83=A3=E3=82=92=20Texture2DArray=20=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - URP Renderer の SiriusPostProcessingFeature で allowHeatDistortionPostProcess を有効化する - Workshop_HeatDistortion の Global Volume Profile へ HeatDistortionVolume を追加し、NoiseTexture にプリセットの 3DCells64Sheet を割り当てる - 3DCells64Sheet は Texture3D としてインポートされていたため、 シェーダー側の TEXTURE2D_ARRAY 宣言に合わせて Texture Shape を 2D Array へ変更する(Unity Editor が再生成した .meta) Co-Authored-By: Claude Opus 5 --- .../Global Volume Profile.asset | 50 ++++++++++++++++++- ...niversalRenderPipelineAsset_Renderer.asset | 1 + .../Assets/3DCells64Sheet.png.meta | 28 ++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/Assets/Demo/Workshop_HeatDistortion/Global Volume Profile.asset b/Assets/Demo/Workshop_HeatDistortion/Global Volume Profile.asset index 4fdfa0b..90d5c18 100644 --- a/Assets/Demo/Workshop_HeatDistortion/Global Volume Profile.asset +++ b/Assets/Demo/Workshop_HeatDistortion/Global Volume Profile.asset @@ -50,6 +50,53 @@ MonoBehaviour: k__BackingField: 218281240 m_OverrideState: 0 m_Value: 1.7 +--- !u!114 &-2917405184620356695 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 19a67b886d9240d43bab0e4aee471186, type: 3} + m_Name: HeatDistortionVolume + m_EditorClassIdentifier: Sirius.PostProcessing.Runtime::Sirius.PostProcessing.Runtime.Scripts.Volumes.HeatDistortionVolume + active: 1 + _intensity: + m_OverrideState: 1 + m_Value: 2 + _blend: + m_OverrideState: 1 + m_Value: 0.5 + _startDistance: + m_OverrideState: 1 + m_Value: 20 + _fadeDistance: + m_OverrideState: 1 + m_Value: 150 + _speed: + m_OverrideState: 1 + m_Value: 0.2 + _chromaticSeparation: + m_OverrideState: 1 + m_Value: 0.2 + _zenithMask: + m_OverrideState: 1 + m_Value: 0 + _horizonMask: + m_OverrideState: 1 + m_Value: 0 + _horizonExponent: + m_OverrideState: 1 + m_Value: 3 + _noiseScale: + m_OverrideState: 1 + m_Value: 2 + _noiseTexture: + m_OverrideState: 1 + m_Value: {fileID: 18700000, guid: f3f8592ee4a4db144887fb9fe80683b8, type: 3} + dimension: 1 --- !u!114 &11400000 MonoBehaviour: m_ObjectHideFlags: 0 @@ -62,4 +109,5 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3} m_Name: Global Volume Profile m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.VolumeProfile - components: [] + components: + - {fileID: -2917405184620356695} diff --git a/Assets/Settings/UniversalRenderPipelineAsset_Renderer.asset b/Assets/Settings/UniversalRenderPipelineAsset_Renderer.asset index 8dc65d3..d1ba876 100644 --- a/Assets/Settings/UniversalRenderPipelineAsset_Renderer.asset +++ b/Assets/Settings/UniversalRenderPipelineAsset_Renderer.asset @@ -15,6 +15,7 @@ MonoBehaviour: m_Active: 1 allowRadialBlurPostProcess: 1 allowDirectionalBlurPostProcess: 1 + allowHeatDistortionPostProcess: 1 --- !u!114 &11400000 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/SiriusAssets/Sirius.Core.Assets/Assets/3DCells64Sheet.png.meta b/SiriusAssets/Sirius.Core.Assets/Assets/3DCells64Sheet.png.meta index 329ded0..45c404c 100644 --- a/SiriusAssets/Sirius.Core.Assets/Assets/3DCells64Sheet.png.meta +++ b/SiriusAssets/Sirius.Core.Assets/Assets/3DCells64Sheet.png.meta @@ -55,7 +55,7 @@ TextureImporter: alphaIsTransparency: 0 spriteTessellationDetail: -1 textureType: 0 - textureShape: 8 + textureShape: 4 singleChannelComponent: 0 flipbookRows: 1 flipbookColumns: 64 @@ -93,6 +93,32 @@ TextureImporter: ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: 4 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: iOS + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: 4 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] From 5c3f3852f2d7e8fa08dcd8b27e1ccd0c3b17ec74 Mon Sep 17 00:00:00 2001 From: Oikawa Date: Sun, 6 Sep 2026 15:40:52 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=E9=99=BD=E7=82=8E=E3=81=AE=20AI-DLC=20?= =?UTF-8?q?=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88(intent?= =?UTF-8?q?=20/=20plan)=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 の Intent と Phase 2 の Plan。Construction および Phase 3 の品質 チェックで判明した設計変更・誤りを plan.md に反映済み。 - ノイズ座標をワールド座標からスクリーン座標へ変更 - エッジガードの不採用 - 色収差をチャンネル独立のノイズサンプリングへ変更 - _intensity 既定値を 0.0f 化 - UoW#5(答えドキュメント)の撤回。docs/workshop/answers/ はワーク①〜③の 答え合わせ用であり、仕込みバグのないワーク④には正解コードが存在しない Co-Authored-By: Claude Opus 5 --- .../2026-09-05-heat-distortion/intent.md | 37 +++++ .../ai-dlc/2026-09-05-heat-distortion/plan.md | 133 ++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 docs/ai-dlc/2026-09-05-heat-distortion/intent.md create mode 100644 docs/ai-dlc/2026-09-05-heat-distortion/plan.md diff --git a/docs/ai-dlc/2026-09-05-heat-distortion/intent.md b/docs/ai-dlc/2026-09-05-heat-distortion/intent.md new file mode 100644 index 0000000..c5e4d2f --- /dev/null +++ b/docs/ai-dlc/2026-09-05-heat-distortion/intent.md @@ -0,0 +1,37 @@ +# HeatDistortion(陽炎エフェクト) + +## Description + +奥行き方向の温度差によって背景が揺らめいて見える、大気ゆらぎ(陽炎)表現のポストプロセスエフェクトを SIRIUS のポストプロセス機構に追加する。シーンの深度情報を手がかりに揺らぎの発生領域・強度を制御し、3D ノイズテクスチャで揺らぎのパターンを生成、背景のサンプリング座標をオフセットすることで屈折しているように見せる。 + +## Context + +- **配置先パッケージ(候補)**: `Sirius.PostProcessing`。既存の `RadialBlur` / `DirectionalBlur` / `RotationBlur` と同じ `VolumeComponent` + `ScriptableRenderPass` + `AllowFlag` 構成に倣う想定(最終決定は plan.md)。 +- **既存類似機構との関係**: 既存 3 種のポストエフェクトと同様、`SiriusPostProcessingFeature` に新規パスを追加する形で統合する。既存パスとの共存(同時 ON)を想定する。 +- **利用素材の指定**: 揺らぎパターン生成用の 3D ノイズテクスチャとして、既存プリセット `3DCells64Sheet`(`SiriusAssets/Sirius.Core.Assets/Assets/3DCells64Sheet.png`)を利用する。新規ノイズテクスチャは作成しない。 +- **利用想定**: レベルデザイナー / エフェクトデザイナーが Volume Profile 上でパラメータ(強度・スケール等)を調整し、シーンに配置して使う。 +- **動的軸**: 静的。Inspector / Volume Profile で事前設定する運用のみを想定し、ランタイムでの ON/OFF・強度切替は不要(既存の `RotationBlurVolume` 等と同じ運用)。 +- **パフォーマンス制約**: 既存のポストエフェクト群と同程度のオーダー(フルスクリーンパス 1 回分程度)に収まること。OFF 時(強度 0 相当)は本機能由来の追加コストが実質発生しないこと。 + +## Completion Criteria + +### ✅ 成功ケース + +- Volume Profile 上でパラメータ(強度・ノイズスケール等)を調整すると、深度に応じた背景の揺らぎ度合いが変化して見える +- 既存の `RadialBlur` / `DirectionalBlur` / `RotationBlur` と同時に ON にしても、双方の効果が破綻せず共存する +- Volume の Weight/強度を 0 にすると、視覚的に効果が発生しない(既存の `IsActive()` パターンに準ずる無効化判定を持つ) + +### ❌ 失敗ケース + +- 強度 0(無効状態)でも本機能由来の追加パス実行・追加コストが発生しない +- 深度の不連続点(オブジェクトのエッジ)で背景ピクセルのにじみ出し等、破綻した見た目のアーティファクトが出ない +- ノイズテクスチャが未設定(null)の状態でも例外・クラッシュを起こさない + +### 🔒 品質ゲート + +- コンパイル時エラー 0 / 新規警告 0 +- 既存および新規の自動テスト(EditMode / PlayMode)が pass +- ビジュアルリグレッションテストで既存シーンに意図しない差分がないこと +- 人手レビュー pass + +具体の検証手段(コマンド / スキル / 計測ツール)は Phase 2 (Inception) で plan.md に記述する。 diff --git a/docs/ai-dlc/2026-09-05-heat-distortion/plan.md b/docs/ai-dlc/2026-09-05-heat-distortion/plan.md new file mode 100644 index 0000000..37f78aa --- /dev/null +++ b/docs/ai-dlc/2026-09-05-heat-distortion/plan.md @@ -0,0 +1,133 @@ +# HeatDistortion(陽炎)実装計画 + + + +## Step 1 調査サマリ(既存パターンの言語化) + +- **RenderPass は Volume 駆動の自己完結原則**: `RadialBlurRenderPass` / `DirectionalBlurRenderPass` / `RotationBlurRenderPass` はいずれも `VolumeManager.instance.stack.GetComponent()` で Volume の値だけを読み、シーン側の状態を参照しない。`IsActive()` が false なら `RecordRenderGraph` が早期 return し、パス自体が実質何もしない(ゼロコスト無効化)。 +- **Volume 公開パターン**: `ClampedFloatParameter` / `BoolParameter` / `TextureParameter` を `[SerializeField]` で持ち、同名の `get/set` プロパティを公開。`Mask` 系テクスチャは常に nullable(`TextureParameter(null)` がデフォルト)で、パス側が `mask ? mask : Texture2D.whiteTexture` のようにフォールバックする。 +- **パスの実装は単一 Blit パス**: `renderGraph.CreateTexture` → `AddRasterRenderPass` → `Blitter.BlitTexture(cmd, source, Vector2.one, mat, 0)` → `resourceData.cameraColor = dest` という定型。マルチパス/追加 RT は使っていない。 +- **深度アクセスは Sirius.Core の共通 hlsl 経由**: `ScreenSpaceUtil.hlsl` の `GetWorldPosition` / `GetCameraDistance` / `DeclareDepthTexture.hlsl` の `SampleSceneDepth` は `_CameraDepthTexture`(URP グローバルテクスチャ)を直接参照する設計で、RenderGraph 側の `builder.UseTexture` 宣言は不要(既存 3 パスも同様にグローバルテクスチャは明示宣言していない)。`Assets/Settings/UniversalRenderPipelineAsset_Renderer.asset` は `m_RequireDepthTexture: 1` 済みで、深度テクスチャは既に有効化されている(追加設定不要)。 +- **ノイズテクスチャの実体**: intent 記載の `3DCells64Sheet.png` は `flipbookColumns: 64` で、64 枚のスライスを持つシート状のノイズ。`TEXTURE2D_ARRAY` / `SAMPLE_TEXTURE2D_ARRAY_LOD` でスライス指定サンプリングする(3D ボリュームノイズの疑似トライリニアは スライス N / N+1 を手動 lerp する)。 + - ⚠️ **Construction時の修正(Phase 2 の調査誤り)**: 当初 plan には「`.meta` 上 `textureShape: 8`(Texture2DArray)としてインポート済み」と記載していたが、**これは誤り**。`TextureImporterShape` の実値は `Texture2D=1 / TextureCube=2 / Texture2DArray=4 / Texture3D=8` であり、`8` は **Texture3D** を指す。つまり当該アセットは Texture3D としてインポートされていた。本実装の `TEXTURE2D_ARRAY` を成立させるため、Construction 中に **Inspector で Texture2DArray(`textureShape: 4`)へ変更**しており、この `.meta` 変更は本機能に必須の差分として PR に含める。 + - なお元が Texture3D だったため、`TEXTURE3D` + ハードウェアのトライリニア補間で実装する選択肢も本来あった(スライスの手動 lerp が不要になる)。本 Construction では既に Texture2DArray 前提で実装が進んでいたためそのまま採用したが、再設計するならこちらの方が素直。 +- **multi_compile は不使用**: 3 パスとも shader keyword 分岐なし。固定 1 パーミュテーションが既存原則。 +- **参考データの発見(重要・注意点あり)**: `Assets/Demo/Workshop_HeatDistortion/Global Volume Profile.asset` 内に、`components: []`(未適用)だが孤立した `HeatDistortionVolume` のシリアライズ済みブロックが残存している。フィールド名とデフォルト値は以下の通りで、採用設計のパラメータ命名の参考にした: + - `_Intensity` = 1, `_StartDistance` = 20, `_FadeDistance` = 150, `_Speed` = 0.1369, `_ChromaticSeparation` = 0.5, `_ZenithMask`(bool, override off) = 0, `_HorizonMask`(bool, override off) = 1, `_HorizonExponent`(override off) = 2.5, `_NoiseScale`(override off) = 1.7 + - ⚠️ **注意**: この孤立データの `m_EditorClassIdentifier` は `Sirius.PostProcessing.HeatDistortionVolume`(名前空間 `Sirius.PostProcessing` 直下)であり、本リポジトリの現行規約(`Sirius.PostProcessing.Runtime.Scripts.Volumes` 名前空間、`RotationBlurVolume` 等参照)と異なる。製品版 Sirius など別コードベース由来の残骸の可能性が高いため、**名前空間はこの孤立データに合わせず、現行 `Volumes` 名前空間に従う**。フィールド名/デフォルト値のみ参考として採用。 + - このデータには **ノイズテクスチャ用フィールドが存在しない**(テクスチャ関連の項目が一切ない)。一方 intent.md の失敗ケースには「ノイズテクスチャ未設定(null)でもクラッシュしない」という要件が明記されており、null になり得る=ユーザーが差し替え可能な公開フィールドの存在を前提にした文言。孤立データの分解元が不明な簡略実装である可能性を踏まえ、**本設計では既存 3 エフェクトの `Mask` パターンに倣い `NoiseTexture` を nullable `TextureParameter` として公開する**(既存原則との整合を優先)。 + +## 採用設計 + +**Plan A: 既存 3 エフェクトと同一の Volume 駆動 + RenderGraph 単一 Blit パス構成** + +- `HeatDistortionVolume`(`VolumeComponentMenu("Sirius/HeatDistortion")`): `Intensity` / `Blend`(Construction時に追加した合成率)/ `StartDistance` / `FadeDistance` / `Speed` / `ChromaticSeparation` / `ZenithMask`(bool) / `HorizonMask`(bool) / `HorizonExponent` / `NoiseScale` / `NoiseTexture`(nullable `TextureParameter`) +- `HeatDistortionRenderPass`: 既存 3 パスと同型(`ScriptableRenderPass` + `IAllowExecute`、`UsingShaderNameList`、`UpdateMaterialProperties`、`RecordRenderGraph` で Blit 1 回)。Volume から読んだ値を `SetFloat`/`SetVector`/`SetTexture` でマテリアルに渡す。 +- `HeatDistortion.shader`(既存スタブの `Frag` を実装): + 1. `SampleSceneDepth` → `GetViewPosition`/`GetCameraDistance` でカメラ距離を取得し、`StartDistance`〜`FadeDistance` で強度を減衰させる距離カーブを作る + 2. スクリーン座標 UV(アスペクト比補正込み)を Texture2DArray の xy、`_Time.y * Speed` をスライス番号(0〜63)の連続値として使い、隣接 2 スライスを lerp してノイズを取得(`NoiseScale` で座標スケール) + - Construction時の修正: 当初案はワールド座標 xz を xy に使う設計だったが、実装後の目視確認で**遠方ほど 1 ピクセルあたりのワールド座標変化が大きくなりノイズが高密度に潰れる**ことが判明。距離減衰(`StartDistance`〜`FadeDistance`)で歪みが最も強くなる遠方領域と、ノイズが最も視認しにくくなる領域が一致してしまうため、スクリーン座標基準に変更した。副作用としてノイズパターンがカメラ移動に追従せず画面に貼り付く挙動になるが、陽炎表現としては一般的な手法であり許容する。`NoiseScale` の意味は「ワールド単位あたりのタイル数」から「画面高さあたりのタイル数」に変わる(既定値 1.7・範囲 0〜10 はそのまま流用可能なため Volume 側は変更なし) + 3. カメラ前方ベクトルと世界上方向の角度から Horizon/Zenith マスクを計算し、`HorizonMask`/`ZenithMask`/`HorizonExponent` で重み付け + 4. ノイズ値を UV オフセットに変換(`RotationBlur.shader` と同様にアスペクト比補正を必ず入れる。ワークショップ資料 `01_intro_ai_dlc.md` に明記された「AI が省略しがちなバグ」の再発を避けるため、Construction 時に意図的に最初から入れる) + 5. **エッジにじみ防止**: オフセット先 UV でも深度をサンプルし、オフセット元との深度差が一定閾値を超える場合はオフセットを 0 に戻す(intent の失敗ケース「深度の不連続点でにじみ出ない」を満たすための実装。閾値は固定の内部定数とし、Volume パラメータとしては公開しない) + - **Construction時の修正(最終的に不採用)**: 当初の「絶対深度差 2.0 ワールド単位を超えたらオフセットを 0 にする」二値ガードは、シーンの被写体距離が 180〜230 あるため**ほぼ全画面で発動**し、エフェクトが視認できなくなった(A/B スクリーンショットで確認)。原因は (a) 閾値が絶対距離で遠景の地面のような平坦面でも即座に振り切れる (b) `abs` で「奥方向へのずれ」まで弾いている (c) 二値ステップで境界がちらつく、の 3 点。「方向性あり・カメラ距離に対する相対閾値・`smoothstep` でソフト」な形へ作り替えたうえで、適用先もオフセット量から**合成係数側**へ移した。最終的にはユーザー判断でガード自体を削除し、`t` は `distanceMask * Blend` の 2 項に落ち着いている + 6. `ChromaticSeparation` で RGB チャンネルごとにオフセット量をずらし、色収差混じりの陽炎らしい見た目にする + - **Construction時の修正**: 当初は単一ノイズから作った 1 本のオフセットを `(1+c) / 1 / (1-c)` 倍する方式だったが、3 チャンネルのオフセットが常に同一直線上に並び分離量の比が固定されるため、**R/G/B それぞれに独立したノイズをサンプルする**方式へ変更した。ノイズのスライス(時間軸)をチャンネルごとにずらす(`HEAT_DISTORTION_CHROMA_SLICE_OFFSET`)ことで、`ChromaticSeparation = 0` のとき 3 チャンネルが同一スライスに収束して色ずれが消える。コストは `SampleNoise` が 1 回 → 3 回(テクスチャフェッチ 2 → 6 タップ) + 7. **合成**: 歪み結果を元画像へ `lerp(color, distorted, t)` で合成する。`t = saturate(distanceMask * Blend)`。`Blend` は Volume に公開した合成率(0 で元画像そのまま)で、`IsActive()` にも含めているため **Blend = 0 のときはパス自体がスキップされる**(ゼロコスト無効化)。`angleMask` は `strength`(オフセット量)側に留める(`HorizonExponent` が最大 10 のため二重掛けすると過剰に潰れる) +- `IsActive()`: `Blend > 0 && Intensity > 0 && FadeDistance > 0`(`NoiseTexture` が null でも例外にならないことは RenderPass 側で `SetTexture` を呼ばないことにより担保する) + - **Construction時の修正**: 当初は `Intensity > 0 && FadeDistance > 0` のみだったが、Volume に `Blend` を追加した際に条件へ加えた。これにより Blend = 0 で確実にパスがスキップされる。 + - **Phase 3 品質チェック時に解消**: `_intensity` の既定値が 1.0f のままだと、**Volume の weight を 0 にしても Intensity は既定値 1.0 に補間されるだけで 0 にならず**、intent の成功ケース「Volume の Weight/強度を 0 にすると視覚的に効果が発生しない」を weight 経由で満たせなかった。既存 `RotationBlurVolume._strength` / `RadialBlurVolume._strength` はいずれも既定値 0.0f でこの問題がない(一方シェーダー側 Property の既定値は 1.0 のまま、というのが既存 3 エフェクト共通の規約)。この house convention に合わせ `SiriusPackages/.../HeatDistortionVolume.cs` の `_intensity` 既定値を **0.0f に変更**した(シェーダーの Properties ブロックは 1.0 のまま据え置き)。デモの `Global Volume Profile.asset` は `_intensity` を `m_OverrideState: 1` で明示 override(値 2)しているため、この既定値変更によるデモの見た目への影響はない + +## 棄却した代替案 + +### Plan B: MonoBehaviour + MaterialPropertyBlock 駆動 +棄却理由: intent.md の動的軸が「静的」と明記されており、Volume の Weight/Inspector 運用で十分。かつ `Sirius.PostProcessing` の既存 3 エフェクトは全て Volume 駆動であり、本機能だけ MonoBehaviour 経路にする構造的必然性がない(既存原則との異質性が唯一の理由になってしまい、客観的な採用理由が作れない)。 + +### Plan C: 2 パス構成(オフセット計算 RT を先に生成 → 合成パスで適用) +棄却理由: 既存 3 エフェクトは全てテクセルごとに 1 回の `_BlitTexture` サンプルで完結する単一 Blit パス。本機能もテクセルごとに「深度 1 回サンプル → ノイズ 1〜2 回サンプル → オフセット後に 1 回サンプル」で完結でき、追加 RT を経由する理由がない。RenderGraph のリソース数・UoW 数が増えるだけで既存原則から逸脱する。 + +## Units of Work + +### UoW#1 [Volume] 依存なし +- 対象: `SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs`(新規) +- 追加/変更ファイル: 上記 1 ファイル +- 依存: なし +- 担当: AI +- コミット先: SiriusPackages + +### UoW#2 [RenderPass] 依存: UoW#1 +- 対象: `SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs`(新規) +- 追加/変更ファイル: 上記 1 ファイル +- 依存: UoW#1(`HeatDistortionVolume` の型・プロパティを参照するため) +- 担当: AI +- コミット先: SiriusPackages + +### UoW#3 [Shader] 依存: UoW#1(プロパティ命名を合わせるため) +- 対象: `SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader`(既存スタブを編集) +- 追加/変更ファイル: 上記 1 ファイル。**Shader 名 `Hidden/Sirius/HeatDistortion` は既存スタブのまま維持**(他 3 シェーダーは `...Pass` サフィックス付きだが、本ファイルは既存スタブ命名を尊重しリネームしない) +- 依存: UoW#1 +- 担当: AI +- コミット先: SiriusPackages + +### UoW#4 [Feature配線] 依存: UoW#2 +- 対象: `SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/SiriusPostProcessingFeature.cs` +- 追加/変更ファイル: `allowHeatDistortionPostProcess` フィールド+`[AllowFlag]` ペア追加、`Create()`/`AddRenderPasses()`/`Dispose()` へ配線 +- 依存: UoW#2 +- 担当: AI +- コミット先: SiriusPackages + +### UoW#5 [答えドキュメント] — ❌ **撤回(Phase 3 品質チェック時にユーザー指摘で判明した Phase 2 の誤り)** + +当初「`docs/workshop/answers/` へ `HeatDistortionVolume.cs` / `HeatDistortionRenderPass.cs` / `HeatDistortion.shader` の答え一式を追加する」としていたが、これは **`answers/` の役割を取り違えた誤り**だったため撤回し、実装済みだった 3 ファイルとそのコミットを削除した。 + +**誤りの根拠:** +- `docs/workshop/answers/` は **ワーク①〜③の「答え合わせ」用**ディレクトリ。README.md の「各ワークの『答え』は `docs/workshop/answers/` にあります」という注記は、ワーク①〜③のフロー説明(「② AI が『正しそうに見える』コードを作る(ここに不具合が仕込まれています)→ ③ 受講者が見つける → ④ 答え合わせ」)の直後に置かれている。README_DEVELOPERS.md も `RotationBlur.shader` / `DirectionalBlur.shader` を「正解コード」として参照している +- ワーク④について README は「こちらには**仕込みの不具合はありません**。AI が書いたコードが正しいかどうかを、あなた自身が確かめてください」と明記しており、**正解コードが存在しない**。受講者ごとに実装が異なるのが前提 +- したがって自分の実装を「答え」として `answers/` に置くのは筋が違う。fork 元への PR に含めると、受講者が講師側のコンテンツを上書きする形になる +- 加えてパッケージ実体との二重管理になる(実際、`_intensity` 既定値の修正を 2 箇所に入れる必要が生じた) + +**Phase 2 での誤推論:** 「RotationBlur(ワーク③)の答えが Volume / RenderPass / Shader の 3 ファイルあるから、同じくゼロから実装するワーク④も 3 ファイル構成にする」と考えたが、RotationBlur に答えがあるのは**ワーク③が仕込みバグを探す演習で正解が一意に定まるから**であって、ファイル数の問題ではなかった。 + +### UoW#6 [デモ配線] 依存: UoW#1〜4 +- 対象: `Assets/Settings/UniversalRenderPipelineAsset_Renderer.asset`(`allowHeatDistortionPostProcess: 1` 追加)、`Assets/Demo/Workshop_HeatDistortion/Global Volume Profile.asset`(`HeatDistortionVolume` を `components` に追加し、`NoiseTexture` に `3DCells64Sheet` を割り当て) +- 追加/変更ファイル: 上記 2 ファイル +- 依存: UoW#1〜4(Volume/Pass/配線が存在しないと Inspector 上で選択できない) +- 担当: **人間(Unity Editor 上での操作)** — 本スキルの既定方針(Phase 4 相当の Unity 手動設定はユーザーに案内する)に従う。自動配線が必要な場合は `workshop-rotation-blur-setup` に相当する専用スキルを別途作成できるが、本 Plan のスコープ外(ユーザーが明示的に依頼した場合のみ) +- コミット先: ルート直下(`Assets/`) + +## 並列可能ペア + +- ~~UoW#5(答えドキュメント)‖ UoW#4(Feature配線)~~ — UoW#5 は撤回したため該当なし +- UoW#1〜4 は基本的に直列(Volume→Pass→Shader命名確定→Feature配線)。本 Construction では並列実行はしない方針だが、依存関係の理解のため明示 + +## 触ってはいけないファイル + +- `*.meta` 全般(既存 GUID 維持、新規は Unity Editor 任せ) +- `allowRadialBlurPostProcess` / `allowDirectionalBlurPostProcess` / `allowRotationBlurPostProcess`(既存 3 エフェクトの allow フラグ値) +- `docs/workshop/answers/` 配下すべて — ワーク①〜③の「答え合わせ」用ディレクトリであり、**ワーク④(陽炎)は仕込みバグのない新規実装ワークなので正解コードが存在せず、本機能のファイルは一切追加しない**(当初 UoW#5 として追加していたが撤回。詳細は UoW#5 の項を参照) +- `Assets/Tests/Runtime/AverageTest.cs` への `Workshop_HeatDistortion` テストケース追加 — **明示的に対象外**。同ファイル 113 行目のコメント「ワーク④(陽炎)・ワーク⑤(光芒)はゼロからの新規実装ワークのため、ビジュアル回帰テストの対象外」により、本機能は AverageTest の新規 TestCase を追加しない。品質ゲートは「コンパイル 0 エラー」「既存 3 ケース(RadialBlur/DirectionalBlur/RotationBlur)に回帰がないこと」「人手での Unity Editor 上の目視確認」に読み替える +- サブモジュールポインタ(該当があれば) +- `Assets/Demo/Workshop_HeatDistortion/*.mat`(`Mat_Near`/`Mat_Mid`/`Mat_Far`/`Mat_Ground` — 深度テスト用に既に配置済みのため変更不要) + +## PR 構成 + +単一リポジトリ(サブモジュールなし)のため PR は 1 本。 + +**ブランチ方針(Phase 3 で確定)**: 現行ブランチ `feat/rotation-blur` には未マージの RotationBlur コミット(ワーク③)が乗っており、`SiriusPostProcessingFeature.cs` と `Assets/Settings/UniversalRenderPipelineAsset_Renderer.asset` の 2 ファイルを本機能と共有している。ユーザー判断により **RotationBlur は放置(マージしない)** となったため、`origin/main` からブランチを作成し、この 2 ファイルは「`origin/main` の内容 + HeatDistortion の追加分のみ」に再構成する(RotationBlur の配線は含めない)。 + +ブランチ名は **`feat/heatDistortion`**(README ワーク④ step-1 の指定プロンプトの文言に合わせる。当初 plan には `feat/heat-distortion` と書いていたが、README の指定を優先した)。PR の提出先は fork 元 `CyberAgentGameEntertainment/GraphicsAcademyLight` の `main`。 + +コミットは性質ごとに分割(Phase 3 実施結果): + +1. パッケージ実装コミット(UoW#1〜4)— `SiriusPackages/Sirius.PostProcessing/`(Volume / RenderPass / Shader / Feature 配線 + 新規 `.meta`) +2. パッケージドキュメントコミット — `SiriusPackages/Sirius.PostProcessing/.claude/skills/ct-pkg-sirius-postprocessing/SKILL.md`(Phase 3 Step 7-2。パス一覧への追加と陽炎固有の注意点) +3. デモ配線・アセットコミット(UoW#6)— `Assets/Settings/UniversalRenderPipelineAsset_Renderer.asset`, `Assets/Demo/Workshop_HeatDistortion/Global Volume Profile.asset`, `SiriusAssets/Sirius.Core.Assets/Assets/3DCells64Sheet.png.meta`(Texture3D → Texture2DArray 変更。本機能に必須) +4. AI-DLC ドキュメントコミット — `docs/ai-dlc/2026-09-05-heat-distortion/`(intent.md / plan.md) + +※ 当初 2 番目に置いていた「答えドキュメントコミット(UoW#5)」は撤回した(UoW#5 の項を参照)。 + +**PR に含めない作業ツリー上の変更**(`git add` はファイル名を明示して除外する): +`.claude/settings.json`, `ProjectSettings/GraphicsRegressionTestSettings.asset`, `ProjectSettings/ShaderGraphSettings.asset`, `ProjectSettings/TimelineSettings.asset`, `ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json`, `.vsconfig`(未追跡), `GraphicsAcademyLight.slnx`(未追跡) From 17d40bd32996e9ddbd7ebaca5599055ff25efdb6 Mon Sep 17 00:00:00 2001 From: Oikawa Date: Sun, 6 Sep 2026 16:56:23 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=E6=9C=AA=E4=BD=BF=E7=94=A8=E3=81=AE?= =?UTF-8?q?=E8=A7=92=E5=BA=A6=E3=83=9E=E3=82=B9=E3=82=AF=E3=83=91=E3=83=A9?= =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=82=BF=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 視線の上下方向成分で歪みを減衰させる角度マスク(水平線/天頂)を廃止する。 デモの Global Volume Profile では ZenithMask / HorizonMask / HorizonExponent のいずれも override off のまま運用されており、調整対象になっていなかった。 - HeatDistortionVolume から ZenithMask / HorizonMask / HorizonExponent を フィールド・公開プロパティごと削除する - HeatDistortionRenderPass の対応する SetFloat と ShaderPropertyIDs を削除する - シェーダーの Properties / uniform 宣言、angleMask の算出と strength への 乗算を削除する - angleMask 専用だった viewDir / verticalDot も未使用になるため削除する。 GetCameraDistance は引き続き使うため ScreenSpaceUtil.hlsl の include は残す HorizonExponent は角度マスクの減衰カーブ専用パラメータであり、マスク廃止で 参照元が無くなったため併せて削除した。これにより遠景の歪みは視線方向に よらず一様にかかるようになる。 Co-Authored-By: Claude Opus 5 --- .../Passes/HeatDistortionRenderPass.cs | 6 ----- .../Scripts/Volumes/HeatDistortionVolume.cs | 24 ------------------- .../Runtime/Shaders/HeatDistortion.shader | 22 +---------------- 3 files changed, 1 insertion(+), 51 deletions(-) diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs index acc7d77..42d71a2 100644 --- a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs @@ -38,9 +38,6 @@ private void UpdateMaterialProperties(HeatDistortionVolume heatDistortionVolume) _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionFadeDistance, heatDistortionVolume.FadeDistance); _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionSpeed, heatDistortionVolume.Speed); _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionChromaticSeparation, heatDistortionVolume.ChromaticSeparation); - _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionZenithMask, heatDistortionVolume.ZenithMask ? 1.0f : 0.0f); - _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionHorizonMask, heatDistortionVolume.HorizonMask ? 1.0f : 0.0f); - _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionHorizonExponent, heatDistortionVolume.HorizonExponent); _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionNoiseScale, heatDistortionVolume.NoiseScale); // NoiseTextureはnullableなTexture2DArrayのため、未設定時はSetTextureを呼ばずシェーダー側のデフォルト値に委ねる // (Texture2D.whiteTextureのような次元の異なるフォールバックを渡すと次元不一致になるため) @@ -67,9 +64,6 @@ private static class ShaderPropertyIDs public static readonly int HeatDistortionFadeDistance = Shader.PropertyToID("_HeatDistortionFadeDistance"); public static readonly int HeatDistortionSpeed = Shader.PropertyToID("_HeatDistortionSpeed"); public static readonly int HeatDistortionChromaticSeparation = Shader.PropertyToID("_HeatDistortionChromaticSeparation"); - public static readonly int HeatDistortionZenithMask = Shader.PropertyToID("_HeatDistortionZenithMask"); - public static readonly int HeatDistortionHorizonMask = Shader.PropertyToID("_HeatDistortionHorizonMask"); - public static readonly int HeatDistortionHorizonExponent = Shader.PropertyToID("_HeatDistortionHorizonExponent"); public static readonly int HeatDistortionNoiseScale = Shader.PropertyToID("_HeatDistortionNoiseScale"); public static readonly int HeatDistortionNoiseTex = Shader.PropertyToID("_HeatDistortionNoiseTex"); } diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs index e49f12d..077700b 100644 --- a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Volumes/HeatDistortionVolume.cs @@ -18,12 +18,6 @@ public class HeatDistortionVolume : VolumeComponent, IPostProcessComponent private ClampedFloatParameter _speed = new(0.1369f, 0.0f, 5.0f); [SerializeField, Tooltip("色収差の強さ")] private ClampedFloatParameter _chromaticSeparation = new(0.5f, 0.0f, 1.0f); - [SerializeField, Tooltip("天頂方向をマスクするかどうか")] - private BoolParameter _zenithMask = new(false); - [SerializeField, Tooltip("水平線方向をマスクするかどうか")] - private BoolParameter _horizonMask = new(true); - [SerializeField, Tooltip("水平線/天頂マスクの減衰カーブ")] - private ClampedFloatParameter _horizonExponent = new(2.5f, 0.1f, 10.0f); [SerializeField, Tooltip("揺らぎノイズの座標スケール")] private ClampedFloatParameter _noiseScale = new(1.7f, 0.0f, 10.0f); [SerializeField, Tooltip("揺らぎパターン生成用の3Dノイズテクスチャ(Texture2DArray)")] @@ -65,24 +59,6 @@ public float ChromaticSeparation set => _chromaticSeparation.value = value; } - public bool ZenithMask - { - get => _zenithMask.value; - set => _zenithMask.value = value; - } - - public bool HorizonMask - { - get => _horizonMask.value; - set => _horizonMask.value = value; - } - - public float HorizonExponent - { - get => _horizonExponent.value; - set => _horizonExponent.value = value; - } - public float NoiseScale { get => _noiseScale.value; diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader b/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader index 39e51a1..c622433 100644 --- a/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader @@ -8,9 +8,6 @@ Shader "Hidden/Sirius/HeatDistortion" _HeatDistortionFadeDistance("Fade Distance", Float) = 150.0 _HeatDistortionSpeed("Speed", Float) = 0.1369 _HeatDistortionChromaticSeparation("Chromatic Separation", Float) = 0.5 - _HeatDistortionZenithMask("Zenith Mask", Float) = 0 - _HeatDistortionHorizonMask("Horizon Mask", Float) = 1 - _HeatDistortionHorizonExponent("Horizon Exponent", Float) = 2.5 _HeatDistortionNoiseScale("Noise Scale", Float) = 1.7 _HeatDistortionNoiseTex("Noise Texture (Texture2DArray)", 2DArray) = "" {} } @@ -45,9 +42,6 @@ Shader "Hidden/Sirius/HeatDistortion" uniform half _HeatDistortionFadeDistance; uniform half _HeatDistortionSpeed; uniform half _HeatDistortionChromaticSeparation; - uniform half _HeatDistortionZenithMask; - uniform half _HeatDistortionHorizonMask; - uniform half _HeatDistortionHorizonExponent; uniform half _HeatDistortionNoiseScale; TEXTURE2D_ARRAY(_HeatDistortionNoiseTex); @@ -91,20 +85,7 @@ Shader "Hidden/Sirius/HeatDistortion" const half2 noiseUV = uv * noiseAspect * _HeatDistortionNoiseScale; const half sliceT = frac(_Time.y * _HeatDistortionSpeed); - // 水平線/天頂マスク: 視線の上下方向成分から重みを計算 - const half3 viewDir = normalize(GetCameraVector(uv)); - const half verticalDot = saturate(abs(viewDir.y)); - half angleMask = 1.0h; - if (_HeatDistortionHorizonMask > 0.5h) - { - angleMask *= pow(saturate(1.0h - verticalDot), _HeatDistortionHorizonExponent); - } - if (_HeatDistortionZenithMask > 0.5h) - { - angleMask *= pow(verticalDot, _HeatDistortionHorizonExponent); - } - - const half strength = _HeatDistortionIntensity * distanceMask * angleMask * HEAT_DISTORTION_UV_OFFSET_SCALE; + const half strength = _HeatDistortionIntensity * distanceMask * HEAT_DISTORTION_UV_OFFSET_SCALE; // アスペクト比補正(非正方形画面でも歪みが円形に見えるようにする) const half2 aspectCorrection = half2(_ScreenParams.y * rcp(_ScreenParams.x), 1.0h); // 色収差: R/G/Bそれぞれに独立したノイズをサンプルし、チャンネルごとに別のオフセットを作る。 @@ -128,7 +109,6 @@ Shader "Hidden/Sirius/HeatDistortion" // 合成係数: 距離フェードを「オフセット量」だけでなく「合成側」でも効かせる。 // Blendは Volume から与える手動の合成率で、0なら元画像をそのまま返す - // (angleMaskはstrength側に留める。HorizonExponentが最大10のため二重掛けすると過剰に潰れる) const half t = saturate(distanceMask * _HeatDistortionBlend); return lerp(color, half4(colorR, colorG, colorB, color.a), t); From fab174eed0fc8764ba02c02ca171cec326046510 Mon Sep 17 00:00:00 2001 From: Oikawa Date: Sun, 6 Sep 2026 17:31:51 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=E3=82=B7=E3=82=A7=E3=83=BC=E3=83=80?= =?UTF-8?q?=E3=83=BC=E3=81=AE=20half1=20=E8=A1=A8=E8=A8=98=E3=82=92=20half?= =?UTF-8?q?=20=E3=81=AB=E7=B5=B1=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 色収差のチャンネル別サンプリング3行だけが half1 を使っており、同じ関数内の half / half2 / half3 / half4 や SiriusPackages 全体の記法と揃っていなかった。 half1 は vector であり、スカラーの half とは暗黙変換がコストなしで 効き生成コードも同一のため、この変更に挙動の差はない。 Co-Authored-By: Claude Opus 5 --- .../Runtime/Shaders/HeatDistortion.shader | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader b/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader index c622433..de04109 100644 --- a/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Shaders/HeatDistortion.shader @@ -102,9 +102,9 @@ Shader "Hidden/Sirius/HeatDistortion" const half2 offsetG = noiseG * strength * aspectCorrection; const half2 offsetB = noiseB * strength * aspectCorrection; - const half1 colorR = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv + offsetR).r; - const half1 colorG = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv + offsetG).g; - const half1 colorB = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv + offsetB).b; + const half colorR = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv + offsetR).r; + const half colorG = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv + offsetG).g; + const half colorB = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv + offsetB).b; const half4 color = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv); // 合成係数: 距離フェードを「オフセット量」だけでなく「合成側」でも効かせる。 From ffe21d403fa0935e31330a9360eeb3b8876faaab Mon Sep 17 00:00:00 2001 From: Oikawa Date: Sun, 6 Sep 2026 17:59:13 +0900 Subject: [PATCH 7/7] =?UTF-8?q?HeatDistortion=20=E3=81=AE=E3=83=8E?= =?UTF-8?q?=E3=82=A4=E3=82=BA=E3=83=86=E3=82=AF=E3=82=B9=E3=83=81=E3=83=A3?= =?UTF-8?q?=E3=82=92=20null=20=E6=99=82=E3=82=82=E3=83=90=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=83=89=E6=9B=B4=E6=96=B0=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skills/ct-pkg-sirius-postprocessing/SKILL.md | 2 +- .../Features/Passes/HeatDistortionRenderPass.cs | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/SiriusPackages/Sirius.PostProcessing/.claude/skills/ct-pkg-sirius-postprocessing/SKILL.md b/SiriusPackages/Sirius.PostProcessing/.claude/skills/ct-pkg-sirius-postprocessing/SKILL.md index 944eb01..87e7d51 100644 --- a/SiriusPackages/Sirius.PostProcessing/.claude/skills/ct-pkg-sirius-postprocessing/SKILL.md +++ b/SiriusPackages/Sirius.PostProcessing/.claude/skills/ct-pkg-sirius-postprocessing/SKILL.md @@ -28,5 +28,5 @@ description: "Sirius.PostProcessingパッケージの設計詳細。Use when: (1 - **深度依存**: カメラ距離を `StartDistance`〜`FadeDistance` で減衰カーブに変換して歪み強度を決めるため、URP Renderer の `m_RequireDepthTexture` が有効である必要がある。深度は `Sirius.Core` の `ScreenSpaceUtil.hlsl`(`GetCameraDistance` / `GetCameraVector`)経由で参照しており、RenderGraph 側での `builder.UseTexture` 宣言は不要(`_CameraDepthTexture` は URP のグローバルテクスチャ)。 - **ノイズテクスチャは Texture2DArray**: `_HeatDistortionNoiseTex` は `TEXTURE2D_ARRAY` で宣言している。プリセットの `3DCells64Sheet`(64 スライス)を想定し、時間をスライス番号に対応させて隣接 2 スライスを lerp する。**Texture3D や Texture2D を割り当てると次元不一致になる**ので、インポート設定の Texture Shape を `2D Array` にすること。 -- **null 許容**: `NoiseTexture` 未設定時は `RenderPass` 側で `SetTexture` を呼ばない。`Texture2D.whiteTexture` のような次元の異なるフォールバックを渡すと次元不一致になるため、他パスの `Mask` パターン(`mask ? mask : whiteTexture`)は流用できない。 +- **null 許容**: `NoiseTexture` 未設定時も `RenderPass` 側で `SetTexture` を毎回呼び、null をそのままバインドする(マテリアルをパスで使い回すため、分岐でスキップすると以前のテクスチャが残り Volume の現在値とズレる)。null を渡した場合は次元の一致するシェーダー側デフォルトへフォールバックする。`Texture2D.whiteTexture` のような次元の異なるフォールバックを明示的に渡すと次元不一致になるため、他パスの `Mask` パターン(`mask ? mask : whiteTexture`)は流用できない。 - **既定値 0 でゼロコスト**: `IsActive()` は `Blend > 0 && Intensity > 0 && FadeDistance > 0`。`_intensity` の既定値は他 Volume の `_strength` と同じく **0.0f** にしてある。これにより Volume の Weight を 0 にした場合(=既定値へ補間される場合)も `IsActive()` が false になり、パスがスキップされる。 diff --git a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs index 42d71a2..632e82b 100644 --- a/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs +++ b/SiriusPackages/Sirius.PostProcessing/Runtime/Scripts/Features/Passes/HeatDistortionRenderPass.cs @@ -39,12 +39,11 @@ private void UpdateMaterialProperties(HeatDistortionVolume heatDistortionVolume) _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionSpeed, heatDistortionVolume.Speed); _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionChromaticSeparation, heatDistortionVolume.ChromaticSeparation); _postProcessMaterial.SetFloat(ShaderPropertyIDs.HeatDistortionNoiseScale, heatDistortionVolume.NoiseScale); - // NoiseTextureはnullableなTexture2DArrayのため、未設定時はSetTextureを呼ばずシェーダー側のデフォルト値に委ねる - // (Texture2D.whiteTextureのような次元の異なるフォールバックを渡すと次元不一致になるため) - if (heatDistortionVolume.NoiseTexture != null) - { - _postProcessMaterial.SetTexture(ShaderPropertyIDs.HeatDistortionNoiseTex, heatDistortionVolume.NoiseTexture); - } + // NoiseTextureがnullのときもバインドを更新する。マテリアルはパスで使い回すため、 + // 分岐でSetTextureを飛ばすと以前のTexture2DArrayが残り、Volumeの現在値とバインドがズレる。 + // nullを渡した場合は次元の一致するシェーダー側デフォルトへフォールバックする + // (Texture2D.whiteTextureのような次元の異なるフォールバックを明示的に渡してはならない) + _postProcessMaterial.SetTexture(ShaderPropertyIDs.HeatDistortionNoiseTex, heatDistortionVolume.NoiseTexture); } public void Cleanup()