像素化
This commit is contained in:
31624
Assets/FR2_Cache.asset
31624
Assets/FR2_Cache.asset
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -13533,16 +13533,16 @@ Canvas:
|
||||
m_GameObject: {fileID: 4337614254416964835}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 1
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 87189952}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_PixelPerfect: 1
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 1
|
||||
m_AdditionalShaderChannelsFlag: 25
|
||||
m_AdditionalShaderChannelsFlag: 1
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
@@ -15834,7 +15834,7 @@ MonoBehaviour:
|
||||
m_TargetGraphic: {fileID: 4337614255116277774}
|
||||
m_HandleRect: {fileID: 4337614255116277775}
|
||||
m_Direction: 2
|
||||
m_Value: 0
|
||||
m_Value: 1
|
||||
m_Size: 1
|
||||
m_NumberOfSteps: 0
|
||||
m_OnValueChanged:
|
||||
|
||||
@@ -10,6 +10,19 @@ namespace Ichni
|
||||
/// </summary>
|
||||
public static class CustomCurvePresets
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 瞬间完成
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static AnimationCurve Instant()
|
||||
{
|
||||
Keyframe[] keys = new Keyframe[2];
|
||||
keys[0] = new Keyframe(0, 1, 0, 0);
|
||||
keys[1] = new Keyframe(1, 1, 0, 0);
|
||||
return new AnimationCurve(keys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 抛物线曲线,在中间达到最大值,两端为起始值
|
||||
/// </summary>
|
||||
|
||||
@@ -138,6 +138,7 @@ namespace Ichni.RhythmGame
|
||||
{ "Vignette", new VignetteEffect(1, 1, 0.4f, Color.black, CustomCurvePresets.Parabolic(1, 0, 1)) },
|
||||
{ "SetInteger", new SetIntegerEffect("New Variable", 0, false, 0, 1) },
|
||||
{ "EnableControl", new EnableControlEffect(null, "New Variable", 0, false, "") },
|
||||
{"Pixelate", new PixelateEffect(1, 320, 180, CustomCurvePresets.Instant())},
|
||||
{ "LowPassFilter", new LowPassFilterEffect(1, 10, CustomCurvePresets.Parabolic(1, 0, 1)) },
|
||||
{ "HighPassFilter", new HighPassFilterEffect(1, 22000, CustomCurvePresets.Parabolic(1, 0, 1)) },
|
||||
{ "DTM_RippleEffect", new DTMRippleEffect(0.65f, Color.white, 0) }
|
||||
|
||||
@@ -78,7 +78,10 @@ namespace Ichni.RhythmGame
|
||||
|
||||
public override EffectBase ConvertToGameType(GameElement attachedGameElement)
|
||||
{
|
||||
return new BloomEffect(duration, peak, intensityCurve);
|
||||
return new BloomEffect(duration, peak, intensityCurve)
|
||||
{
|
||||
attachedGameElement = attachedGameElement
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.Editor;
|
||||
using Ichni.RhythmGame.Beatmap;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public class PixelateEffect : EffectBase
|
||||
{
|
||||
public float duration;
|
||||
public float bottomX;
|
||||
public float bottomY;
|
||||
public AnimationCurve intensityCurve;
|
||||
|
||||
public PixelateEffect(float duration, float bottomX, float bottomY, AnimationCurve intensityCurve)
|
||||
{
|
||||
this.effectTime = duration;
|
||||
this.duration = duration;
|
||||
this.bottomX = bottomX;
|
||||
this.bottomY = bottomY;
|
||||
this.intensityCurve = intensityCurve;
|
||||
}
|
||||
|
||||
public override void Recover()
|
||||
{
|
||||
EditorManager.instance.postProcessingManager.SetPixelateStrength(Screen.width, Screen.height);
|
||||
EditorManager.instance.postProcessingManager.SetFeatureActive(false);
|
||||
}
|
||||
|
||||
public override void Disrupt()
|
||||
{
|
||||
EditorManager.instance.postProcessingManager.SetPixelateStrength(Screen.width, Screen.height);
|
||||
EditorManager.instance.postProcessingManager.SetFeatureActive(false);
|
||||
}
|
||||
|
||||
public override void PreExecute()
|
||||
{
|
||||
EditorManager.instance.postProcessingManager.SetFeatureActive(true);
|
||||
EditorManager.instance.postProcessingManager.SetPixelateStrength(Screen.width, Screen.height);
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
float x = Mathf.Lerp(Screen.width, bottomX, intensityCurve.Evaluate(effectProgressPercent));
|
||||
float y = Mathf.Lerp(Screen.height, bottomY, intensityCurve.Evaluate(effectProgressPercent));
|
||||
Debug.Log(x + ", " + y);
|
||||
|
||||
EditorManager.instance.postProcessingManager.SetPixelateStrength(x,y);
|
||||
}
|
||||
|
||||
public override void Adjust()
|
||||
{
|
||||
EditorManager.instance.postProcessingManager.SetPixelateStrength(Screen.width, Screen.height);
|
||||
EditorManager.instance.postProcessingManager.SetFeatureActive(false);
|
||||
}
|
||||
|
||||
public override EffectBase_BM ConvertToBM()
|
||||
{
|
||||
return new PixelateEffect_BM(duration, bottomX, bottomY, intensityCurve);
|
||||
}
|
||||
|
||||
public override void SetUpInspector()
|
||||
{
|
||||
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
|
||||
var container = inspector.GenerateContainer("Pixelate Effect");
|
||||
var effectSettings = container.GenerateSubcontainer(3);
|
||||
var effectTimeField = inspector.GenerateInputField(this, effectSettings, "Effect Time", nameof(duration));
|
||||
var bottomXField = inspector.GenerateInputField(this, effectSettings, "Bottom X", nameof(bottomX));
|
||||
var bottomYField = inspector.GenerateInputField(this, effectSettings, "Bottom Y", nameof(bottomY));
|
||||
var intensityCurveButton = inspector.GenerateButton(this, effectSettings, "Intensity Curve", () =>
|
||||
{
|
||||
var intensityCurveWindow =
|
||||
inspector.GenerateCompositeParameterWindow(this, "Intensity Curve", nameof(intensityCurve)).SetAsCustomCurve();
|
||||
});
|
||||
|
||||
var clearButton = inspector.GenerateButton(this, effectSettings, "Clear Pixelate", () =>
|
||||
{
|
||||
EditorManager.instance.postProcessingManager.SetFeatureActive(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
namespace Beatmap
|
||||
{
|
||||
public class PixelateEffect_BM : EffectBase_BM
|
||||
{
|
||||
public float duration;
|
||||
public float bottomX;
|
||||
public float bottomY;
|
||||
public AnimationCurve intensityCurve;
|
||||
|
||||
public PixelateEffect_BM(float duration, float bottomX, float bottomY, AnimationCurve intensityCurve)
|
||||
{
|
||||
this.effectTime = duration;
|
||||
this.duration = duration;
|
||||
this.bottomX = bottomX;
|
||||
this.bottomY = bottomY;
|
||||
this.intensityCurve = intensityCurve;
|
||||
}
|
||||
|
||||
public override EffectBase ConvertToGameType(GameElement attachedGameElement)
|
||||
{
|
||||
return new PixelateEffect(duration, bottomX, bottomY, intensityCurve)
|
||||
{
|
||||
attachedGameElement = attachedGameElement
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d1fa92947bbef246a8a112b2a6b96c4
|
||||
guid: b1ecccb5fd84627489e74a02a44da11a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -1,12 +1,90 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace Ichni.Editor
|
||||
{
|
||||
public class PostProcessingManager : MonoBehaviour
|
||||
{
|
||||
public Volume globalVolume;
|
||||
public PixelateFeature pixelateFeature;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
FindAndCacheFeatureWithReflection();
|
||||
}
|
||||
|
||||
private void FindAndCacheFeatureWithReflection()
|
||||
{
|
||||
var pipelineAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
|
||||
if (pipelineAsset == null)
|
||||
{
|
||||
Debug.LogError("当前渲染管线不是 UniversalRenderPipelineAsset。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 使用反射来获取内部的 m_RendererDataList 字段
|
||||
FieldInfo rendererDataListField = typeof(UniversalRenderPipelineAsset).GetField("m_RendererDataList", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (rendererDataListField == null)
|
||||
{
|
||||
Debug.LogError("在 UniversalRenderPipelineAsset 中无法通过反射找到 'm_RendererDataList' 字段。API可能已在你的URP版本中更改。");
|
||||
return;
|
||||
}
|
||||
|
||||
var rendererDataList = rendererDataListField.GetValue(pipelineAsset) as ScriptableRendererData[];
|
||||
if (rendererDataList == null)
|
||||
{
|
||||
Debug.LogError("获取渲染器数据列表失败。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 遍历获取到的列表来查找我们的Feature
|
||||
foreach (var rendererData in rendererDataList)
|
||||
{
|
||||
if (rendererData == null) continue;
|
||||
|
||||
var feature = rendererData.rendererFeatures.OfType<PixelateFeature>().FirstOrDefault();
|
||||
if (feature != null)
|
||||
{
|
||||
pixelateFeature = feature;
|
||||
Debug.Log("成功找到并缓存 pixelateFeature (通过反射)!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pixelateFeature == null)
|
||||
{
|
||||
Debug.LogError("在所有 RendererData 中都未找到 pixelateFeature。");
|
||||
}
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void SetFeatureActive(bool enable)
|
||||
{
|
||||
if (pixelateFeature != null)
|
||||
{
|
||||
pixelateFeature.SetActive(enable);
|
||||
}
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void SetPixelateStrength(float strengthX, float strengthY)
|
||||
{
|
||||
if (pixelateFeature != null)
|
||||
{
|
||||
pixelateFeature.settings.pixelateStrengthX = strengthX;
|
||||
pixelateFeature.settings.pixelateStrengthY = strengthY;
|
||||
pixelateFeature.pixelatePass.UpdateConfig(strengthX, strengthY);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Pixelate feature is not initialized.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,10 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
m_Active: 0
|
||||
settings:
|
||||
renderPassEvent: 600
|
||||
pixelateMaterial: {fileID: 2100000, guid: f0cf39229422d1b428bf04e0bc828dc6, type: 2}
|
||||
pixelateShader: {fileID: 0}
|
||||
pixelateStrengthX: 64
|
||||
pixelateStrengthY: 64
|
||||
passEvent: 500
|
||||
--- !u!114 &-1878332245247344467
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -51,6 +53,24 @@ MonoBehaviour:
|
||||
- {fileID: 2800000, guid: 3302065f671a8450b82c9ddf07426f3a, type: 3}
|
||||
- {fileID: 2800000, guid: 56a77a3e8d64f47b6afe9e3c95cb57d5, type: 3}
|
||||
m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3}
|
||||
--- !u!114 &-545220758568441603
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
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: f41bcf9b1f0eb6446907e2c52c9f2d39, type: 3}
|
||||
m_Name: PixelateFeature
|
||||
m_EditorClassIdentifier:
|
||||
m_Active: 1
|
||||
settings:
|
||||
pixelateShader: {fileID: 4800000, guid: 272e7eef87baea8408e583d2670e66dd, type: 3}
|
||||
pixelateStrengthX: 1621.1226
|
||||
pixelateStrengthY: 911.8815
|
||||
passEvent: 500
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -70,9 +90,9 @@ MonoBehaviour:
|
||||
m_RendererFeatures:
|
||||
- {fileID: -1878332245247344467}
|
||||
- {fileID: 9211144479881546400}
|
||||
- {fileID: -5115341559200278812}
|
||||
- {fileID: 2224949836353836081}
|
||||
m_RendererFeatureMap: adc0de57c6d2eee5a0ca230a1a8fd47fe4161343f4a702b93114ef7e169ce01e
|
||||
- {fileID: -545220758568441603}
|
||||
m_RendererFeatureMap: adc0de57c6d2eee5a0ca230a1a8fd47f3114ef7e169ce01efde85bd99bfc6ef8
|
||||
m_UseNativeRenderPass: 0
|
||||
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
|
||||
xrSystemData: {fileID: 11400000, guid: 60e1133243b97e347b653163a8c01b64, type: 2}
|
||||
|
||||
@@ -850,6 +850,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"MotionAngles" : false,
|
||||
"elementName" : "New Track Percent Point",
|
||||
"tags" : [
|
||||
|
||||
@@ -1362,6 +1363,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"MotionAngles" : false,
|
||||
"elementName" : "New Track Percent Point",
|
||||
"tags" : [
|
||||
|
||||
@@ -1874,6 +1876,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"MotionAngles" : false,
|
||||
"elementName" : "New Track Percent Point",
|
||||
"tags" : [
|
||||
|
||||
@@ -69943,6 +69946,34 @@
|
||||
},
|
||||
"emissionIntensity" : 0,
|
||||
"effectTime" : 0
|
||||
},{
|
||||
"__type" : "Ichni.RhythmGame.Beatmap.PixelateEffect_BM,Assembly-CSharp",
|
||||
"duration" : 5,
|
||||
"bottomX" : 320,
|
||||
"bottomY" : 180,
|
||||
"intensityCurve" : {
|
||||
"keys" : [
|
||||
{
|
||||
"time" : 0,
|
||||
"value" : 0,
|
||||
"inTangent" : 0,
|
||||
"outTangent" : 0
|
||||
},{
|
||||
"time" : 0.5,
|
||||
"value" : 1,
|
||||
"inTangent" : 0,
|
||||
"outTangent" : 0
|
||||
},{
|
||||
"time" : 1,
|
||||
"value" : 0,
|
||||
"inTangent" : 0,
|
||||
"outTangent" : 0
|
||||
}
|
||||
],
|
||||
"preWrapMode" : 8,
|
||||
"postWrapMode" : 8
|
||||
},
|
||||
"effectTime" : 5
|
||||
}
|
||||
],"Late":[
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,21 @@
|
||||
Shader "Hidden/Custom/Pixelate"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderPipeline" = "UniversalPipeline" }
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Pixelate Pass"
|
||||
ZTest Always
|
||||
Cull Off
|
||||
ZWrite Off
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma vertex PixelatePassVert
|
||||
#pragma fragment PixelatePassFrag
|
||||
#include "PixelatePass.hlsl"
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 272e7eef87baea8408e583d2670e66dd
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,47 +1,100 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
public class PixelateFeature : ScriptableRendererFeature
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Settings
|
||||
public class PixelatePass : ScriptableRenderPass
|
||||
{
|
||||
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
|
||||
[Tooltip("用于像素化的材质")]
|
||||
public Material pixelateMaterial = null;
|
||||
private static readonly string ProfilerTag = "PixelateEffect";
|
||||
|
||||
private static readonly int PixelateStrengthXID = Shader.PropertyToID("_PixelateStrengthX");
|
||||
private static readonly int PixelateStrengthYID = Shader.PropertyToID("_PixelateStrengthY");
|
||||
private static readonly int SourceTextureID = Shader.PropertyToID("_SourceTexture");
|
||||
|
||||
public Material pixelateMaterial;
|
||||
public float pixelateX = 64f;
|
||||
public float pixelateY = 64f;
|
||||
|
||||
private RTHandle sourceRT;
|
||||
private RTHandle transientRT;
|
||||
|
||||
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
||||
{
|
||||
sourceRT = renderingData.cameraData.renderer.cameraColorTargetHandle;
|
||||
RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
|
||||
descriptor.depthBufferBits = 0;
|
||||
RenderingUtils.ReAllocateIfNeeded(ref transientRT, descriptor, name: "_PixelateTransientRT");
|
||||
}
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (pixelateMaterial == null) return;
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get(ProfilerTag);
|
||||
|
||||
pixelateMaterial.SetFloat(PixelateStrengthXID, pixelateX);
|
||||
pixelateMaterial.SetFloat(PixelateStrengthYID, pixelateY);
|
||||
|
||||
cmd.SetGlobalTexture(SourceTextureID, sourceRT);
|
||||
cmd.SetRenderTarget(transientRT, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
|
||||
cmd.DrawProcedural(Matrix4x4.identity, pixelateMaterial, 0, MeshTopology.Triangles, 3);
|
||||
|
||||
cmd.SetGlobalTexture(SourceTextureID, transientRT);
|
||||
cmd.SetRenderTarget(sourceRT, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
|
||||
cmd.DrawProcedural(Matrix4x4.identity, pixelateMaterial, 0, MeshTopology.Triangles, 3);
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
public void UpdateConfig(float x, float y)
|
||||
{
|
||||
pixelateX = x;
|
||||
pixelateY = y;
|
||||
}
|
||||
}
|
||||
|
||||
public Settings settings = new Settings();
|
||||
private PixelatePass m_PixelatePass;
|
||||
[System.Serializable]
|
||||
public class PixelateSettings
|
||||
{
|
||||
[SerializeField] private Shader pixelateShader;
|
||||
private Material pixelateMaterial;
|
||||
public Material PixelateMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pixelateMaterial == null && pixelateShader != null)
|
||||
{
|
||||
pixelateMaterial = new Material(pixelateShader)
|
||||
{
|
||||
hideFlags = HideFlags.HideAndDontSave
|
||||
};
|
||||
}
|
||||
return pixelateMaterial;
|
||||
}
|
||||
}
|
||||
[Min(2)] public float pixelateStrengthX = 64f;
|
||||
[Min(2)] public float pixelateStrengthY = 64f;
|
||||
public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
|
||||
}
|
||||
|
||||
public PixelateSettings settings = new();
|
||||
public PixelatePass pixelatePass;
|
||||
|
||||
// 当Feature被创建或Inspector中的值被改变时调用
|
||||
public override void Create()
|
||||
{
|
||||
// 检查材质是否存在
|
||||
if (settings.pixelateMaterial != null)
|
||||
pixelatePass = new PixelatePass
|
||||
{
|
||||
m_PixelatePass = new PixelatePass(settings.pixelateMaterial);
|
||||
// 将Inspector中设置的事件赋值给Pass
|
||||
m_PixelatePass.renderPassEvent = settings.renderPassEvent;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果材质为空,则不创建Pass,避免后续报错
|
||||
m_PixelatePass = null;
|
||||
}
|
||||
renderPassEvent = settings.passEvent,
|
||||
pixelateMaterial = settings.PixelateMaterial,
|
||||
pixelateX = settings.pixelateStrengthX,
|
||||
pixelateY = settings.pixelateStrengthY
|
||||
};
|
||||
}
|
||||
|
||||
// 【核心修正】这个方法现在非常干净,只负责将创建好的Pass入队
|
||||
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
||||
{
|
||||
if (m_PixelatePass == null)
|
||||
{
|
||||
// 如果Pass没有被成功创建(因为没材质),就直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
// 将我们的Pass添加到渲染队列中,URP会在正确的时间执行它
|
||||
renderer.EnqueuePass(m_PixelatePass);
|
||||
renderer.EnqueuePass(pixelatePass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
public class PixelatePass : ScriptableRenderPass
|
||||
{
|
||||
private Material m_PixelateMaterial;
|
||||
|
||||
// 构造函数,接收材质
|
||||
public PixelatePass(Material pixelateMaterial)
|
||||
{
|
||||
this.m_PixelateMaterial = pixelateMaterial;
|
||||
}
|
||||
|
||||
// 这个方法在每一帧渲染该Pass之前被调用
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
// 1. 安全检查
|
||||
if (m_PixelateMaterial == null)
|
||||
{
|
||||
Debug.LogError("Pixelate Material not assigned to the pass.");
|
||||
return;
|
||||
}
|
||||
// 如果渲染的不是游戏主相机(例如Scene视图的相机),则直接返回,避免在编辑器里也显示效果
|
||||
if (renderingData.cameraData.cameraType != CameraType.Game)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取命令缓冲区
|
||||
CommandBuffer cmd = CommandBufferPool.Get("PixelatePass");
|
||||
|
||||
// 3. 【核心修正】在Execute方法内部,安全地获取当前摄像机的渲染目标
|
||||
// URP 12+ 使用 renderingData.cameraData.renderer.cameraColorTargetHandle
|
||||
RTHandle source = renderingData.cameraData.renderer.cameraColorTargetHandle;
|
||||
|
||||
// 4. 执行Blit操作
|
||||
// 将源纹理(source)通过我们的材质处理后,再写回源纹理(source)
|
||||
Blit(cmd, source, source, m_PixelateMaterial, 0);
|
||||
|
||||
// 5. 执行并释放命令缓冲区
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef PIXELATE_PASS_INCLUDED
|
||||
#define PIXELATE_PASS_INCLUDED
|
||||
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
|
||||
TEXTURE2D(_SourceTexture);
|
||||
SAMPLER(sampler_SourceTexture);
|
||||
float4 _SourceTexture_TexelSize;
|
||||
|
||||
float _PixelateStrengthX;
|
||||
float _PixelateStrengthY;
|
||||
|
||||
struct Varyings
|
||||
{
|
||||
float4 positionCS_SS : SV_POSITION;
|
||||
float2 screenUV : VAR_SCREEN_UV;
|
||||
};
|
||||
|
||||
Varyings PixelatePassVert(uint vertexID : SV_VertexID)
|
||||
{
|
||||
Varyings output;
|
||||
output.positionCS_SS = float4(
|
||||
vertexID <= 1 ? -1.0 : 3.0,
|
||||
vertexID == 1 ? 3.0 : -1.0,
|
||||
0.0, 1.0
|
||||
);
|
||||
output.screenUV = float2(
|
||||
vertexID <= 1 ? 0.0 : 2.0,
|
||||
vertexID == 1 ? 2.0 : 0.0
|
||||
);
|
||||
if (_ProjectionParams.x < 0.0)
|
||||
{
|
||||
output.screenUV.y = 1.0 - output.screenUV.y;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
half4 PixelatePassFrag(Varyings input) : SV_Target
|
||||
{
|
||||
float2 screenUV = input.screenUV;
|
||||
float2 strength = float2(_PixelateStrengthX, _PixelateStrengthY);
|
||||
float2 uvScaled = screenUV * strength;
|
||||
float2 uvFloor = floor(uvScaled);
|
||||
float2 uvCenter = uvFloor + 0.5;
|
||||
float2 uvPixelated = uvCenter / strength;
|
||||
|
||||
return SAMPLE_TEXTURE2D(_SourceTexture, sampler_SourceTexture, uvPixelated);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8be62d4e6aca4494caf6095e90160cd9
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user