diff --git a/YACReader/YACReader.pro b/YACReader/YACReader.pro index 940ea723..42f78184 100644 --- a/YACReader/YACReader.pro +++ b/YACReader/YACReader.pro @@ -157,7 +157,10 @@ SOURCES += ../common/comic.cpp \ SOURCES += ../common/rhi/yacreader_flow_rhi.cpp SOURCES += ../common/rhi/yacreader_comic_flow_rhi.cpp \ ../common/rhi/yacreader_page_flow_rhi.cpp - RESOURCES += ../common/rhi/shaders/shaders.qrc + RESOURCES += ../common/rhi/shaders/shaders.qrc + # Make raw GLSL shader sources editable in Qt Creator + OTHER_FILES += ../common/rhi/shaders/flow.vert \ + ../common/rhi/shaders/flow.frag } } diff --git a/YACReaderLibrary/YACReaderLibrary.pro b/YACReaderLibrary/YACReaderLibrary.pro index 11952c35..1a79aadb 100644 --- a/YACReaderLibrary/YACReaderLibrary.pro +++ b/YACReaderLibrary/YACReaderLibrary.pro @@ -253,7 +253,10 @@ SOURCES += comic_flow.cpp \ SOURCES += ../common/rhi/yacreader_flow_rhi.cpp SOURCES += ../common/rhi/yacreader_comic_flow_rhi.cpp \ ../common/rhi/yacreader_page_flow_rhi.cpp - RESOURCES += ../common/rhi/shaders/shaders.qrc + RESOURCES += ../common/rhi/shaders/shaders.qrc + # Make raw GLSL shader sources editable in Qt Creator + OTHER_FILES += ../common/rhi/shaders/flow.vert \ + ../common/rhi/shaders/flow.frag } } diff --git a/common/rhi/shaders/flow.frag b/common/rhi/shaders/flow.frag index 01b474d9..5e6f6c40 100644 --- a/common/rhi/shaders/flow.frag +++ b/common/rhi/shaders/flow.frag @@ -2,8 +2,11 @@ // Inputs from vertex shader layout(location = 0) in vec2 vTexCoord; -layout(location = 1) in vec4 vColor; -layout(location = 2) in float vIsReflection; +layout(location = 1) in flat vec4 vInstanceShading; +layout(location = 2) in flat float vInstanceOpacity; +layout(location = 3) in flat float vIsReflection; +layout(location = 4) in flat float vInstanceRotation; +layout(location = 5) in vec2 vLocalPos; // Output layout(location = 0) out vec4 fragColor; @@ -29,16 +32,30 @@ void main() { vec4 texColor = texture(coverTexture, vTexCoord); - // Apply shading: multiply texture by vColor.r to darken - float shadingAmount = vColor.r; + // Compute per-pixel shading using per-instance corner shading values + float leftUp = vInstanceShading.x; + float leftDown = vInstanceShading.y; + float rightUp = vInstanceShading.z; + float rightDown = vInstanceShading.w; - // For reflections, apply gradient fade (darker at bottom, fading to black) + // Use interpolated local vertex position to match original vertex-based shading. + // position.y in vertex ranges [-0.5..0.5], so map to [0..1] with +0.5 + float y = clamp(vLocalPos.y + 0.5, 0.0, 1.0); + float x = clamp(vLocalPos.x + 0.5, 0.0, 1.0); + float leftShading = mix(leftDown, leftUp, y); + float rightShading = mix(rightDown, rightUp, y); + float shading = mix(leftShading, rightShading, x); + + // Combine with per-instance opacity (opacity only darkens RGB, alpha preserved) + float shadingAmount = shading * vInstanceOpacity; + + // For reflections, apply a gradient fade using reflection uniforms (darker further away) if (vIsReflection > 0.5) { - // vTexCoord.y goes from 1 (top of reflection, near cover) to 0 (bottom, far from cover) - // We want it brightest near the cover and fading away + // Legacy reflection fade: ramp from 0.0 to 0.33 using texture V coordinate float gradientFade = mix(0.0, 0.33, vTexCoord.y); shadingAmount *= gradientFade; } + // Final color: shaded RGB, keep source alpha fragColor = vec4(texColor.rgb * shadingAmount, texColor.a); } diff --git a/common/rhi/shaders/flow.frag.qsb b/common/rhi/shaders/flow.frag.qsb index f50613bb..02f9d8a6 100644 Binary files a/common/rhi/shaders/flow.frag.qsb and b/common/rhi/shaders/flow.frag.qsb differ diff --git a/common/rhi/shaders/flow.vert b/common/rhi/shaders/flow.vert index 1584e783..1d9d18c4 100644 --- a/common/rhi/shaders/flow.vert +++ b/common/rhi/shaders/flow.vert @@ -12,11 +12,15 @@ layout(location = 5) in vec4 instanceModel_row3; layout(location = 6) in vec4 instanceShading1; layout(location = 7) in float instanceOpacity; layout(location = 8) in float instanceFlip; +layout(location = 9) in float instanceRotation; // Outputs to fragment shader layout(location = 0) out vec2 vTexCoord; -layout(location = 1) out vec4 vColor; -layout(location = 2) out float vIsReflection; +layout(location = 1) out flat vec4 vInstanceShading; +layout(location = 2) out flat float vInstanceOpacity; +layout(location = 3) out flat float vIsReflection; +layout(location = 4) out flat float vInstanceRotation; +layout(location = 5) out vec2 vLocalPos; // Uniform buffer layout(std140, binding = 0) uniform UniformBuffer @@ -39,21 +43,16 @@ void main() gl_Position = viewProjectionMatrix * instanceModel * vec4(position, 1.0); vTexCoord = texCoord; + vLocalPos = position.xy; // Flip texture vertically per-instance when requested (reflection) if (instanceFlip != 0.0) { - vTexCoord.y = 1.0 - texCoord.y; + vTexCoord.y = 1.0 - vTexCoord.y; } - float leftUpShading = instanceShading1.x; - float leftDownShading = instanceShading1.y; - float rightUpShading = instanceShading1.z; - float rightDownShading = instanceShading1.w; - - float leftShading = mix(leftDownShading, leftUpShading, (position.y + 0.5)); - float rightShading = mix(rightDownShading, rightUpShading, (position.y + 0.5)); - float shading = mix(leftShading, rightShading, (position.x + 0.5)); - - vColor = vec4(shading * instanceOpacity); + // Pass per-instance values to fragment shader as flat (no interpolation) + vInstanceShading = instanceShading1; + vInstanceOpacity = instanceOpacity; vIsReflection = instanceFlip; + vInstanceRotation = instanceRotation; } diff --git a/common/rhi/shaders/flow.vert.qsb b/common/rhi/shaders/flow.vert.qsb index 11aa806e..7f28484f 100644 Binary files a/common/rhi/shaders/flow.vert.qsb and b/common/rhi/shaders/flow.vert.qsb differ diff --git a/common/rhi/yacreader_flow_rhi.cpp b/common/rhi/yacreader_flow_rhi.cpp index e325f1c2..d3b7b715 100644 --- a/common/rhi/yacreader_flow_rhi.cpp +++ b/common/rhi/yacreader_flow_rhi.cpp @@ -194,9 +194,9 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb) // Create instance buffer for per-draw instance data if (!scene.instanceBuffer) { - // Allocate buffer for per-instance data (model matrix + shading + opacity + flipFlag) - // mat4 (16 floats) + vec4 (4 floats) + float (1 float) + float (1 float) = 22 floats - scene.instanceBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, 22 * sizeof(float))); + // Allocate buffer for per-instance data (model matrix + shading + opacity + flipFlag + rotation) + // mat4 (16 floats) + vec4 (4 floats) + float (opacity) + float (flipFlag) + float (rotation) = 23 floats + scene.instanceBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, 23 * sizeof(float))); scene.instanceBuffer->create(); } @@ -333,7 +333,7 @@ void YACReaderFlow3D::ensurePipeline() QRhiVertexInputLayout inputLayout; inputLayout.setBindings({ { 5 * sizeof(float) }, // Per-vertex data (position + texCoord) - { 22 * sizeof(float), QRhiVertexInputBinding::PerInstance } // Per-instance data + { 23 * sizeof(float), QRhiVertexInputBinding::PerInstance } // Per-instance data }); inputLayout.setAttributes({ // Per-vertex attributes @@ -347,7 +347,8 @@ void YACReaderFlow3D::ensurePipeline() { 1, 5, QRhiVertexInputAttribute::Float4, 12 * sizeof(float) }, // row 3 { 1, 6, QRhiVertexInputAttribute::Float4, 16 * sizeof(float) }, // shading vec4 { 1, 7, QRhiVertexInputAttribute::Float, 20 * sizeof(float) }, // opacity - { 1, 8, QRhiVertexInputAttribute::Float, 21 * sizeof(float) } // flipFlag (1.0 = reflection) + { 1, 8, QRhiVertexInputAttribute::Float, 21 * sizeof(float) }, // flipFlag (1.0 = reflection) + { 1, 9, QRhiVertexInputAttribute::Float, 22 * sizeof(float) } // rotation }); scene.pipeline->setVertexInputLayout(inputLayout); @@ -408,7 +409,7 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb) bool isReflection; bool isMark; QRhiTexture *texture; - float instanceData[22]; + float instanceData[23]; UniformData uniformData; }; @@ -482,7 +483,7 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb) } // Ensure instance buffer is large enough for all draws - auto requiredInstanceSize = static_cast(draws.size() * 22 * sizeof(float)); + auto requiredInstanceSize = static_cast(draws.size() * 23 * sizeof(float)); if (!scene.instanceBuffer || scene.instanceBuffer->size() < requiredInstanceSize) { scene.instanceBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, requiredInstanceSize)); if (!scene.instanceBuffer->create()) { @@ -514,8 +515,8 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb) // Update instance buffer with all instance data for (int i = 0; i < draws.size(); ++i) { - int offset = i * 22 * sizeof(float); - batch->updateDynamicBuffer(scene.instanceBuffer.get(), offset, 22 * sizeof(float), draws[i].instanceData); + int offset = i * 23 * sizeof(float); + batch->updateDynamicBuffer(scene.instanceBuffer.get(), offset, 23 * sizeof(float), draws[i].instanceData); } // === PHASE 2: RENDER (DURING PASS) === @@ -601,6 +602,9 @@ void YACReaderFlow3D::prepareDrawData(const YACReader3DImageRHI &image, bool isR outInstanceData[i] = matData[i]; } + // Store per-instance rotation in the instance data (new slot at index 22) + outInstanceData[22] = image.current.rot; + // Prepare uniform data outUniformData.viewProjectionMatrix = viewProjectionMatrix; outUniformData.backgroundColor = QVector3D(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF()); @@ -637,7 +641,7 @@ void YACReaderFlow3D::executeDrawWithOffset(QRhiCommandBuffer *cb, QRhiTexture * // Bind vertex buffers with offset into instance buffer const QRhiCommandBuffer::VertexInput vbufBindings[] = { { scene.vertexBuffer.get(), 0 }, - { scene.instanceBuffer.get(), quint32(uniformSlot * 22 * sizeof(float)) } + { scene.instanceBuffer.get(), quint32(uniformSlot * 23 * sizeof(float)) } }; cb->setVertexInput(0, 2, vbufBindings);