Files
PuzGame/src/game/rendering/Dither.cpp
2025-06-01 02:06:32 +02:00

135 lines
4.8 KiB
C++

#include "../Gen.h"
#include "../Log.h"
#include "Dither.h"
#include "bx/math.h"
using namespace Gen;
void DitherGen(DitherData& data, int32_t recursion)
{
data.Points[0] = {0.0f, 0.0f};
data.Points[1] = {0.5f, 0.5f};
data.Points[2] = {0.5f, 0.0f};
data.Points[3] = {0.0f, 0.5f};
data.PointCount = 4;
for (int32_t i = 0; i < data.BrightnessBucketCount; ++i)
data.BrightnessBuckets[i] = 0;
// add "subdivided" beyer matrix layers
for (int32_t recursionLevel = 0; recursionLevel < recursion - 1; ++recursionLevel)
{
int32_t startCount = data.PointCount;
float offset = bx::pow(0.5f, recursionLevel + 1);
for (int32_t i = 1; i < 4; ++i)
{
for (int32_t j = 0; j < startCount; ++j)
{
data.Points[data.PointCount] = data.Points[j] + data.Points[i] * offset;
data.PointCount++;
}
}
}
// Texture info setup
uint64_t dotsPerSide = bx::round(bx::pow(2, recursion));
data.DitherTexDepth = dotsPerSide * dotsPerSide;
data.DitherTexWH = 16 * dotsPerSide;
uint64_t texPixelCount = data.DitherTexWH * data.DitherTexWH * data.DitherTexDepth;
if (BX_COUNTOF(DitherData::DitherTex) < texPixelCount)
{
LOG_ERROR("Too many pixels: %llu", texPixelCount);
return;
}
// What does this do?
float invRes = 1.0f / data.DitherTexWH;
for (int32_t z = 0; z < data.DitherTexDepth; ++z)
{
int32_t dotCount = z + 1;
float dotArea = 0.5f / dotCount;
float dotRadius = bx::sqrt(dotArea / bx::kPi);
int32_t zOffset = z * data.DitherTexWH * data.DitherTexWH;
for (int32_t y = 0; y < data.DitherTexWH; ++y)
{
int32_t yOffset = y * data.DitherTexWH;
for (int32_t x = 0; x < data.DitherTexWH; ++x)
{
Vec2 point{(x + 0.5f) * invRes, (y + 0.5f) * invRes};
float dist = bx::kFloatInfinity;
for (int32_t i = 0; i < dotCount; ++i)
{
Vec2 vec = point - data.Points[i];
float wrapX = bx::wrap(vec.x + 0.5f, 1.0f) - 0.5f;
float wrapY = bx::wrap(vec.y + 0.5f, 1.0f) - 0.5f;
Vec2 wrappedVec = {wrapX, wrapY};
float curDist = Magnitude(wrappedVec);
dist = bx::min(dist, curDist);
}
dist = dist / (dotRadius * 2.4f);
float val = bx::clamp(1.0f - dist, 0.0f, 1.0f);
data.DitherTex[x + yOffset + zOffset] = Vec4{val, val, val, 1.0f};
int32_t bucket = bx::clamp(
uint32_t(val * DitherData::BrightnessBucketCount), 0, DitherData::BrightnessBucketCount - 1);
data.BrightnessBuckets[bucket] += 1;
}
}
}
// Brightness ramp
int32_t sum = 0;
for (int32_t i = 0; i < data.BrightnessBucketCount; ++i)
{
sum += data.BrightnessBuckets[data.BrightnessBucketCount - 1 - i];
data.BrightnessRamp[i + 1] = sum / (float)texPixelCount;
}
// Upload textures
CleanupDitherData(data);
const bgfx::Memory* memPreview = bgfx::makeRef(data.DitherTex, texPixelCount * sizeof(Vec4));
const bgfx::Memory* memFinal = bgfx::makeRef(data.DitherTex, texPixelCount * sizeof(Vec4));
const bgfx::Memory* memRamp = bgfx::makeRef(data.BrightnessRamp, sizeof(data.BrightnessRamp));
data.PreviewTex = bgfx::createTexture2D(data.DitherTexWH,
data.DitherTexWH * data.DitherTexDepth,
false,
false,
bgfx::TextureFormat::RGBA32F,
0,
memPreview);
data.FinalTex = bgfx::createTexture3D(
data.DitherTexWH, data.DitherTexWH, data.DitherTexDepth, false, bgfx::TextureFormat::RGBA32F, 0, memFinal);
data.RampTex =
bgfx::createTexture2D(BX_COUNTOF(data.BrightnessRamp), 1, false, 1, bgfx::TextureFormat::R32F, 0, memRamp);
if (!isValid(data.DitherSampler))
{
data.DitherSampler = bgfx::createUniform("s_ditherSampler", bgfx::UniformType::Sampler);
}
if (!isValid(data.RampSampler))
{
data.RampSampler = bgfx::createUniform("s_rampSampler", bgfx::UniformType::Sampler);
}
}
void CleanupDitherData(DitherData& data)
{
if (isValid(data.PreviewTex))
{
bgfx::destroy(data.PreviewTex);
data.PreviewTex = BGFX_INVALID_HANDLE;
}
if (isValid(data.FinalTex))
{
bgfx::destroy(data.FinalTex);
data.FinalTex = BGFX_INVALID_HANDLE;
}
if (isValid(data.RampTex))
{
bgfx::destroy(data.RampTex);
data.RampTex = BGFX_INVALID_HANDLE;
}
}