blur: add noise in perceptual space
The previous implementation added noise in linear space, which resulted in the effect becoming more pronounced on black backgrounds. This patch changes the process to be applied in perceptual space, by making the noise addition pass a separate draw call and disabling GL_FRAMEBUFFER_SRGB during that. After this change, noise will look much more suppressed and almost never grainy. This change also changes the range of the noise from [-strength..strength) to [0..strength), as blending can only be either additive or subtractive. As a result, users might need to ramp up their noise parameter after this change. v2: Add more explanation around the draw call. v3: Fix noise not fading out with the fade out effect. v4: Restore an accidentally removed comment. v5: Add CCBUG. v6: Rebase. v7: Fix a formatting issue. CCBUG: 409620
This commit is contained in:
parent
0a2c511489
commit
c193efc962
3 changed files with 45 additions and 35 deletions
|
@ -623,7 +623,7 @@ void BlurEffect::generateNoiseTexture()
|
|||
uint8_t *noiseImageLine = (uint8_t *) noiseImage.scanLine(y);
|
||||
|
||||
for (int x = 0; x < noiseImage.width(); x++) {
|
||||
noiseImageLine[x] = qrand() % m_noiseStrength + (128 - m_noiseStrength / 2);
|
||||
noiseImageLine[x] = qrand() % m_noiseStrength;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -715,34 +715,58 @@ void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float o
|
|||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
if (m_noiseStrength > 0) {
|
||||
// Apply an additive noise onto the blurred image.
|
||||
// The noise is useful to mask banding artifacts, which often happens due to the smooth color transitions in the
|
||||
// blurred image.
|
||||
// The noise is applied in perceptual space (i.e. after glDisable(GL_FRAMEBUFFER_SRGB)). This practice is also
|
||||
// seen in other application of noise synthesis (films, image codecs), and makes the noise less visible overall
|
||||
// (reduces graininess).
|
||||
glEnable(GL_BLEND);
|
||||
if (opacity < 1.0) {
|
||||
// We need to modulate the opacity of the noise as well; otherwise a thin layer would appear when applying
|
||||
// effects like fade out.
|
||||
// glBlendColor should have been set above.
|
||||
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE);
|
||||
} else {
|
||||
// Add the shader's output directly to the pixels in framebuffer.
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
}
|
||||
applyNoise(vbo, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6, screenProjection, windowRect.topLeft());
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
vbo->unbindArrays();
|
||||
}
|
||||
|
||||
void BlurEffect::upscaleRenderToScreen(GLVertexBuffer *vbo, int vboStart, int blurRectCount, QMatrix4x4 screenProjection, QPoint windowPosition)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
m_renderTextures[1].bind();
|
||||
|
||||
if (m_noiseStrength > 0) {
|
||||
m_shader->bind(BlurShader::NoiseSampleType);
|
||||
m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale());
|
||||
m_shader->setNoiseTextureSize(m_noiseTexture->size() * GLRenderTarget::virtualScreenScale());
|
||||
m_shader->setTexturePosition(windowPosition * GLRenderTarget::virtualScreenScale());
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
m_noiseTexture->bind();
|
||||
} else {
|
||||
m_shader->bind(BlurShader::UpSampleType);
|
||||
m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale());
|
||||
}
|
||||
m_shader->bind(BlurShader::UpSampleType);
|
||||
m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale());
|
||||
|
||||
m_shader->setOffset(m_offset);
|
||||
m_shader->setModelViewProjectionMatrix(screenProjection);
|
||||
|
||||
//Render to the screen
|
||||
vbo->draw(GL_TRIANGLES, vboStart, blurRectCount);
|
||||
m_shader->unbind();
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
void BlurEffect::applyNoise(GLVertexBuffer *vbo, int vboStart, int blurRectCount, QMatrix4x4 screenProjection, QPoint windowPosition)
|
||||
{
|
||||
m_shader->bind(BlurShader::NoiseSampleType);
|
||||
m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale());
|
||||
m_shader->setNoiseTextureSize(m_noiseTexture->size() * GLRenderTarget::virtualScreenScale());
|
||||
m_shader->setTexturePosition(windowPosition * GLRenderTarget::virtualScreenScale());
|
||||
|
||||
m_noiseTexture->bind();
|
||||
|
||||
m_shader->setOffset(m_offset);
|
||||
m_shader->setModelViewProjectionMatrix(screenProjection);
|
||||
|
||||
vbo->draw(GL_TRIANGLES, vboStart, blurRectCount);
|
||||
m_shader->unbind();
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ private:
|
|||
void generateNoiseTexture();
|
||||
|
||||
void upscaleRenderToScreen(GLVertexBuffer *vbo, int vboStart, int blurRectCount, QMatrix4x4 screenProjection, QPoint windowPosition);
|
||||
void applyNoise(GLVertexBuffer *vbo, int vboStart, int blurRectCount, QMatrix4x4 screenProjection, QPoint windowPosition);
|
||||
void downSampleTexture(GLVertexBuffer *vbo, int blurRectCount);
|
||||
void upSampleTexture(GLVertexBuffer *vbo, int blurRectCount);
|
||||
void copyScreenSampleTexture(GLVertexBuffer *vbo, int blurRectCount, QRegion blurShape, QMatrix4x4 screenProjection);
|
||||
|
|
|
@ -133,31 +133,19 @@ BlurShader::BlurShader(QObject *parent)
|
|||
|
||||
streamFragCopy.flush();
|
||||
|
||||
// Fragment shader - Noise texture
|
||||
// Fragment shader - Noise tiling
|
||||
QTextStream streamFragNoise(&fragmentNoiseSource);
|
||||
|
||||
streamFragNoise << glHeaderString << glUniformString;
|
||||
|
||||
streamFragNoise << "uniform sampler2D noiseTexUnit;\n";
|
||||
streamFragNoise << "uniform vec2 noiseTextureSize;\n";
|
||||
streamFragNoise << "uniform vec2 texStartPos;\n";
|
||||
|
||||
// Upsampling + Noise
|
||||
streamFragNoise << "void main(void)\n";
|
||||
streamFragNoise << "{\n";
|
||||
streamFragNoise << " vec2 uv = vec2(gl_FragCoord.xy / renderTextureSize);\n";
|
||||
streamFragNoise << " vec2 uvNoise = vec2((texStartPos.xy + gl_FragCoord.xy) / noiseTextureSize);\n";
|
||||
streamFragNoise << " \n";
|
||||
streamFragNoise << " vec4 sum = " << texture2D << "(texUnit, uv + vec2(-halfpixel.x * 2.0, 0.0) * offset);\n";
|
||||
streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, halfpixel.y) * offset) * 2.0;\n";
|
||||
streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, halfpixel.y * 2.0) * offset);\n";
|
||||
streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, halfpixel.y) * offset) * 2.0;\n";
|
||||
streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x * 2.0, 0.0) * offset);\n";
|
||||
streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, -halfpixel.y) * offset) * 2.0;\n";
|
||||
streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, -halfpixel.y * 2.0) * offset);\n";
|
||||
streamFragNoise << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, -halfpixel.y) * offset) * 2.0;\n";
|
||||
streamFragNoise << " \n";
|
||||
streamFragNoise << " " << fragColor << " = sum / 12.0 - (vec4(0.5, 0.5, 0.5, 0) - vec4(" << texture2D << "(noiseTexUnit, uvNoise).rrr, 0));\n";
|
||||
streamFragNoise << " " << fragColor << " = vec4(" << texture2D << "(texUnit, uvNoise).rrr, 0);\n";
|
||||
streamFragNoise << "}\n";
|
||||
|
||||
streamFragNoise.flush();
|
||||
|
@ -168,9 +156,9 @@ BlurShader::BlurShader(QObject *parent)
|
|||
m_shaderNoisesample.reset(ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentNoiseSource));
|
||||
|
||||
m_valid = m_shaderDownsample->isValid() &&
|
||||
m_shaderUpsample->isValid() &&
|
||||
m_shaderCopysample->isValid() &&
|
||||
m_shaderNoisesample->isValid();
|
||||
m_shaderUpsample->isValid() &&
|
||||
m_shaderCopysample->isValid() &&
|
||||
m_shaderNoisesample->isValid();
|
||||
|
||||
if (m_valid) {
|
||||
m_mvpMatrixLocationDownsample = m_shaderDownsample->uniformLocation("modelViewProjectionMatrix");
|
||||
|
@ -227,9 +215,6 @@ BlurShader::BlurShader(QObject *parent)
|
|||
m_shaderNoisesample->setUniform(m_texStartPosLocationNoisesample, QVector2D(1.0, 1.0));
|
||||
m_shaderNoisesample->setUniform(m_halfpixelLocationNoisesample, QVector2D(1.0, 1.0));
|
||||
|
||||
glUniform1i(m_shaderNoisesample->uniformLocation("texUnit"), 0);
|
||||
glUniform1i(m_shaderNoisesample->uniformLocation("noiseTexUnit"), 1);
|
||||
|
||||
ShaderManager::instance()->popShader();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue