Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

New Feature: NTSC Volume #22

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
58312ca
Worked on AnalogSignalVolume
PixelDough Aug 24, 2021
d573532
Added QAM calculation and sharpening to the analog signal volume.
PixelDough Aug 24, 2021
68812f6
Worked on NTSC (formerly Analog Signal) Volume effects
PixelDough Aug 25, 2021
5731631
Improved Gaussian calculation of NTSC filter
PixelDough Aug 25, 2021
3bc4f64
Added flickering to the NTSC effect
PixelDough Aug 25, 2021
451dbc4
Worked on flickering effect
PixelDough Aug 28, 2021
9959545
Added ability for users to toggle whether the flicker effect uses Tim…
PixelDough Aug 28, 2021
2451d6b
Merge branch 'master' of https://github.com/pastasfuture/com.hauntedp…
PixelDough Oct 19, 2021
32411e1
Merge branch 'pastasfuture-master'
PixelDough Oct 19, 2021
0667546
Merge branch 'pastasfuture:master' into master
PixelDough Oct 19, 2021
65c46d9
Worked on tooltips and updated changelog
PixelDough Oct 20, 2021
d92a788
Fixed typos in changelog
PixelDough Oct 20, 2021
a51bbda
Made adjustments based on feedback from pull request
PixelDough Oct 20, 2021
3d9e0e0
Removed files generated by Rider
PixelDough Oct 20, 2021
34344f4
Fixed unnecessary spacing change on a line
PixelDough Oct 20, 2021
ca42322
Adjusted NTSC filter to not be visible when PSXQuality is disabled an…
PixelDough Sep 30, 2023
75baf68
Merge branch 'master' into master
PixelDough Sep 30, 2023
301741b
Made the NTSC filter compatible with the CRT noise from CompositeSign…
PixelDough Sep 30, 2023
cbb7abf
Merge branch 'master' into master
PixelDough Sep 30, 2023
437e3cb
Removed warning about issues with CRT volume
PixelDough Sep 30, 2023
cea9ac0
Merge branch 'master' of https://github.com/PixelDough/com.hauntedpsx…
PixelDough Sep 30, 2023
7770128
Removed flickering parameters
PixelDough Sep 30, 2023
e287ec3
Adjusted CRT noise time so that it's stepped to every 1/60th of a sec…
PixelDough Sep 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
---------------------------------------------------------------------------------------------------------------------------
New Volume Feature: **NTSC Volume**
---------------------------------------------------------------------------------------------------------------------------
Produces a filtered image that resembles the output of an old television using Quadrature Amplatude Modulation. Use this as an alternative to the Cathode Ray Tube volume to achieve a different but distinct retro look.

**Enabled**: Controls whether the NTSC effect is active, which creates color bleeding and a natural blurriness.
IMPORTANT: This effect is not compatible with the Cathode Ray Tube Volume, and using them together can yield strange results.
**Horizontal Carrier Frequency**: The carrier wave is driven by a very fast oscillator at a fixed frequency. Since the beam is travelling, the phase of the carrier is linear both in time but also in horizontal distance over a scanline. This value determines the frequency of the wave of the horizontal carrier. Ideally, this should be set to a value which makes the scanlines as hidden as possible. Doing it this way will create a "rainbowing" effect along edges, directly related to the scanline frequency produced by this value.
**Kernel Width Ratio**: Controls the scale of the horizontal blur. To achieve the intended effect, this should be used to blur out the vertical lines produced by the Horizontal Carrier Frequency parameter.
**Sharpen**: How much to apply sharpening after blurring.
**Line Phase Shift**: Offsets the wave produced by the Horizontal Carrier Frequency. In most cases this value is unnoticable, and is best left at the default of 3.14.
**Flicker Percent**: Represents how fast the flicker effect animates relative to the current FPS.
**Flicker Scale X**: How much to scale the flicker effect horizontally (default 0.1).
**Flicker Scale Y**: How much to scale the flicker effect vertically (default 4).
**Use Time Scale**: Setting this to true will cause the flicker effect to be effected by Time.timeScale.

---------------------------------------------------------------------------------------------------------------------------
Bugfix: **Legacy Canvas UI no longer drawing**
---------------------------------------------------------------------------------------------------------------------------
Expand Down
64 changes: 64 additions & 0 deletions Editor/Volume/NTSCVolumeEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Collections;
using System.Collections.Generic;
using HauntedPSX.RenderPipelines.PSX.Runtime;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;

