Fix the fragment shader to work with any background color

There now some dither to avoid banding in the gradients.
This commit is contained in:
luisangelsm
2026-01-19 19:23:57 +01:00
parent 41b02725d7
commit 720d58533c
6 changed files with 47 additions and 27 deletions

View File

@ -51,11 +51,19 @@ void main()
// For reflections, apply a gradient fade using reflection uniforms (darker further away) // For reflections, apply a gradient fade using reflection uniforms (darker further away)
if (vIsReflection > 0.5) { if (vIsReflection > 0.5) {
// Legacy reflection fade: ramp from 0.0 to 0.33 using texture V coordinate float gradientFade = mix(reflectionUp, reflectionDown, vTexCoord.y);
float gradientFade = mix(0.0, 0.33, vTexCoord.y);
shadingAmount *= gradientFade; shadingAmount *= gradientFade;
} }
// Final color: shaded RGB, keep source alpha // Subtle dithering to break up bands (very low amplitude, within 8-bit quantization)
fragColor = vec4(texColor.rgb * shadingAmount, texColor.a); float rnd = fract(sin(dot(gl_FragCoord.xy, vec2(12.9898,78.233))) * 43758.5453);
// scale to roughly +/- 0.5/255 range (adjust strength if needed)
float dither = (rnd - 0.5) / 255.0;
float shadedAmountDithered = clamp(shadingAmount + dither, 0.0, 1.0);
fragColor = vec4(
mix(backgroundColor, texColor.rgb, shadedAmountDithered),
texColor.a
);
} }

Binary file not shown.

View File

