483 lines
19 KiB
GLSL
483 lines
19 KiB
GLSL
//-----------------------------------------------------------------------
|
|
// Author: Colby-O
|
|
// File: PSXMaster_URP.shader
|
|
//-----------------------------------------------------------------------
|
|
|
|
Shader "Hidden/PSXMaster_URP"
|
|
{
|
|
Properties
|
|
{
|
|
[Header(Internal)]
|
|
_VNTGScreenParams ("Screen Width/Height", Vector) = (1920, 1080, 0, 0)
|
|
|
|
[Header(Resolution)]
|
|
_EnablePixelation ("Enable Pixelation", Float) = 1
|
|
_EnableLetterbox ("Letterbox To Perserve Aspect Ratio", Float) = 1.0
|
|
_PixelResolution ("Pixelation Resolution", Vector) = (256, 256, 0, 0)
|
|
|
|
[Header(Color and Dither)]
|
|
_EnableColorPrecision ("Enable Color Precision", Float) = 1
|
|
_ColorPrecision ("Color Steps", Float) = 32
|
|
_DitherPattern ("Dither Pattern", Float) = 1.0
|
|
_DitherPixelPerfect ("Use Pixel Perfect Dither", Float) = 1.0
|
|
_DitherRelativeScale("Use Relative Dither Scale", Float) = 1.0
|
|
_DitherReferenceResolution ("Dither Reference Resolution", Vector) = (1920, 1080, 0, 0)
|
|
_DitherScale ("Dither Pattern Scale", Float) = 1.0
|
|
_DitherThreshold ("Dither Sensitivity", Float) = 1.0
|
|
|
|
[Header(Palette)]
|
|
_EnablePalette ("Enable Color Palette", Float) = 0
|
|
[Toggle] _PaletteNormalizeLuminance ("Palette Normalize Luminance", Float) = 0
|
|
[Toggle] _PreserveLighting ("Preserve Lighting Intensity", Float) = 0
|
|
_PaletteLUT ("3D Color Palette Map", 3D) = "white" {}
|
|
|
|
[Header(Fog)]
|
|
_EnableFog ("Enable Fog", Float) = 0
|
|
_IgnoreSkybox ("Ignore Skybox", Float) = 0
|
|
_FogDensity ("Fog Density", Float) = 1.0
|
|
_FogColor ("Fog Color", Color) = (0, 0, 0, 1)
|
|
_FogNoiseStrength ("Fog Noise Strength", Float) = 0.1
|
|
_FogNoiseScale ("Fog Noise Scale", Float) = 10.0
|
|
_FogNoiseStart ("Fog Noise Start", Float) = 0.7
|
|
_FogEdgeSmoothness ("Fog Edge Smoothness", Float) = 0.5
|
|
}
|
|
|
|
SubShader
|
|
{
|
|
Tags { "RenderPipeline"="UniversalPipeline" }
|
|
ZWrite Off ZTest Always Cull Off
|
|
|
|
Pass
|
|
{
|
|
HLSLPROGRAM
|
|
#pragma vertex Vert
|
|
#pragma fragment Frag
|
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
|
|
|
|
float2 _VNTGScreenParams;
|
|
|
|
float _EnablePixelation;
|
|
float _EnableLetterbox;
|
|
float2 _PixelResolution;
|
|
|
|
float _EnableColorPrecision;
|
|
float _ColorPrecision;
|
|
|
|
float _EnableDither;
|
|
float _DitherMode;
|
|
int _DitherPattern;
|
|
float _DitherPixelPerfect;
|
|
float _DitherRelativeScale;
|
|
float2 _DitherReferenceResolution;
|
|
float _DitherScale;
|
|
float _DitherThreshold;
|
|
|
|
float _EnablePalette;
|
|
float _PaletteNormalizeLuminance;
|
|
float _PreserveLighting;
|
|
TEXTURE3D(_PaletteLUT);
|
|
SAMPLER(sampler_PaletteLUT);
|
|
|
|
int _EnableFog;
|
|
float _IgnoreSkybox;
|
|
float4 _FogColor;
|
|
float _FogDensity;
|
|
float _FogEdgeSmoothness;
|
|
float _FogNoiseStrength;
|
|
float _FogNoiseScale;
|
|
float _FogNoiseStart;
|
|
|
|
/**
|
|
* Generates a random value from a 3D vector.
|
|
*
|
|
* @param p The input 3D coordinates.
|
|
* @return A random float value in the range [0.0, 1.0].
|
|
*/
|
|
inline float Hash31(float3 p)
|
|
{
|
|
p = frac(p * 0.1031);
|
|
p += dot(p, p.yzx + 33.33);
|
|
return frac((p.x + p.y) * p.z);
|
|
}
|
|
|
|
/**
|
|
* Calculates a andom gradient vector for a given 3D point.
|
|
*
|
|
* @param p The input 3D coordinates.
|
|
* @return A gradient direction.
|
|
*/
|
|
inline float3 Gradient(float3 p)
|
|
{
|
|
float h = Hash31(p) * 6.2831853;
|
|
return float3(cos(h), sin(h), cos(h * 0.5));
|
|
}
|
|
|
|
/**
|
|
* Computes the quintic smoothing for Perlin noise.
|
|
*
|
|
* @param t The distance [0, 1] within a cell.
|
|
* @return The smoothed weight.
|
|
*/
|
|
inline float3 Fade(float3 t)
|
|
{
|
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
}
|
|
|
|
/**
|
|
* Generates 3D Perlin Noise for a given coordinate.
|
|
*
|
|
* @param p The input 3D coordinates.
|
|
* @return A noise value in the range [0.0, 1.0].
|
|
*/
|
|
inline float Perlin3D(float3 p)
|
|
{
|
|
float3 pi = floor(p);
|
|
float3 pf = frac(p);
|
|
|
|
float3 f = Fade(pf);
|
|
|
|
// Calculate dot products between corner gradients and distance vectors
|
|
float n000 = dot(Gradient(pi + float3(0,0,0)), pf - float3(0,0,0));
|
|
float n100 = dot(Gradient(pi + float3(1,0,0)), pf - float3(1,0,0));
|
|
float n010 = dot(Gradient(pi + float3(0,1,0)), pf - float3(0,1,0));
|
|
float n110 = dot(Gradient(pi + float3(1,1,0)), pf - float3(1,1,0));
|
|
float n001 = dot(Gradient(pi + float3(0,0,1)), pf - float3(0,0,1));
|
|
float n101 = dot(Gradient(pi + float3(1,0,1)), pf - float3(1,0,1));
|
|
float n011 = dot(Gradient(pi + float3(0,1,1)), pf - float3(0,1,1));
|
|
float n111 = dot(Gradient(pi + float3(1,1,1)), pf - float3(1,1,1));
|
|
|
|
// Trilinear interpolation
|
|
float nx00 = lerp(n000, n100, f.x);
|
|
float nx10 = lerp(n010, n110, f.x);
|
|
float nx01 = lerp(n001, n101, f.x);
|
|
float nx11 = lerp(n011, n111, f.x);
|
|
|
|
float nxy0 = lerp(nx00, nx10, f.y);
|
|
float nxy1 = lerp(nx01, nx11, f.y);
|
|
|
|
return lerp(nxy0, nxy1, f.z) * 0.5 + 0.5;
|
|
}
|
|
|
|
/**
|
|
* Calculates the perceived brightness (Luminance) of an RGB color
|
|
*
|
|
* @param c The input RGB color.
|
|
* @return The brightness of the color.
|
|
*/
|
|
inline float GetLuminance(float3 c)
|
|
{
|
|
return dot(c, float3(0.299, 0.587, 0.114));
|
|
}
|
|
|
|
/**
|
|
* Retrieves a predefined 4x4 dither matrix.
|
|
*
|
|
* @param index The index of the desired pattern.
|
|
* @return A 4x4 matrix containing the dither pattern.
|
|
*/
|
|
inline float4x4 GetDitherPattern(int index) {
|
|
if (index == 0)
|
|
{
|
|
return float4x4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
}
|
|
else if (index == 1)
|
|
{
|
|
return float4x4(0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0);
|
|
}
|
|
else if (index == 2)
|
|
{
|
|
return float4x4(0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5) / 16.0;
|
|
}
|
|
else if (index == 3)
|
|
{
|
|
return float4x4(2.0, 0.0, 0.0, 2.0, 0.0, 4.0, 4.0, 0.0, 0.0, 4.0, 4.0, 0.0, 2.0, 0.0, 0.0, 2.0) / 4.0;
|
|
}
|
|
else if (index == 4)
|
|
{
|
|
return float4x4(0, 2, 0, 2, 3, 1, 3, 1, 0, 2, 0, 2, 3, 1, 3, 1) / 4.0;
|
|
}
|
|
else if (index == 5)
|
|
{
|
|
return float4x4(0, 4, 8, 12, 0, 4, 8, 12, 0, 4, 8, 12, 0, 4, 8, 12) / 16.0;
|
|
}
|
|
else if (index == 6)
|
|
{
|
|
return float4x4(0, 0, 0, 0, 8, 8, 8, 8, 15, 15, 15, 15, 8, 8, 8, 8) / 16.0;
|
|
}
|
|
else if (index == 7)
|
|
{
|
|
return float4x4(3, 6, 9, 12, 6, 9, 12, 3, 9, 12, 3, 6, 12, 3, 6, 9) / 16.0;
|
|
}
|
|
else if (index == 8)
|
|
{
|
|
return float4x4(13, 10, 11, 14, 6, 1, 2, 7, 5, 0, 3, 8, 12, 9, 4, 15) / 16.0;
|
|
}
|
|
else if (index == 9)
|
|
{
|
|
return float4x4(0.1, 0.7, 0.3, 0.9, 0.5, 0.2, 0.8, 0.4, 0.9, 0.3, 0.7, 0.1, 0.4, 0.8, 0.2, 0.5);
|
|
}
|
|
else if (index == 10)
|
|
{
|
|
return float4x4(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
|
|
}
|
|
|
|
return float4x4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
}
|
|
|
|
/**
|
|
* Performs a binary dither check against a 4x4 matrix based on screen coordinates.
|
|
* Compares the luminance of the scene color against a pattern threshold to decide visibility.
|
|
*
|
|
* @param uv The input coordinate.
|
|
* @param scene The input RGB color.
|
|
* @param pattern A 4x4 dither matrix.
|
|
* @param perChannel If 1 uses per channel dithering else uses overall luminance.
|
|
* @return 1.0 if the scaled luminance exceeds the threshold, otherwise 0.0.
|
|
*/
|
|
inline float3 GetDitherValue(uint2 uv, float3 scene, float4x4 pattern, int perChannel)
|
|
{
|
|
uint x = uv.x % 4;
|
|
uint y = uv.y % 4;
|
|
float threshold = pattern[y][x];
|
|
float ditherThreshold = _DitherThreshold * 20.0;
|
|
|
|
float lum = GetLuminance(scene);
|
|
float3 lumDither = step(threshold, lum * ditherThreshold);
|
|
float3 perChannelDither = step(threshold, scene * ditherThreshold);
|
|
|
|
return lerp(lumDither, perChannelDither, perChannel);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the dither offset from a 4x4 pattern for a given screen coordinate.
|
|
* This value is centered around 0 by subtracting 0.5, allowing additive dithering adjustments.
|
|
*
|
|
* @param uv The input coordinate.
|
|
* @param pattern A 4x4 dither matrix.
|
|
* @return A float value in the range [-0.5, 0.5] representing the dither shift at the given coordinate.
|
|
*/
|
|
inline float GetDitherShift(uint2 uv, float4x4 pattern)
|
|
{
|
|
uint x = uv.x % 4;
|
|
uint y = uv.y % 4;
|
|
return pattern[y][x] - 0.5;
|
|
}
|
|
|
|
/**
|
|
* Reduces the color depth of an RGB color into a discrete number of levels.
|
|
*
|
|
* @param color The input RGB color.
|
|
* @param steps The number of color levels per channel.
|
|
* @return The quantized color vector.
|
|
*/
|
|
inline float3 Quantize(float3 color, float steps)
|
|
{
|
|
return (_EnableColorPrecision > 0.5) ? floor(color * steps) / steps : color;
|
|
}
|
|
|
|
/**
|
|
* Converts screen space coordinates and depth buffer values back into 3D world space.
|
|
*
|
|
* @param uv The input coordinate.
|
|
* @param rawDepth The raw depth value from the Depth Texture.
|
|
* @return The reconstructed 3D position.
|
|
*/
|
|
inline float3 ReconstructWorldPosition(float2 uv, float rawDepth)
|
|
{
|
|
float2 screenPos = uv * _ScreenSize.xy;
|
|
float3 positionRWS = ComputeWorldSpacePosition(uv, rawDepth, UNITY_MATRIX_I_VP);
|
|
float3 absoluteWS = GetAbsolutePositionWS(positionRWS);
|
|
return absoluteWS;
|
|
}
|
|
|
|
/**
|
|
* Computes the scene color with fog at a coordiante.
|
|
*
|
|
* @param uv The input coordinate.
|
|
* @param scene The input RGB color.
|
|
* @return The final RGB color with fog.
|
|
*/
|
|
inline float3 CalculateFog(float2 uv, float3 scene)
|
|
{
|
|
float3 fogColor = scene;
|
|
if(_EnableFog)
|
|
{
|
|
// Convert raw depth to linear eye space distance
|
|
float rawDepth = SampleSceneDepth(uv);
|
|
float linearDepth = LinearEyeDepth(rawDepth, _ZBufferParams);
|
|
|
|
// Exponential squared fog calculation
|
|
float fogFactor = abs(1.0 - exp2(-linearDepth * _FogDensity));
|
|
|
|
// 3D noise based on world position
|
|
float3 worldPos = ReconstructWorldPosition(uv, rawDepth);
|
|
float noise = Perlin3D(worldPos * _FogNoiseScale);
|
|
|
|
// Generate masks to control where noise appears
|
|
float noiseMask = pow(fogFactor, _FogNoiseStart);
|
|
float edgeMask = abs(fogFactor * (1.0 - fogFactor) * 4.0);
|
|
float speckledMask = pow(edgeMask, _FogEdgeSmoothness);
|
|
|
|
// Combine fog with noise
|
|
float dynamicFog = fogFactor + ((noise - 0.5) * _FogNoiseStrength * speckledMask * noiseMask);
|
|
|
|
fogColor = lerp(scene, _FogColor.rgb, saturate(dynamicFog));
|
|
|
|
#if UNITY_REVERSED_Z
|
|
if (_IgnoreSkybox < 0.5 && rawDepth < 0.00001) return lerp(scene, fogColor, pow(_FogDensity, 0.1));
|
|
#else
|
|
if (_IgnoreSkybox < 0.5 && rawDepth > 0.99999) return lerp(scene, fogColor, pow(_FogDensity, 0.1));
|
|
#endif
|
|
}
|
|
|
|
return fogColor;
|
|
}
|
|
|
|
/**
|
|
* Applies a dithering effect to a scene color at a given screen coordinate.
|
|
* Supports both luminance-based and per-channel dithering, as well as additive or multiplicative modes.
|
|
*
|
|
* @param uv The input coordinate.
|
|
* @param scene The input RGB color.
|
|
* @return The dithered RGB color after applying the selected dither mode.
|
|
*/
|
|
inline float3 ApplyDither(float2 uv, float3 scene)
|
|
{
|
|
float3 finalCol = scene;
|
|
|
|
float screenAspect = _VNTGScreenParams.x / _VNTGScreenParams.y;
|
|
float targetAspect = _DitherReferenceResolution.x / _DitherReferenceResolution.y;
|
|
|
|
float aspectScale = (screenAspect < targetAspect) ? (_VNTGScreenParams.y / _DitherReferenceResolution.y) : (_VNTGScreenParams.x / _DitherReferenceResolution.x);
|
|
|
|
float useRelative = step(0.5, _DitherRelativeScale);
|
|
float scale = useRelative * aspectScale + (1.0 - useRelative);
|
|
|
|
float2 pixelCoords = uv * lerp(_VNTGScreenParams, _PixelResolution, _EnablePixelation * _DitherPixelPerfect);
|
|
|
|
uint2 ditherCoord = (uint2)(pixelCoords / (scale * max(1.0, _DitherScale)));
|
|
|
|
float4x4 pattern = GetDitherPattern(_DitherPattern);
|
|
|
|
float perChannelDither = step(0.5, _DitherMode);
|
|
|
|
float3 ditherMult = GetDitherValue(ditherCoord, scene.rgb, pattern, perChannelDither);
|
|
|
|
float ditherShift = GetDitherShift(ditherCoord, pattern);
|
|
float additiveValue = ditherShift * (1.0 / _ColorPrecision) * (1.0 - _DitherThreshold);
|
|
|
|
float isMultiplicative = step(_DitherMode, 1.5);
|
|
|
|
float3 multCol = finalCol * ditherMult;
|
|
float3 addCol = saturate(finalCol + additiveValue);
|
|
|
|
finalCol = lerp(addCol, multCol, isMultiplicative);
|
|
|
|
finalCol = lerp(scene, finalCol, _EnableDither);
|
|
|
|
return finalCol;
|
|
}
|
|
|
|
/**
|
|
* Applies a color palette lookup using a 3D LUT.
|
|
*
|
|
* @param The input RGB color.
|
|
* @return Remapped RGB color from the palette LUT
|
|
*/
|
|
inline float3 ApplyPaletteLookup(float3 color)
|
|
{
|
|
float originalLength = length(color);
|
|
float originalLum = GetLuminance(color);
|
|
|
|
if (_PaletteNormalizeLuminance > 0.5)
|
|
{
|
|
if (originalLength < 0.0001)
|
|
{
|
|
return float3(0.0, 0.0, 0.0);
|
|
}
|
|
|
|
float3 normalizedColor = color / originalLength;
|
|
float3 uvw = normalizedColor * (31.0 / 32.0) + (0.5 / 32.0);
|
|
float3 matchedColor = SAMPLE_TEXTURE3D(_PaletteLUT, sampler_PaletteLUT, uvw).rgb;
|
|
|
|
if (_PreserveLighting > 0.5)
|
|
{
|
|
float matchedLum = GetLuminance(matchedColor);
|
|
|
|
if (matchedLum > 0.0001)
|
|
{
|
|
matchedColor *= (originalLum / matchedLum);
|
|
}
|
|
}
|
|
|
|
return matchedColor;
|
|
}
|
|
else
|
|
{
|
|
float3 uvw = color * (31.0 / 32.0) + (0.5 / 32.0);
|
|
float3 matchedColor = SAMPLE_TEXTURE3D(_PaletteLUT, sampler_PaletteLUT, uvw).rgb;
|
|
|
|
if (_PreserveLighting > 0.5)
|
|
{
|
|
float matchedLum = GetLuminance(matchedColor);
|
|
|
|
if (matchedLum > 0.0001)
|
|
{
|
|
matchedColor *= (originalLum / matchedLum);
|
|
}
|
|
}
|
|
|
|
return matchedColor;
|
|
}
|
|
}
|
|
|
|
float4 Frag(Varyings IN) : SV_Target
|
|
{
|
|
float2 uv = IN.texcoord;
|
|
float mask = 1.0;
|
|
if (_EnableLetterbox > 0.5 && _EnablePixelation > 0.5)
|
|
{
|
|
float targetAspect = _PixelResolution.x / _PixelResolution.y;
|
|
float screenAspect = _VNTGScreenParams.x / _VNTGScreenParams.y;
|
|
|
|
float2 scale = 1.0;
|
|
if (screenAspect > targetAspect)
|
|
{
|
|
scale.x = screenAspect / targetAspect;
|
|
}
|
|
else
|
|
{
|
|
scale.y = targetAspect / screenAspect;
|
|
}
|
|
|
|
uv = (IN.texcoord - 0.5) * scale + 0.5;
|
|
|
|
float2 border = step(0.0, uv) * step(uv, 1.0);
|
|
mask = border.x * border.y;
|
|
}
|
|
|
|
float2 pixel = floor(uv * _PixelResolution);
|
|
float2 downsampledUV = (_EnablePixelation > 0.5) ? (pixel + 0.5) / _PixelResolution : uv;
|
|
|
|
float4 scene = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, downsampledUV);
|
|
|
|
float3 finalCol = ApplyDither((_DitherPixelPerfect > 0.5) ? uv : IN.texcoord, scene.rgb);
|
|
|
|
finalCol = Quantize(finalCol, _ColorPrecision);
|
|
|
|
if (_EnablePalette > 0.5)
|
|
{
|
|
finalCol = ApplyPaletteLookup(finalCol);
|
|
}
|
|
|
|
finalCol = CalculateFog(downsampledUV, finalCol);
|
|
|
|
return float4(finalCol * mask, scene.a);
|
|
}
|
|
ENDHLSL
|
|
}
|
|
}
|
|
} |