namespace HauntedPSX.RenderPipelines.PSX.Editor
{
[CanEditMultipleObjects]
[VolumeComponentEditor(typeof(NTSCVolume))]
public class NTSCVolumeEditor : VolumeComponentEditor
{

SerializedDataParameter m_isEnabled;
SerializedDataParameter m_KernelWidthRatio;
SerializedDataParameter m_SharpenPercent;
SerializedDataParameter m_HorizontalCarrierFrequency;
SerializedDataParameter m_LinePhaseShift;
SerializedDataParameter m_FlickerPercent;
SerializedDataParameter m_FlickerScaleX;
SerializedDataParameter m_FlickerScaleY;
SerializedDataParameter m_FlickerUseTimeScale;

public override void OnEnable()
{
var o = new PropertyFetcher<NTSCVolume>(serializedObject);
m_isEnabled = Unpack(o.Find(x => x.isEnabled));
m_HorizontalCarrierFrequency = Unpack(o.Find(x => x.horizontalCarrierFrequency));
m_KernelWidthRatio = Unpack(o.Find(x => x.kernelWidthRatio));
m_SharpenPercent = Unpack(o.Find(x => x.sharpenPercent));
m_LinePhaseShift = Unpack(o.Find(x => x.linePhaseShift));
m_FlickerPercent = Unpack(o.Find(x => x.flickerPercent));
m_FlickerScaleX = Unpack(o.Find(x => x.flickerScaleX));
m_FlickerScaleY = Unpack(o.Find(x => x.flickerScaleY));
m_FlickerUseTimeScale = Unpack(o.Find(x => x.flickerUseTimeScale));
}

public override void OnInspectorGUI()
{
PropertyField(m_isEnabled, EditorGUIUtility.TrTextContent("Enabled", "Controls whether the NTSC effect is active, which creates color bleeding and a natural blurriness." +
"\n\nIMPORTANT: This effect is not compatible with the Cathode Ray Tube Volume, and using them together can yield strange results."));
PropertyField(m_HorizontalCarrierFrequency, EditorGUIUtility.TrTextContent("Horizontal Carrier Frequency",
"The carrier wave is driven by a very fast oscillator at a fixed frequency. Since the beam is travelling, " +
"the phase of the carrier is linear both in time but also in horizontal distance over a scanline. This value " +
"determines the frequency of the wave of the horizontal carrier. " +
"\n\nIdeally, this should be set to a value which " +
"makes the scanlines as hidden as possible. Doing it this way will create a \"rainbowing\" effect along edges, " +
"directly related to the scanline frequency produced by this value."));
PropertyField(m_KernelWidthRatio,
EditorGUIUtility.TrTextContent("Kernel Width Ratio",
"Controls the scale of the horizontal blur. " +
"\n\nTo achieve the intended effect, this should be used to blur out the vertical lines produced by the Horizontal Carrier Frequency parameter."));
PropertyField(m_SharpenPercent, EditorGUIUtility.TrTextContent("Sharpen", "How much to apply sharpening after blurring."));
PropertyField(m_LinePhaseShift, EditorGUIUtility.TrTextContent("Line Phase Shift",
"Offsets the wave produced by the Horizontal Carrier Frequency. In most cases this value is unnoticable, and is best left at the default of 3.14."));
PropertyField(m_FlickerPercent, EditorGUIUtility.TrTextContent("Flicker Percent", "Represents how fast the flicker effect animates relative to the current FPS."));
PropertyField(m_FlickerScaleX, EditorGUIUtility.TrTextContent("Flicker Scale X", "How much to scale the flicker effect horizontally (default 0.1)."));
PropertyField(m_FlickerScaleY, EditorGUIUtility.TrTextContent("Flicker Scale Y", "How much to scale the flicker effect vertically (default 4)."));
PropertyField(m_FlickerUseTimeScale, EditorGUIUtility.TrTextContent("Use Time Scale", "Setting this to true will cause the flicker effect to be effected by Time.timeScale."));
}

}
}
11 changes: 11 additions & 0 deletions Editor/Volume/NTSCVolumeEditor.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Runtime/Material/PSXLit/PSXLitPass.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Varyings LitPassVertex(Attributes v)
o.normalWS = EvaluateNormalDoubleSidedPerVertex(o.normalWS, o.positionWS, _WorldSpaceCameraPos);
o.lighting = EvaluateLightingPerVertex(objectPositionWS, positionWS, o.normalWS, v.color, o.lightmapUV, o.uvw.z);
o.fog = EvaluateFogPerVertex(objectPositionWS, objectPositionVS, positionWS, positionVS, o.uvw.z, _FogWeight, precisionColor, precisionColorInverse);

return o;
}

Expand Down
112 changes: 108 additions & 4 deletions Runtime/PostProcessing/Shaders/CRT.shader
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,22 @@ Shader "Hidden/HauntedPS1/CRT"
float2 _CRTGrateMaskIntensityMinMax;
float2 _CRTBarrelDistortion;
float _CRTVignetteSquared;
int _NTSCIsEnabled;
float _NTSCHorizontalCarrierFrequency;
float _NTSCKernelWidthRatio;
float _NTSCSharpenPercent;
float _NTSCLinePhaseShift;
float _NTSCFlickerPercent;
float _NTSCFlickerScaleX;
float _NTSCFlickerScaleY;
int _NTSCFlickerUseTimeScale;
TEXTURE2D(_FrameBufferTexture);
TEXTURE2D(_WhiteNoiseTexture);
TEXTURE2D(_BlueNoiseTexture);
TEXTURE2D(_CRTGrateMaskTexture);

static const float E = 2.71828;

// Emulated input resolution.
#if 1
// Fix resolution to set amount.
Expand Down Expand Up @@ -139,6 +150,91 @@ Shader "Hidden/HauntedPS1/CRT"
return color;
}

float3 QuadratureAmplitudeModulation(float3 colorYIQ, float2 positionFramebufferNDC)
{
float Y = colorYIQ.r;
float I = colorYIQ.g;
float Q = colorYIQ.b;

float lineNumber = 1;
float carrier_phase =
_NTSCHorizontalCarrierFrequency * positionFramebufferNDC.x +
_NTSCLinePhaseShift * lineNumber;
float s = sin(carrier_phase);
float c = cos(carrier_phase);

float modulated = I * s + Q * c;
float3 premultiplied = float3(Y, 2 * s * modulated, 2 * c * modulated);

return premultiplied;
}

float Gaussian(int positionX, int halfRange)
{
return exp2(-.5 * ((float)positionX / halfRange * (float)positionX / halfRange));
}

float3 ComputeGaussianInYIQ(float2 positionFramebufferNDC)
{
float3 colorTotal = 0;
float weightTotal = 0.0;

float2 positionFramebufferCenterPixels = positionFramebufferNDC * _ScreenSizeRasterizationRTScaled.xy;
int halfRange = 3;
for (int x = -halfRange; x <= halfRange; ++x)
{
// Convert from framebuffer normalized position to pixel position
float2 positionFramebufferCurrentPixels = positionFramebufferCenterPixels + float2(x * (_NTSCKernelWidthRatio * _NTSCHorizontalCarrierFrequency), 0);
float2 positionFramebufferCurrentNDC = positionFramebufferCurrentPixels * _ScreenSizeRasterizationRTScaled.zw;

positionFramebufferCurrentNDC = ClampRasterizationRTUV(positionFramebufferCurrentNDC);
float3 colorCurrent = FCCYIQFromSRGB(FetchFrameBuffer(positionFramebufferCurrentNDC).rgb);

// Use the original offset value in order to keep it the same distance across resolutions
colorCurrent = QuadratureAmplitudeModulation(colorCurrent, positionFramebufferCurrentPixels);

// Fetch the Gauss weight at the current x offset position
float weightCurrent = Gaussian(x, halfRange);

colorTotal += colorCurrent * weightCurrent;
weightTotal += weightCurrent;
}

// After loop.
colorTotal /= weightTotal;

return colorTotal;
}

