Skip to content

Instantly share code, notes, and snippets.

@digiwombat
Created January 25, 2022 04:07
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save digiwombat/1991353eadaf918291e8a199694fcc7c to your computer and use it in GitHub Desktop.
Save digiwombat/1991353eadaf918291e8a199694fcc7c to your computer and use it in GitHub Desktop.
URP 2D Lit Smooth Pixel Scaling
Shader "Sprites/JitterFreeLit"
{
Properties
{
_MainTex("Diffuse", 2D) = "white" {}
_MaskTex("Mask", 2D) = "white" {}
_NormalMap("Normal Map", 2D) = "bump" {}
// Legacy properties. They're here so that materials using this shader can gracefully fallback to the legacy sprite shader.
[HideInInspector] _Color("Tint", Color) = (1,1,1,1)
[HideInInspector] _RendererColor("RendererColor", Color) = (1,1,1,1)
[HideInInspector] _Flip("Flip", Vector) = (1,1,1,1)
[HideInInspector] _AlphaTex("External Alpha", 2D) = "white" {}
[HideInInspector] _EnableExternalAlpha("Enable External Alpha", Float) = 0
}
SubShader
{
Tags {"Queue" = "Transparent" "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline" }
Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha
Cull Off
ZWrite Off
Pass
{
Tags { "LightMode" = "Universal2D" }
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#pragma vertex CombinedShapeLightVertex
#pragma fragment CombinedShapeLightFragment
#pragma multi_compile USE_SHAPE_LIGHT_TYPE_0 __
#pragma multi_compile USE_SHAPE_LIGHT_TYPE_1 __
#pragma multi_compile USE_SHAPE_LIGHT_TYPE_2 __
#pragma multi_compile USE_SHAPE_LIGHT_TYPE_3 __
#pragma multi_compile _ DEBUG_DISPLAY
struct Attributes
{
float3 positionOS : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
half4 color : COLOR;
float2 uv : TEXCOORD0;
half2 lightingUV : TEXCOORD1;
#if defined(DEBUG_DISPLAY)
float3 positionWS : TEXCOORD2;
#endif
UNITY_VERTEX_OUTPUT_STEREO
};
#include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/LightingUtility.hlsl"
//TEXTURE2D(_MainTex);
//SAMPLER(sampler_MainTex);
TEXTURE2D(_MaskTex);
SAMPLER(sampler_MaskTex);
sampler2D _MainTex;
half4 _MainTex_ST;
float4 _MainTex_TexelSize;
#if USE_SHAPE_LIGHT_TYPE_0
SHAPE_LIGHT(0)
#endif
#if USE_SHAPE_LIGHT_TYPE_1
SHAPE_LIGHT(1)
#endif
#if USE_SHAPE_LIGHT_TYPE_2
SHAPE_LIGHT(2)
#endif
#if USE_SHAPE_LIGHT_TYPE_3
SHAPE_LIGHT(3)
#endif
Varyings CombinedShapeLightVertex(Attributes v)
{
Varyings o = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.positionCS = TransformObjectToHClip(v.positionOS);
#if defined(DEBUG_DISPLAY)
o.positionWS = TransformObjectToWorld(v.positionOS);
#endif
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.lightingUV = half2(ComputeScreenPos(o.positionCS / o.positionCS.w).xy);
o.color = v.color;
return o;
}
#include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/CombinedShapeLightShared.hlsl"
float4 texturePointSmooth(sampler2D tex, float2 uvs)
{
float2 size;
size.x = _MainTex_TexelSize.z;
size.y = _MainTex_TexelSize.w;
float2 pixel = float2(1.0,1.0) / size;
uvs -= pixel * float2(0.5,0.5);
float2 uv_pixels = uvs * size;
float2 delta_pixel = frac(uv_pixels) - float2(0.5,0.5);
float2 ddxy = fwidth(uv_pixels);
float2 mip = log2(ddxy) - 0.5;
float2 clampedUV = uvs + (clamp(delta_pixel / ddxy, 0.0, 1.0) - delta_pixel) * pixel;
return tex2Dlod(tex, float4(clampedUV,0, min(mip.x, mip.y)));
}
half4 CombinedShapeLightFragment(Varyings i) : SV_Target
{
//const half4 main = i.color * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
const half4 main = i.color * texturePointSmooth(_MainTex, i.uv);
//const half4 mask = SAMPLE_TEXTURE2D(_MaskTex, sampler_MaskTex, i.uv);
const half4 mask = texturePointSmooth(_MainTex, i.uv);
SurfaceData2D surfaceData;
InputData2D inputData;
InitializeSurfaceData(main.rgb, main.a, mask, surfaceData);
InitializeInputData(i.uv, i.lightingUV, inputData);
return CombinedShapeLightShared(surfaceData, inputData);
}
ENDHLSL
}
Pass
{
Tags { "LightMode" = "NormalsRendering"}
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#pragma vertex NormalsRenderingVertex
#pragma fragment NormalsRenderingFragment
struct Attributes
{
float3 positionOS : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
float4 tangent : TANGENT;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
half4 color : COLOR;
float2 uv : TEXCOORD0;
half3 normalWS : TEXCOORD1;
half3 tangentWS : TEXCOORD2;
half3 bitangentWS : TEXCOORD3;
UNITY_VERTEX_OUTPUT_STEREO
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
TEXTURE2D(_NormalMap);
SAMPLER(sampler_NormalMap);
half4 _NormalMap_ST; // Is this the right way to do this?
float4 _MainTex_TexelSize;
Varyings NormalsRenderingVertex(Attributes attributes)
{
Varyings o = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(attributes);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.positionCS = TransformObjectToHClip(attributes.positionOS);
o.uv = TRANSFORM_TEX(attributes.uv, _NormalMap);
o.color = attributes.color;
o.normalWS = -GetViewForwardDir();
o.tangentWS = TransformObjectToWorldDir(attributes.tangent.xyz);
o.bitangentWS = cross(o.normalWS, o.tangentWS) * attributes.tangent.w;
return o;
}
#include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/NormalsRenderingShared.hlsl"
half4 NormalsRenderingFragment(Varyings i) : SV_Target
{
const half4 mainTex = i.color * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
const half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, i.uv));
return NormalsRenderingShared(mainTex, normalTS, i.tangentWS.xyz, i.bitangentWS.xyz, i.normalWS.xyz);
}
ENDHLSL
}
Pass
{
Tags { "LightMode" = "UniversalForward" "Queue"="Transparent" "RenderType"="Transparent"}
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#pragma vertex UnlitVertex
#pragma fragment UnlitFragment
struct Attributes
{
float3 positionOS : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
#if defined(DEBUG_DISPLAY)
float3 positionWS : TEXCOORD2;
#endif
UNITY_VERTEX_OUTPUT_STEREO
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
Varyings UnlitVertex(Attributes attributes)
{
Varyings o = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(attributes);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.positionCS = TransformObjectToHClip(attributes.positionOS);
#if defined(DEBUG_DISPLAY)
o.positionWS = TransformObjectToWorld(v.positionOS);
#endif
o.uv = TRANSFORM_TEX(attributes.uv, _MainTex);
o.color = attributes.color;
return o;
}
float4 UnlitFragment(Varyings i) : SV_Target
{
float4 mainTex = i.color * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
#if defined(DEBUG_DISPLAY)
SurfaceData2D surfaceData;
InputData2D inputData;
half4 debugColor = 0;
InitializeSurfaceData(mainTex.rgb, mainTex.a, surfaceData);
InitializeInputData(i.uv, inputData);
SETUP_DEBUG_DATA_2D(inputData, i.positionWS);
if(CanDebugOverrideOutputColor(surfaceData, inputData, debugColor))
{
return debugColor;
}
#endif
return mainTex;
}
ENDHLSL
}
}
Fallback "Sprites/Default"
}
@digiwombat
Copy link
Author

This is a URP 2D Lit version of the JitterFreeUnLit.shader by 9D Tony.

To use it, make sure your sprites are set to filter mode Bilinear and then just apply the Shader to a Material and the Material to your sprites.

Hope it's useful to you. Enjoy.

@digiwombat
Copy link
Author

You can also do a ShaderGraph version by just running the _MainTex and UV through the custom function.

image

float2 size;
size.x = _MainTex_TexelSize.z;
size.y = _MainTex_TexelSize.w;

float2 pixel = float2(1.0,1.0) / size;

uvs -= pixel * float2(0.5,0.5);
float2 uv_pixels = uvs * size;
float2 delta_pixel = frac(uv_pixels) - float2(0.5,0.5);

float2 ddxy = fwidth(uv_pixels);
float2 mip = log2(ddxy) - 0.5;

float2 clampedUV = uvs + (clamp(delta_pixel / ddxy, 0.0, 1.0) - delta_pixel) * pixel;
_out = tex2Dlod(tex, float4(clampedUV,0, min(mip.x, mip.y)));

@justcore69
Copy link

Thank you very much! I was looking for this shader for a while, but all what i found was either unlit shader or code implementation. Your custom function turned out to be the best option for me! ⭐

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment