diff --git a/Runtime/MotionBlur.cs b/Runtime/MotionBlur.cs index fb48a15..b147d5a 100644 --- a/Runtime/MotionBlur.cs +++ b/Runtime/MotionBlur.cs @@ -8,6 +8,12 @@ namespace kTools.Motion [Serializable, VolumeComponentMenu("kMotion/Motion Blur")] public sealed class MotionBlur: VolumeComponent, IPostProcessComponent { + /// + /// Enable/Disable camera-based motion blur. Object-based motion blur is applied anyway. + /// + [Tooltip("Enable/Disable camera-based motion blur. Object-based motion blur is applied anyway.")] + public BoolParameter cameraBasedMB = new BoolParameter(true); + /// /// The quality of the effect. Lower presets will result in better performance at the expense of visual quality. /// @@ -19,7 +25,13 @@ public sealed class MotionBlur: VolumeComponent, IPostProcessComponent /// [Tooltip("The strength of the motion blur filter. Acts as a multiplier for velocities.")] public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f); - + + /// + /// The minimum velocity for the motion blur filter to be applied. + /// + [Tooltip("The minimum velocity for the motion blur filter to be applied.")] + public ClampedFloatParameter threshold = new ClampedFloatParameter(0.01f, 0f, 1f); + /// /// Is the component active? /// diff --git a/Runtime/MotionBlurRenderPass.cs b/Runtime/MotionBlurRenderPass.cs index 8dfb0e2..e4cd462 100644 --- a/Runtime/MotionBlurRenderPass.cs +++ b/Runtime/MotionBlurRenderPass.cs @@ -24,7 +24,7 @@ sealed class MotionBlurRenderPass : ScriptableRenderPass internal MotionBlurRenderPass() { // Set data - renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing; + renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; } #endregion @@ -33,7 +33,8 @@ internal void Setup(MotionBlur motionBlur) { // Set data m_MotionBlur = motionBlur; - m_Material = new Material(Shader.Find(kMotionBlurShader)); + if(!m_Material) + m_Material = new Material(Shader.Find(kMotionBlurShader)); } #endregion @@ -53,7 +54,8 @@ public override void Execute(ScriptableRenderContext context, ref RenderingData { // Set Material properties from VolumeComponent m_Material.SetFloat("_Intensity", m_MotionBlur.intensity.value); - + m_Material.SetFloat("_Threshold", m_MotionBlur.threshold.value); + // TODO: Why doesnt RenderTargetHandle.CameraTarget work? var colorTextureIdentifier = new RenderTargetIdentifier("_CameraColorTexture"); diff --git a/Runtime/MotionRendererFeature.cs b/Runtime/MotionRendererFeature.cs index 942eb47..b999485 100644 --- a/Runtime/MotionRendererFeature.cs +++ b/Runtime/MotionRendererFeature.cs @@ -8,9 +8,10 @@ namespace kTools.Motion sealed class MotionRendererFeature : ScriptableRendererFeature { #region Fields + public LayerMask m_LayerMask; static MotionRendererFeature s_Instance; - readonly MotionVectorRenderPass m_MotionVectorRenderPass; - readonly MotionBlurRenderPass m_MotionBlurRenderPass; + static MotionVectorRenderPass m_MotionVectorRenderPass; + static MotionBlurRenderPass m_MotionBlurRenderPass; Dictionary m_MotionDatas; uint m_FrameCount; @@ -23,9 +24,6 @@ internal MotionRendererFeature() { // Set data s_Instance = this; - m_MotionVectorRenderPass = new MotionVectorRenderPass(); - m_MotionBlurRenderPass = new MotionBlurRenderPass(); - m_MotionDatas = new Dictionary(); } #endregion @@ -33,12 +31,19 @@ internal MotionRendererFeature() public override void Create() { name = "Motion"; + m_MotionVectorRenderPass = new MotionVectorRenderPass(); + m_MotionBlurRenderPass = new MotionBlurRenderPass(); + m_MotionDatas = new Dictionary(); } #endregion #region RenderPass public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { + // Get motion blur settings + var stack = VolumeManager.instance.stack; + var motionBlur = stack.GetComponent(); + // Get MotionData var camera = renderingData.cameraData.camera; MotionData motionData; @@ -53,12 +58,10 @@ public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingD UpdateMotionData(camera, motionData); // Motion vector pass - m_MotionVectorRenderPass.Setup(motionData); + m_MotionVectorRenderPass.Setup(motionData, motionBlur, m_LayerMask.value); renderer.EnqueuePass(m_MotionVectorRenderPass); // Motion blur pass - var stack = VolumeManager.instance.stack; - var motionBlur = stack.GetComponent(); if (motionBlur.IsActive() && !renderingData.cameraData.isSceneViewCamera) { m_MotionBlurRenderPass.Setup(motionBlur); diff --git a/Runtime/MotionVectorRenderPass.cs b/Runtime/MotionVectorRenderPass.cs index 0456709..96108be 100644 --- a/Runtime/MotionVectorRenderPass.cs +++ b/Runtime/MotionVectorRenderPass.cs @@ -24,6 +24,8 @@ sealed class MotionVectorRenderPass : ScriptableRenderPass Material m_CameraMaterial; Material m_ObjectMaterial; MotionData m_MotionData; + MotionBlur m_MotionBlur; + int m_layerMask; #endregion #region Constructors @@ -35,24 +37,30 @@ internal MotionVectorRenderPass() #endregion #region State - internal void Setup(MotionData motionData) + internal void Setup(MotionData motionData, MotionBlur motionBlur, int layerMask) { // Set data m_MotionData = motionData; - m_CameraMaterial = new Material(Shader.Find(kCameraShader)); - m_ObjectMaterial = new Material(Shader.Find(kObjectShader)); + m_MotionBlur = motionBlur; + m_layerMask = layerMask; + if(!m_CameraMaterial) + m_CameraMaterial = new Material(Shader.Find(kCameraShader)); + if(!m_ObjectMaterial) + m_ObjectMaterial = new Material(Shader.Find(kObjectShader)); } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { // Configure Render Target m_MotionVectorHandle.Init(kMotionVectorTexture); + cameraTextureDescriptor.colorFormat = RenderTextureFormat.RG16; + // Debug.Log(cameraTextureDescriptor.colorFormat); cmd.GetTemporaryRT(m_MotionVectorHandle.id, cameraTextureDescriptor, FilterMode.Point); ConfigureTarget(m_MotionVectorHandle.Identifier(), m_MotionVectorHandle.Identifier()); cmd.SetRenderTarget(m_MotionVectorHandle.Identifier(), m_MotionVectorHandle.Identifier()); - // TODO: Why do I have to clear here? - cmd.ClearRenderTarget(true, true, Color.black, 1.0f); + // Clear with 0.5 because we packed vectors from clip into NDC space + cmd.ClearRenderTarget(true, true, Color.gray, 1.0f); } #endregion @@ -80,7 +88,8 @@ public override void Execute(ScriptableRenderContext context, ref RenderingData camera.depthTextureMode |= DepthTextureMode.MotionVectors | DepthTextureMode.Depth; // Drawing - DrawCameraMotionVectors(context, cmd, camera); + if(m_MotionBlur.cameraBasedMB.value) + DrawCameraMotionVectors(context, cmd, camera); DrawObjectMotionVectors(context, ref renderingData, cmd, camera); } ExecuteCommand(context, cmd); @@ -128,7 +137,7 @@ void DrawObjectMotionVectors(ScriptableRenderContext context, ref RenderingData var cullingResults = context.Cull(ref cullingParameters); var drawingSettings = GetDrawingSettings(ref renderingData); - var filteringSettings = new FilteringSettings(RenderQueueRange.opaque, camera.cullingMask); + var filteringSettings = new FilteringSettings(RenderQueueRange.opaque, camera.cullingMask & m_layerMask); var renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); // Draw Renderers diff --git a/Shaders/CameraMotionVectors.shader b/Shaders/CameraMotionVectors.shader index 5e27f4a..4944d0a 100644 --- a/Shaders/CameraMotionVectors.shader +++ b/Shaders/CameraMotionVectors.shader @@ -69,8 +69,7 @@ // Convert velocity from Clip space (-1..1) to NDC 0..1 space // Note it doesn't mean we don't have negative value, we store negative or positive offset in NDC space. - // Note: ((positionVP * 0.5 + 0.5) - (previousPositionVP * 0.5 + 0.5)) = (velocity * 0.5) - return half4(velocity.xy * 0.5, 0, 0); + return half4(velocity.xy * 0.5 + 0.5, 0, 0); } ENDHLSL diff --git a/Shaders/MotionBlur.shader b/Shaders/MotionBlur.shader index b8ee1b2..eec76c4 100644 --- a/Shaders/MotionBlur.shader +++ b/Shaders/MotionBlur.shader @@ -20,6 +20,7 @@ TEXTURE2D(_MotionVectorTexture); SAMPLER(sampler_MotionVectorTexture); float _Intensity; + float _Threshold; float4 _MainTex_TexelSize; // ------------------------------------- @@ -52,11 +53,11 @@ // ------------------------------------- // Fragment - float3 GatherSample(float sampleNumber, float2 velocity, float invSampleCount, float2 centerUV, float randomVal, float velocitySign) + float4 GatherSample(float sampleNumber, float2 velocity, float invSampleCount, float2 centerUV, float randomVal, float velocitySign) { float offsetLength = (sampleNumber + 0.5) + (velocitySign * (randomVal - 0.5)); float2 sampleUV = centerUV + (offsetLength * invSampleCount) * velocity * velocitySign; - return SAMPLE_TEXTURE2D_X(_MainTex, sampler_PointClamp, sampleUV).xyz; + return SAMPLE_TEXTURE2D_X(_MainTex, sampler_PointClamp, sampleUV); } half4 DoMotionBlur(VaryingsMB input, int iterations) @@ -64,11 +65,19 @@ UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); float2 uv = UnityStereoTransformScreenSpaceTex(input.uv.xy); - float2 velocity = SAMPLE_TEXTURE2D(_MotionVectorTexture, sampler_MotionVectorTexture, uv).rg * _Intensity; + float2 velocity = (SAMPLE_TEXTURE2D(_MotionVectorTexture, sampler_MotionVectorTexture, uv).rg * 2 - 1); + // return half4(abs(velocity), 0, 1); + + if(length(velocity) < _Threshold) + { + return SAMPLE_TEXTURE2D_X(_MainTex, sampler_PointClamp, uv); + } + velocity *= _Intensity; + float randomVal = InterleavedGradientNoise(uv * _MainTex_TexelSize.zw, 0); float invSampleCount = rcp(iterations * 2.0); - half3 color = 0.0; + half4 color = 0.0; UNITY_UNROLL for (int i = 0; i < iterations; i++) @@ -77,7 +86,7 @@ color += GatherSample(i, velocity, invSampleCount, uv, randomVal, 1.0); } - return half4(color * invSampleCount, 1.0); + return color * invSampleCount; } ENDHLSL diff --git a/Shaders/ObjectMotionVectors.shader b/Shaders/ObjectMotionVectors.shader index 6b9220c..ac1b175 100644 --- a/Shaders/ObjectMotionVectors.shader +++ b/Shaders/ObjectMotionVectors.shader @@ -73,7 +73,7 @@ #else output.positionCS.z += unity_MotionVectorsParams.z * output.positionCS.w; #endif - + output.positionVP = mul(UNITY_MATRIX_VP, mul(UNITY_MATRIX_M, input.position)); output.previousPositionVP = mul(_PrevViewProjMatrix, mul(unity_MatrixPreviousM, unity_MotionVectorsParams.x == 1 ? float4(input.positionOld, 1) : input.position)); return output; @@ -105,8 +105,7 @@ // Convert from Clip space (-1..1) to NDC 0..1 space. // Note it doesn't mean we don't have negative value, we store negative or positive offset in NDC space. - // Note: ((positionCS * 0.5 + 0.5) - (previousPositionCS * 0.5 + 0.5)) = (velocity * 0.5) - return float4(velocity.xy * 0.5, 0, 0); + return float4(velocity.xy * 0.5 + 0.5, 0, 0); } ENDHLSL }