float4 ComputeAnalogSignal(float2 positionFramebufferNDC)
{
float4 time = _TimeUnscaled;
if (_NTSCFlickerUseTimeScale == 1) time = _Time;
float2 offsetFlicker = (_NTSCFlickerScaleX * float2(sign(sin(time.y * (60 * _NTSCFlickerPercent)) * sign(sin(positionFramebufferNDC.y * _ScreenSizeRasterizationRTScaled.y * _NTSCFlickerScaleY))), 0))
* _ScreenSizeRasterizationRTScaled.zw;
positionFramebufferNDC += offsetFlicker;

positionFramebufferNDC = ClampRasterizationRTUV(positionFramebufferNDC);
float4 color = FetchFrameBuffer(positionFramebufferNDC);

// Convert color space to FCCYIQ
color.rgb = FCCYIQFromSRGB(color.rgb);

// color = QuadratureAmplitudeModulation(color, positionFB, positionSS);

float oldY = color.r;

// Compute the Gaussian effect
color.rgb = ComputeGaussianInYIQ(positionFramebufferNDC);

color.r = oldY + (_NTSCSharpenPercent * (oldY - color.r));

// Convert back to SRGB
color.rgb = SRGBFromFCCYIQ(color.rgb);

return color;
}

// Nearest emulated sample given floating point position and texel offset.
// Also zero's off screen.
float4 Fetch(float2 pos, float2 off, TEXTURE2D(noiseTextureSampler), float4 noiseTextureSize)
Expand All @@ -148,6 +244,12 @@ Shader "Hidden/HauntedPS1/CRT"
pos = (floor(pos * res + off) + 0.5) / res;
if (!ComputeRasterizationRTUVIsInBounds(pos)) { return float4(0.0, 0.0, 0.0, 0.0f); }
float4 value = CompositeSignalAndNoise(noiseTextureSampler, posNoiseSignal, posNoiseCRT, off, FetchFrameBuffer(pos));

if (_NTSCIsEnabled && _IsPSXQualityEnabled)
{
value = float4(ComputeAnalogSignal(pos).rgb, 1);
}

value.rgb = SRGBToLinear(value.rgb);
return value;
}
Expand Down Expand Up @@ -400,13 +502,13 @@ Shader "Hidden/HauntedPS1/CRT"
float4 EvaluateCRT(float2 framebufferUVAbsolute, float2 positionScreenSS)
{
float4 crt = 0.0;

// Carefully handle potentially scaled RT here:
// Need the normalized aka viewport bounds UV here for converting the warp.
float2 framebufferUVNormalized = ComputeRasterizationRTUVNormalizedFromAbsolute(framebufferUVAbsolute);
float2 crtUVNormalized = Warp(framebufferUVNormalized);
float2 crtUVAbsolute = ComputeRasterizationRTUVAbsoluteFromNormalized(crtUVNormalized);

// Note: if we use the pure NDC coordinates, our vignette will be an ellipse, since we do not take into account physical distance differences from the aspect ratio.
// Apply aspect ratio to get circular, physically based vignette:
float2 crtNDCNormalized = crtUVNormalized * 2.0 - 1.0;
Expand All @@ -424,7 +526,6 @@ Shader "Hidden/HauntedPS1/CRT"
float vignette = EvaluatePBRVignette(distanceFromCenterSquaredNDC, _CRTVignetteSquared);

crt = float4(CRTMask(positionScreenSS * _CRTGrateMaskScale.y), 1.0f) * Tri(crtUVAbsolute);

#if 1
// Energy conserving normalized bloom.
crt = lerp(crt, Bloom(crtUVAbsolute), _CRTBloom);
Expand Down Expand Up @@ -491,8 +592,11 @@ Shader "Hidden/HauntedPS1/CRT"

if (!_IsPSXQualityEnabled || !_CRTIsEnabled)
{
float4 sampleTex = SAMPLE_TEXTURE2D_LOD(_FrameBufferTexture, s_point_clamp_sampler, positionFramebufferNDC.xy, 0);
if (_NTSCIsEnabled == 1) sampleTex.rgb = ComputeAnalogSignal(positionFramebufferNDC);

return ComputeRasterizationRTUVIsInBounds(positionFramebufferNDC.xy)
? SAMPLE_TEXTURE2D_LOD(_FrameBufferTexture, s_point_clamp_sampler, positionFramebufferNDC.xy, 0)
? sampleTex
: float4(0.0, 0.0, 0.0, 1.0);
}

Expand Down
50 changes: 50 additions & 0 deletions Runtime/RenderPipeline/PSXRenderPipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ protected override void Render(ScriptableRenderContext context, Camera[] cameras
PushGlobalRasterizationParameters(camera, cmd, rasterizationRT, rasterizationWidth, rasterizationHeight, hdrIsSupported);
PushQualityOverrideParameters(camera, cmd, isPSXQualityEnabled);
PushPrecisionParameters(camera, cmd, m_Asset);
PushAnalogSignalParameters(camera, cmd, m_Asset);
PushFogParameters(camera, cmd);
PushLightingParameters(camera, cmd);
PushTonemapperParameters(camera, cmd);
Expand Down Expand Up @@ -879,6 +880,30 @@ static void PushFogParameters(Camera camera, CommandBuffer cmd)
}
}
}