@ -37,7 +37,7 @@ void YACReaderComicFlow3D::updateImageData()
// Create QRhiTexture from the loaded image // Create QRhiTexture from the loaded image
if (m_rhi) { if (m_rhi) {
QRhiTexture *texture = m_rhi->newTexture(QRhiTexture::BGRA8, img.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips); QRhiTexture *texture = m_rhi->newTexture(QRhiTexture::RGBA8, img.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
if (texture->create()) { if (texture->create()) {
PendingTextureUpload upload; PendingTextureUpload upload;
@ -168,7 +168,7 @@ QImage ImageLoader3D::loadImage(const QString &fileName)
break; break;
} }
return image; return image.convertToFormat(QImage::Format_RGBA8888);
} }
ImageLoader3D::ImageLoader3D(YACReaderFlow3D *flow) ImageLoader3D::ImageLoader3D(YACReaderFlow3D *flow)

View File

@ -37,8 +37,8 @@ YACReaderFlow3D::YACReaderFlow3D(QWidget *parent, struct Preset p)
shadingTop = 0.8f; shadingTop = 0.8f;
shadingBottom = 0.02f; shadingBottom = 0.02f;
reflectionUp = 0.f; reflectionUp = 0.0f;
reflectionBottom = 0.6f; reflectionBottom = 0.33f;
setBackgroundColor(Qt::black); setBackgroundColor(Qt::black);
@ -119,9 +119,9 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
// Initialize default texture from image // Initialize default texture from image
if (!scene.defaultTexture) { if (!scene.defaultTexture) {
QImage defaultImage(":/images/defaultCover.png"); QImage defaultImage = QImage(":/images/defaultCover.png").convertToFormat(QImage::Format_RGBA8888);
scene.defaultTexture.reset(m_rhi->newTexture(QRhiTexture::BGRA8, defaultImage.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips)); scene.defaultTexture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, defaultImage.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips));
scene.defaultTexture->create(); scene.defaultTexture->create();
getResourceBatch()->uploadTexture(scene.defaultTexture.get(), defaultImage); getResourceBatch()->uploadTexture(scene.defaultTexture.get(), defaultImage);
getResourceBatch()->generateMips(scene.defaultTexture.get()); getResourceBatch()->generateMips(scene.defaultTexture.get());
@ -131,9 +131,9 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
#ifdef YACREADER_LIBRARY #ifdef YACREADER_LIBRARY
// Initialize mark textures // Initialize mark textures
if (!scene.markTexture) { if (!scene.markTexture) {
QImage markImage(":/images/readRibbon.png"); QImage markImage = QImage(":/images/readRibbon.png").convertToFormat(QImage::Format_RGBA8888);
if (!markImage.isNull()) { if (!markImage.isNull()) {
scene.markTexture.reset(m_rhi->newTexture(QRhiTexture::BGRA8, markImage.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips)); scene.markTexture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, markImage.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips));
scene.markTexture->create(); scene.markTexture->create();
getResourceBatch()->uploadTexture(scene.markTexture.get(), markImage); getResourceBatch()->uploadTexture(scene.markTexture.get(), markImage);
getResourceBatch()->generateMips(scene.markTexture.get()); getResourceBatch()->generateMips(scene.markTexture.get());
@ -141,9 +141,9 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
} }
if (!scene.readingTexture) { if (!scene.readingTexture) {
QImage readingImage(":/images/readingRibbon.png"); QImage readingImage = QImage(":/images/readingRibbon.png").convertToFormat(QImage::Format_RGBA8888);
if (!readingImage.isNull()) { if (!readingImage.isNull()) {
scene.readingTexture.reset(m_rhi->newTexture(QRhiTexture::BGRA8, readingImage.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips)); scene.readingTexture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, readingImage.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips));
scene.readingTexture->create(); scene.readingTexture->create();
getResourceBatch()->uploadTexture(scene.readingTexture.get(), readingImage); getResourceBatch()->uploadTexture(scene.readingTexture.get(), readingImage);
getResourceBatch()->generateMips(scene.readingTexture.get()); getResourceBatch()->generateMips(scene.readingTexture.get());
@ -605,13 +605,25 @@ void YACReaderFlow3D::prepareDrawData(const YACReader3DImageRHI &image, bool isR
// Store per-instance rotation in the instance data (new slot at index 22) // Store per-instance rotation in the instance data (new slot at index 22)
outInstanceData[22] = image.current.rot; outInstanceData[22] = image.current.rot;
// Prepare uniform data // Prepare uniform data (copy float data into POD arrays)
outUniformData.viewProjectionMatrix = viewProjectionMatrix; const float *vp = viewProjectionMatrix.constData();
outUniformData.backgroundColor = QVector3D(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF()); for (int m = 0; m < 16; ++m)
outUniformData.shadingColor = QVector3D(shadingColor.redF(), shadingColor.greenF(), shadingColor.blueF()); outUniformData.viewProjectionMatrix[m] = vp[m];
outUniformData.backgroundColor[0] = backgroundColor.redF();
outUniformData.backgroundColor[1] = backgroundColor.greenF();
outUniformData.backgroundColor[2] = backgroundColor.blueF();
outUniformData._pad0 = 0.0f;
outUniformData.shadingColor[0] = shadingColor.redF();
outUniformData.shadingColor[1] = shadingColor.greenF();
outUniformData.shadingColor[2] = shadingColor.blueF();
outUniformData._pad1 = 0.0f;
outUniformData.reflectionUp = reflectionUp; outUniformData.reflectionUp = reflectionUp;
outUniformData.reflectionDown = reflectionBottom; outUniformData.reflectionDown = reflectionBottom;
outUniformData.isReflection = isReflection ? 1 : 0; outUniformData.isReflection = isReflection ? 1.0f : 0.0f;
outUniformData._pad2 = 0.0f;
} }
void YACReaderFlow3D::executeDrawWithOffset(QRhiCommandBuffer *cb, QRhiTexture *texture, void YACReaderFlow3D::executeDrawWithOffset(QRhiCommandBuffer *cb, QRhiTexture *texture,

View File

@ -112,14 +112,14 @@ protected:
// Uniform buffer data structure (must match shader layout) // Uniform buffer data structure (must match shader layout)
struct UniformData { struct UniformData {
QMatrix4x4 viewProjectionMatrix; float viewProjectionMatrix[16]; // column-major 4x4
QVector3D backgroundColor; float backgroundColor[3];
float _pad0; float _pad0; // pad to vec4
QVector3D shadingColor; float shadingColor[3];
float _pad1; float _pad1; // pad to vec4
float reflectionUp; float reflectionUp;
float reflectionDown; float reflectionDown;
int isReflection; float isReflection;
float _pad2; float _pad2;
}; };

View File

@ -40,7 +40,7 @@ void YACReaderPageFlow3D::updateImageData()
// Create QRhiTexture from the loaded image and queue the pixel upload // Create QRhiTexture from the loaded image and queue the pixel upload
if (m_rhi) { if (m_rhi) {
QRhiTexture *texture = m_rhi->newTexture(QRhiTexture::BGRA8, img.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips); QRhiTexture *texture = m_rhi->newTexture(QRhiTexture::RGBA8, img.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
if (texture->create()) { if (texture->create()) {
// Queue the image upload so it happens together with other resource updates // Queue the image upload so it happens together with other resource updates
@ -136,7 +136,7 @@ QImage ImageLoaderByteArray3D::loadImage(const QByteArray &raw)
break; break;
} }
return image; return image.convertToFormat(QImage::Format_RGBA8888);
} }
ImageLoaderByteArray3D::ImageLoaderByteArray3D(YACReaderFlow3D *flow) ImageLoaderByteArray3D::ImageLoaderByteArray3D(YACReaderFlow3D *flow)