From 0be1251ce1f5f802f4f4436b83f6751a9e68d564 Mon Sep 17 00:00:00 2001 From: RDW Date: Mon, 18 Mar 2024 15:11:42 +0100 Subject: [PATCH] Client: Improve the accuracy of downsampled texture colors It seems that some error correction is applied by the fixed-function pipeline when operating on the downsampled (reduced-color) textures? Merely truncating the colors, as previously done, yields visible artifacts with an accumulating error consistent with the dropped precision (1 per 32 for bpp = 5). The base colors would have to be either uint8 or floats, which carry more information after all. The results are otherwise fairly exact, at least on the few maps I tested. This may also apply to lightmaps? (TBD) --- Core/NativeClient/WebGPU/Texture.lua | 16 ++++++++++------ Tests/NativeClient/WebGPU/Texture.spec.lua | 12 ++++++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Core/NativeClient/WebGPU/Texture.lua b/Core/NativeClient/WebGPU/Texture.lua index 3a8963b2..a0d8ca66 100644 --- a/Core/NativeClient/WebGPU/Texture.lua +++ b/Core/NativeClient/WebGPU/Texture.lua @@ -7,7 +7,7 @@ local webgpu = require("wgpu") local cast = ffi.cast local new = ffi.new -local math_floor = math.floor +local floor = math.floor local Texture = { DEFAULT_TEXTURE_FORMAT = ffi.C.WGPUTextureFormat_RGBA8Unorm, @@ -192,7 +192,7 @@ function Texture:GenerateCheckeredGridImage(textureWidthInPixels, textureHeightI for v = 0, textureHeightInPixels - 1 do local index = 4 * (v * textureWidthInPixels + u) - local useAlternateColor = math_floor(u / GRID_CELL_SIZE) % 2 == math_floor(v / GRID_CELL_SIZE) % 2 + local useAlternateColor = floor(u / GRID_CELL_SIZE) % 2 == floor(v / GRID_CELL_SIZE) % 2 local cellColor = useAlternateColor and firstColor or secondColor pixels[index + RGBA_OFFSET_RED] = ffi.cast("uint8_t", cellColor.red * 255) @@ -234,10 +234,14 @@ function Texture:CreateReducedColorImage(inputImageBytes, width, height, posteri posterizedBlue = bit.lshift(posterizedBlue, posterizationLevel) posterizedAlpha = bit.lshift(posterizedAlpha, posterizationLevel) - bufferStartPointer[writableAreaStartIndex + 0] = posterizedRed - bufferStartPointer[writableAreaStartIndex + 1] = posterizedGreen - bufferStartPointer[writableAreaStartIndex + 2] = posterizedBlue - bufferStartPointer[writableAreaStartIndex + 3] = posterizedAlpha + local remainingColorDepthInBitsPerPixel = math.max(1, 8 - posterizationLevel) + local numAvailableColorValues = math.pow(2, remainingColorDepthInBitsPerPixel) -- 256, 128, 64, 32, 16, 8 + local errorCorrection = 1 / numAvailableColorValues + + bufferStartPointer[writableAreaStartIndex + 0] = posterizedRed + floor(errorCorrection * red) + bufferStartPointer[writableAreaStartIndex + 1] = posterizedGreen + floor(errorCorrection * green) + bufferStartPointer[writableAreaStartIndex + 2] = posterizedBlue + floor(errorCorrection * blue) + bufferStartPointer[writableAreaStartIndex + 3] = posterizedAlpha + floor(errorCorrection * alpha) numBytesWritten = numBytesWritten + 4 end diff --git a/Tests/NativeClient/WebGPU/Texture.spec.lua b/Tests/NativeClient/WebGPU/Texture.spec.lua index 27612ef9..1202c5cf 100644 --- a/Tests/NativeClient/WebGPU/Texture.spec.lua +++ b/Tests/NativeClient/WebGPU/Texture.spec.lua @@ -209,10 +209,18 @@ describe("Texture", function() it("should reduce the color depth of the texture image", function() local testImage = "\255\192\128\000" .. "\007\007\007\007" local reducedColorImage, width, height = Texture:CreateReducedColorImage(testImage, 2, 1) - local expectedimageBytes = "\248\192\128\000\000\000\000\000" + local expectedImageBytes = buffer.new():put("\255\198\132\000\000\000\000\000") assertEquals(width, 2) assertEquals(height, 1) - assertEquals(tostring(reducedColorImage), expectedimageBytes) + assertEquals(reducedColorImage[0], expectedImageBytes[0]) + assertEquals(reducedColorImage[1], expectedImageBytes[1]) + assertEquals(reducedColorImage[2], expectedImageBytes[2]) + assertEquals(reducedColorImage[3], expectedImageBytes[3]) + assertEquals(reducedColorImage[4], expectedImageBytes[4]) + assertEquals(reducedColorImage[5], expectedImageBytes[5]) + assertEquals(reducedColorImage[6], expectedImageBytes[6]) + assertEquals(reducedColorImage[7], expectedImageBytes[7]) + assertEquals(tostring(reducedColorImage), tostring(expectedImageBytes)) end) end) end)