static void PushAnalogSignalParameters(Camera camera, CommandBuffer cmd, PSXRenderPipelineAsset asset)
{
using (new ProfilingScope(cmd, PSXProfilingSamplers.s_PushPrecisionParameters))
{
var volumeSettings = VolumeManager.instance.stack.GetComponent<NTSCVolume>();
if (!volumeSettings) volumeSettings = NTSCVolume.@default;

cmd.SetGlobalInt(PSXShaderIDs._NTSCIsEnabled,
volumeSettings.isEnabled.value ? 1 : 0);
cmd.SetGlobalFloat(PSXShaderIDs._NTSCHorizontalCarrierFrequency,
volumeSettings.horizontalCarrierFrequency.value);
cmd.SetGlobalFloat(PSXShaderIDs._NTSCKernelWidthRatio, volumeSettings.kernelWidthRatio.value);
cmd.SetGlobalFloat(PSXShaderIDs._NTSCSharpenPercent,
volumeSettings.sharpenPercent.value);
cmd.SetGlobalFloat(PSXShaderIDs._NTSCLinePhaseShift,
volumeSettings.linePhaseShift.value);
cmd.SetGlobalFloat(PSXShaderIDs._NTSCFlickerPercent, volumeSettings.flickerPercent.value);
cmd.SetGlobalFloat(PSXShaderIDs._NTSCFlickerScaleX, volumeSettings.flickerScaleX.value);
cmd.SetGlobalFloat(PSXShaderIDs._NTSCFlickerScaleY, volumeSettings.flickerScaleY.value);
cmd.SetGlobalInt(PSXShaderIDs._NTSCFlickerUseTimeScale,
volumeSettings.flickerUseTimeScale.value ? 1 : 0);
}
}

void PushGlobalRasterizationParameters(Camera camera, CommandBuffer cmd, RTHandle rasterizationRT, int rasterizationWidth, int rasterizationHeight, bool hdrIsSupported)
{
Expand Down Expand Up @@ -906,6 +931,9 @@ void PushGlobalRasterizationParameters(Camera camera, CommandBuffer cmd, RTHandl

float time = GetAnimatedMaterialsTime(camera);
cmd.SetGlobalVector(PSXShaderIDs._Time, new Vector4(time / 20.0f, time, time * 2.0f, time * 3.0f));

float timeUnscaled = GetAnimatedMaterialsTimeUnscaled(camera);
cmd.SetGlobalVector(PSXShaderIDs._TimeUnscaled, new Vector4(timeUnscaled / 20.0f, timeUnscaled, timeUnscaled * 2.0f, timeUnscaled * 3.0f));

Texture2D alphaClippingDitherTex = GetAlphaClippingDitherTexFromAssetAndFrame(asset, (uint)Time.frameCount);
cmd.SetGlobalTexture(PSXShaderIDs._AlphaClippingDitherTexture, alphaClippingDitherTex);
Expand Down Expand Up @@ -1881,5 +1909,27 @@ static float GetAnimatedMaterialsTime(Camera camera)

return time;
}

static float GetAnimatedMaterialsTimeUnscaled(Camera camera)
{
float time = 0.0f;
bool animateMaterials = CoreUtils.AreAnimatedMaterialsEnabled(camera);
if (animateMaterials)
{
#if UNITY_EDITOR
//time = Application.isPlaying ? Time.timeSinceLevelLoad : Time.realtimeSinceStartup;
time = Application.isPlaying ? Time.unscaledTime : Time.realtimeSinceStartup;
#else
//time = Time.timeSinceLevelLoad;
time = Time.unscaledTime;
#endif
}
else
{
time = 0;
}

return time;
}
}
}
Loading