diff --git a/GlitchyEditor/resources/Shaders/RenderTargetGroupViewer.fx b/GlitchyEditor/resources/Shaders/RenderTargetGroupViewer.fx index f7102a7..7efeb98 100644 --- a/GlitchyEditor/resources/Shaders/RenderTargetGroupViewer.fx +++ b/GlitchyEditor/resources/Shaders/RenderTargetGroupViewer.fx @@ -1,3 +1,5 @@ +#include "ShaderHelpers.hlsl" + Texture2DArray Texture : register(t0); SamplerState Sampler : register(s0); @@ -7,11 +9,16 @@ SamplerState IntSampler : register(s1); Texture2DArray UIntTexture : register(t2); SamplerState UIntSampler : register(s2); -#define SWIZZLE_NONE 0 -#define SWIZZLE_R 1 -#define SWIZZLE_G 2 -#define SWIZZLE_B 3 -#define SWIZZLE_A 4 +#define SWIZZLE_ZERO 0 +#define SWIZZLE_ONE 1 +#define SWIZZLE_R 2 +#define SWIZZLE_G 3 +#define SWIZZLE_B 4 +#define SWIZZLE_A 5 +#define SWIZZLE_ONE_MINUS_R 6 +#define SWIZZLE_ONE_MINUS_G 7 +#define SWIZZLE_ONE_MINUS_B 8 +#define SWIZZLE_ONE_MINUS_A 9 cbuffer Constants { @@ -31,6 +38,9 @@ cbuffer Constants float2 Texels; float4x4 WorldViewProjection; + + bool ShowTexelBorders = true; + float TexelBorderCutoff = 10; } struct VS_Input @@ -59,6 +69,12 @@ void SwizzleColor(int swizzleMode, float4 colorToSwizzle, inout float output) { switch (swizzleMode) { + case SWIZZLE_ZERO: + output = 0.0; + break; + case SWIZZLE_ONE: + output = 1.0; + break; case SWIZZLE_R: output = colorToSwizzle.r; break; @@ -70,31 +86,77 @@ void SwizzleColor(int swizzleMode, float4 colorToSwizzle, inout float output) break; case SWIZZLE_A: output = colorToSwizzle.a; + break; + case SWIZZLE_ONE_MINUS_R: + output = 1.0 - colorToSwizzle.r; + break; + case SWIZZLE_ONE_MINUS_G: + output = 1.0 - colorToSwizzle.g; + break; + case SWIZZLE_ONE_MINUS_B: + output = 1.0 - colorToSwizzle.b; + break; + case SWIZZLE_ONE_MINUS_A: + output = 1.0 - colorToSwizzle.a; break; default: break; } } +/** + * Returns whether or not the current pixel lies on the border between two texels. + */ +void ApplyTexelBorder(float2 texelPosition, inout float4 color) +{ + // Calculate derivatives of texelPosition -> how many texels to we move if we move one pixel in x/y direction + float2 dudx = ddx(texelPosition); + float2 dudy = ddy(texelPosition); + + // This is the reciprocal of the screen size of one texel. + float grad = float2(length(dudx), length(dudy)); + + float2 pixelsPerTexel = 1.0f / grad; + + // Threshold is half the size of a screen pixel in texel-space. + float2 threshold = grad / 2.0f; + + // Calculate the distance between the screen pixel and the texel border in texels. + float2 fracPart = frac(texelPosition); + float2 distanceToBorder = min(fracPart, 1.0 - fracPart); + + if (any(distanceToBorder < threshold)) + { + // Ensure the border is always at least 0.5 luminance away from the texel. + // TODO: This can flicker while moving/zooming, because we are by design on a texel border! + float luminance = ColorToLuminance(color.rgb); + float4 borderColor = (luminance > 0.5f) ? float4(0, 0, 0, 1) : float4(1, 1, 1, 1); + + color = lerp(color, borderColor, clamp((min(pixelsPerTexel.x, pixelsPerTexel.y) - TexelBorderCutoff / 2.0) / TexelBorderCutoff, 0.0f, 0.8f)); + } +} + float4 PS(PS_Input input) : SV_Target0 { float4 outputColor; + float2 texelPosition = input.Texcoord * Texels; + if (Mode == 0) { //float4 color = Texture.SampleLevel(Sampler, float3(input.Texcoord, ArraySlice), MipLevel); // Quasi next neighbor - float4 color = Texture.Load(int4(input.Texcoord * Texels, ArraySlice, MipLevel)); + float4 color = Texture.Load(int4(texelPosition, ArraySlice, MipLevel)); outputColor = float4(ColorOffset.xxx, AlphaOffset) + color * float4(ColorScale.xxx, AlphaScale); } else if (Mode == 1) { - int4 color = IntTexture.Load(int4(input.Texcoord * Texels, ArraySlice, MipLevel)); + int4 color = IntTexture.Load(int4(texelPosition, ArraySlice, MipLevel)); outputColor = float4(ColorOffset.xxx, AlphaOffset) + color * float4(ColorScale.xxx, AlphaScale); } else if (Mode == 2) { - uint4 color = UIntTexture.Load(int4(input.Texcoord * Texels, ArraySlice, MipLevel)); + uint4 color = UIntTexture.Load(int4(texelPosition, ArraySlice, MipLevel)); outputColor = float4(ColorOffset.xxx, AlphaOffset) + color * float4(ColorScale.xxx, AlphaScale); } @@ -105,6 +167,11 @@ float4 PS(PS_Input input) : SV_Target0 SwizzleColor(Swizzle.b, outputColor, swizzledOutput.b); SwizzleColor(Swizzle.a, outputColor, swizzledOutput.a); + if (ShowTexelBorders) + { + ApplyTexelBorder(texelPosition, swizzledOutput); + } + return swizzledOutput; } diff --git a/GlitchyEditor/resources/Shaders/ShaderHelpers.hlsl b/GlitchyEditor/resources/Shaders/ShaderHelpers.hlsl index 77413e8..f8d7b58 100644 --- a/GlitchyEditor/resources/Shaders/ShaderHelpers.hlsl +++ b/GlitchyEditor/resources/Shaders/ShaderHelpers.hlsl @@ -72,7 +72,7 @@ float3x3 ConstructTangentSpace(float3 normal, float3 tangent, float3 sigma) */ float ColorToLuminance(float3 rgb) { - return rgb.r * 0.212639 + rgb.g * 0.715169 + rgb.b * 0.072192; + return rgb.r * 0.212639 + rgb.g * 0.715169 + rgb.b * 0.072192; } /** diff --git a/GlitchyEditor/src/EditWindows/AssetViewer.bf b/GlitchyEditor/src/EditWindows/AssetViewer.bf index 68d1f82..af33c70 100644 --- a/GlitchyEditor/src/EditWindows/AssetViewer.bf +++ b/GlitchyEditor/src/EditWindows/AssetViewer.bf @@ -152,11 +152,16 @@ class TextureViewer enum ColorChannelSwizzle : int32 { - None, + Zero, + One, R, G, B, - A + A, + OneMinusR, + OneMinusG, + OneMinusB, + OneMinusA } AssetHandle _effect; @@ -164,6 +169,7 @@ class TextureViewer Material _renderTargetMaterial ~ _.ReleaseRef(); float _zoom = 1.0f; + bool _showTexelBorder = false; BackgroundMode _backgroundMode = .Checkerboard; ColorRGBA _backgroundColor; @@ -171,10 +177,6 @@ class TextureViewer SampleMode _sampleMode = .Linear; RenderTargetGroup _targets ~ _?.ReleaseRef(); - /*RenderTarget2D _target ~ _?.ReleaseRef(); - RenderTarget2D _idTarget ~ _?.ReleaseRef(); - // TODO: we don't need depth! - DepthStencilTarget _depth ~ _?.ReleaseRef();*/ public this() { @@ -281,8 +283,6 @@ class TextureViewer if (ImGui.CollapsingHeader("Color and Transparency")) { - ImGui.TextUnformatted("Channel Swizzle:"); - if (ImGui.BeginTable("ColorAndAlpha", 4)) { ImGui.TableNextRow(); @@ -338,6 +338,10 @@ class TextureViewer ImGui.Separator(); + ImGui.Checkbox("Show texel grid", &_showTexelBorder); + + ImGui.Separator(); + ImGui.TextUnformatted("Channel Swizzle:"); if (ImGui.BeginTable("SwizzleTable", 9)) @@ -376,6 +380,12 @@ class TextureViewer if (ImGui.BeginCombo(scope $"##swizzle{text}", scope $"{swizzle}")) { + if (ImGui.Selectable("0", swizzle == .Zero)) + swizzle = .Zero; + + if (ImGui.Selectable("1", swizzle == .One)) + swizzle = .One; + if (ImGui.Selectable("R", swizzle == .R)) swizzle = .R; @@ -387,9 +397,18 @@ class TextureViewer if (ImGui.Selectable("A", swizzle == .A)) swizzle = .A; + + if (ImGui.Selectable("1 - R", swizzle == .OneMinusR)) + swizzle = .OneMinusR; + + if (ImGui.Selectable("1 - G", swizzle == .OneMinusG)) + swizzle = .OneMinusG; - if (ImGui.Selectable("None", swizzle == .None)) - swizzle = .None; + if (ImGui.Selectable("1 - B", swizzle == .OneMinusB)) + swizzle = .OneMinusB; + + if (ImGui.Selectable("1 - A", swizzle == .OneMinusA)) + swizzle = .OneMinusA; ImGui.EndCombo(); } @@ -590,10 +609,6 @@ class TextureViewer viewedTexture.SamplerState = SamplerStateManager.LinearClamp; }*/ - //Renderer2D.BeginScene(_camera, .SortByTexture, _effect); - - // float3(_position * .(1, -1), 0) - //RenderCommand.Clear(_depth, .Depth, 1.0f, 0); RenderCommand.Clear(_targets, .Depth); Matrix matrix = .Translation(float3((_position + float2(_targets.Width, _targets.Height) / 2) * .(1, -1), 0)) * .Scaling(float3(zoomedTextureSize, 1)); @@ -609,6 +624,9 @@ class TextureViewer _renderTargetMaterial.SetVariable("MipLevel", (float)_mipLevel); _renderTargetMaterial.SetVariable("Swizzle", int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA)); + + _renderTargetMaterial.SetVariable("ShowTexelBorders", _showTexelBorder); + _renderTargetMaterial.SetVariable("TexelBorderCutoff", 10.0f); int textureSlot = -1;