Glsl 产生辉光效果-alpha值存在问题

Glsl 产生辉光效果-alpha值存在问题,glsl,sfml,blur,Glsl,Sfml,Blur,我想为我的游戏创建一个发光效果。为了保持这种极简主义,让我们假设我想要发光的图像。:) 从这个开始: 要得到这样的东西: 这是一条三步走的路 保存场景中的所有亮像素(=发光) 对这些像素应用模糊效果(=模糊) 在桌面上绘制原始图片和模糊纹理(=组装) 步骤1和3没有问题。模糊部分只是不想正常工作 在我进一步解释之前,以下是我的发光结果: (阈值=0.67f) 现在当我模糊这一点时,我得到了一些不幸的结果: 此黑色边缘来自以下事实:任何透明颜色都是黑色vec4(0.0,0.0,0.0,0.0

我想为我的游戏创建一个发光效果。为了保持这种极简主义,让我们假设我想要发光的图像。:)

从这个开始:

要得到这样的东西:

这是一条三步走的路

  • 保存场景中的所有亮像素(=发光)
  • 对这些像素应用模糊效果(=模糊)
  • 在桌面上绘制原始图片和模糊纹理(=组装)
步骤1和3没有问题。模糊部分只是不想正常工作

在我进一步解释之前,以下是我的发光结果: (阈值=0.67f)

现在当我模糊这一点时,我得到了一些不幸的结果: 此黑色边缘来自以下事实:任何透明颜色都是黑色
vec4(0.0,0.0,0.0,0.0)
。在SFML/GLSL中,这不是一个未知的问题,建议使用SFML的
sf::BlendMode
,并将片段着色器中最终像素颜色的
.rgb
值与其alpha值相乘。所以我做了,现在这是我的结果:

它更好,但绝对不好。模糊着色器现在还可以渲染出发光遮罩周围的像素。组装后,它只是一张模糊的图片:

。。我试图通过检查像素的alpha值是否为零来在着色器文件中“修复”此问题。这样的话,我就不看重他们了。但是由于sf::BlendMode被激活,我现在不知道alpha的行为如何——所以我停用了BlendMode,但仍然有奇怪的结果。(在这个问题的关键时刻,我提供了代码和这次尝试的结果)


我没有尝试修复这项工作。我真的需要一些帮助。也许我在着色器中做了一些根本错误的事情。。这是完整的代码- 如果要编译它,请使用2个片段着色器和(在1280x720中)创建一个文件夹资源

荧光粉

boxblur.frag

导致:


[我正在使用Visual Studio 2017社区]-感谢您的帮助

试着做一个小例子,你只想平均两个像素。左(L)和右(R)。然后,左像素由R(L)、G(L)、B(L)、A(L)组成,右像素由R(R)、G(R)、B(R)和A(R)组成

如果没有alpha,蓝色的平均值将为:

(B(L)+B(R)) / 2
考虑到alpha,它变成:

(B(L)*A(L)+B(R)*A(R)) / (A(L)+A(R))
我们可以直接看到,在完全实心像素(alpha=1)的情况下,我们得到与上面完全相同的公式:

(B(L)*1+B(R)*1) / (1+1)  =  (B(L)+B(R)) / 2
此外,假设右像素是完全透明的,左像素是实心的,那么右像素的颜色分量不会影响任何东西,结果与左像素完全相同,这正是我们想要的:

(B(L)*1+B(R)*0) / (1+0)  =  (B(L)) / 1  = B(L)
两个完全透明的像素都会退化,必须以优雅的方式处理


现在,您所要做的就是将其扩展到两个像素之外。:-)

我也在en.sfml-dev.org()上发布了这个问题,并向我展示了正确的方法。 在解决此问题之前,以下是图片结果:

发光(阈值=0.24f):

模糊(4层):

组装:

耶!解决方案是将所有透明像素设置为纯黑色
vec4(0.0,0.0,0.0,1.0)
,然后在模糊后,将它们添加到场景中:

vec4 tex_color = texture2D(texture, gl_TexCoord[0].xy);
vec4 add_color = texture2D(add_texture, gl_TexCoord[0].xy);
gl_FragColor = tex_color + add_color;
这样,如果
add_color
是黑色(“透明”)的,我们添加
tex_color+vec4(0.0,0.0,0.0,1.0)
,结果不变

这很好,因为现在您可以完全忽略alpha通道

为了理解为什么我觉得这很好,你可以在这里读到这篇小文章(请随意跳过):

不用担心alpha,你可以忽略任何
sf::BlendMode
,就像让我头疼了整整两天的令人困惑的
sf::BlendMode::oneminssrcalpha
。当您知道它们都是预乘的时候,尝试计算任何合理的“真”alpha值。当然,您还必须将所有rgb值与像素的alpha相乘,以反转乘法…公式从这里快速升级。还要从alpha中减去1,因为它是
oneminssrcalpha
。。。不要忘了检查所有字母的和(是的,你需要求和)是0的情况(或者在
oneminssrcalpha
中,其他一些情况),因为否则你会被0除掉(或者在
oneminssrcalpha
中,当所有周围像素都是实数时,会被0除掉)。有时,奇怪的alpha值可能会起作用,但只适用于一次模糊,但在我的例子中,我有多次模糊。。等

以下是最终代码:

发光.frag

boxblur.frag

组装.frag

main.cpp

#包括
#包括
#包括
无效运行(){
常数sf::矢量2F大小(1280720);
sf::纹理背景_tex;
background_tex.loadFromFile(“resources/background.jpg”);
sf::精灵背景(背景_-tex);
sf::着色器发光\u着色器;
learnation_shader.loadFromFile(“resources/learnation.frag”,sf::shader::Fragment);
发光_shader.setUniform(“纹理”,sf::shader::CurrentTexture);
发光着色器。设置均匀(“阈值”,0.24f);
sf::着色器模糊\着色器;
blur_shader.loadFromFile(“resources/boxblur.frag”,sf::shader::Fragment);
blur_shader.setUniform(“纹理”,sf::shader::CurrentTexture);
blur_shader.setUniform(“纹理逆”,1.0f/SIZE.x);
sf::着色器组装_着色器;
汇编_shader.loadFromFile(“resources/assemble.frag”,sf::shader::Fragment);
汇编_shader.setUniform(“纹理”,sf::shader::CurrentTexture);
sf::Shader multiply_Shader;
multiply_shader.loadFromFile(“resources/multiply.frag”,sf::shader::Fragment);
multiply_shader.setUniform(“纹理”,sf::shader::CurrentTexture);
sf::RenderStates着色器状态;
//没有blendmode!我们自己制作-Assembly.frag
sf::ContextSettings contex
(B(L)*A(L)+B(R)*A(R)) / (A(L)+A(R))
(B(L)*1+B(R)*1) / (1+1)  =  (B(L)+B(R)) / 2
(B(L)*1+B(R)*0) / (1+0)  =  (B(L)) / 1  = B(L)
vec4 tex_color = texture2D(texture, gl_TexCoord[0].xy);
vec4 add_color = texture2D(add_texture, gl_TexCoord[0].xy);
gl_FragColor = tex_color + add_color;
#version 120

uniform sampler2D texture;
uniform float threshold;

void main(void){
    vec3 current_color = texture2D(texture, gl_TexCoord[0].xy).rgb;
    vec4 pixel =  vec4(0.0, 0.0, 0.0, 1.0);
    float brightness = dot(current_color.rgb, vec3(0.2126, 0.7152, 0.0722));
    if (brightness >= threshold){
        pixel = texture2D(texture, gl_TexCoord[0].xy);
    }
    gl_FragColor = pixel;
}
#version 120

uniform sampler2D texture;
uniform float texture_inverse;
uniform int blur_radius;
uniform vec2 blur_direction;

void main(void){
    vec4 sum = texture2D(texture, gl_TexCoord[0].xy);

    for (int i = 0; i < blur_radius; ++i){
        sum += texture2D(texture, gl_TexCoord[0].xy + (i * texture_inverse) * blur_direction);
        sum += texture2D(texture, gl_TexCoord[0].xy - (i * texture_inverse) * blur_direction);
    }
    gl_FragColor = sum / (blur_radius * 2 + 1);
}
#version 120

uniform sampler2D texture;
uniform float multiply;

void main(void){
    gl_FragColor = texture2D(texture, gl_TexCoord[0].xy) * multiply;
}
#version 120

uniform sampler2D texture;
uniform sampler2D add_texture;
uniform float add_weight;

void main(void){
    vec4 tex_color = texture2D(texture, gl_TexCoord[0].xy);
    vec4 add_color = texture2D(add_texture, gl_TexCoord[0].xy) * add_weight;
    gl_FragColor = tex_color + add_color;
}
#include <SFML/Graphics.hpp>
#include <iostream>
#include <array>

void run() {
    const sf::Vector2f SIZE(1280, 720);

    sf::Texture background_tex;
    background_tex.loadFromFile("resources/background.jpg");
    sf::Sprite background(background_tex);

    sf::Shader luminescence_shader;
    luminescence_shader.loadFromFile("resources/luminescence.frag", sf::Shader::Fragment);
    luminescence_shader.setUniform("texture", sf::Shader::CurrentTexture);
    luminescence_shader.setUniform("threshold", 0.24f);

    sf::Shader blur_shader;
    blur_shader.loadFromFile("resources/boxblur.frag", sf::Shader::Fragment);
    blur_shader.setUniform("texture", sf::Shader::CurrentTexture);
    blur_shader.setUniform("texture_inverse", 1.0f / SIZE.x);

    sf::Shader assemble_shader;
    assemble_shader.loadFromFile("resources/assemble.frag", sf::Shader::Fragment);
    assemble_shader.setUniform("texture", sf::Shader::CurrentTexture);

    sf::Shader multiply_shader;
    multiply_shader.loadFromFile("resources/multiply.frag", sf::Shader::Fragment);
    multiply_shader.setUniform("texture", sf::Shader::CurrentTexture);


    sf::RenderStates shader_states;
    //no blendmode! we make our own - assemble.frag

    sf::ContextSettings context_settings;
    context_settings.antialiasingLevel = 12;

    //draws background
    sf::RenderTexture scene_render;
    scene_render.create(SIZE.x, SIZE.y, context_settings);

    sf::RenderTexture luminescence_render;
    luminescence_render.create(SIZE.x, SIZE.y, context_settings);

    //draws luminescence and blur
    sf::RenderTexture assemble_render;
    assemble_render.create(SIZE.x, SIZE.y, context_settings);



    //addding multiple boxblurs with different radii looks really nice! in this case 4 layers
    std::array<sf::RenderTexture, 4> blur_renders;
    for (int i = 0; i < blur_renders.size(); ++i) {
        blur_renders[i].create(SIZE.x, SIZE.y, context_settings);
    }
    const int BLUR_RADIUS_VALUES[] = { 250, 180, 125, 55 };
    float blur_weight = blur_renders.empty() ? 0.0 : 1.0 / blur_renders.size();

    sf::RenderWindow window(sf::VideoMode(SIZE.x, SIZE.y), "glsl fun", sf::Style::Default, context_settings);

    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) {
                window.close();
            }
        }

        //first draw the scene
        scene_render.clear();
        scene_render.draw(background);
        scene_render.display();


        //apply luminescence
        shader_states.shader = &luminescence_shader;
        luminescence_render.clear();
        luminescence_render.draw(sf::Sprite(scene_render.getTexture()), shader_states);
        luminescence_render.display();

        //apply two pass gaussian blur n times to simulate gaussian blur.
        shader_states.shader = &blur_shader;
        for (int i = 0; i < blur_renders.size(); ++i) {
            blur_shader.setUniform("blur_radius", BLUR_RADIUS_VALUES[i]);

            blur_renders[i].clear();
            blur_renders[i].draw(sf::Sprite(luminescence_render.getTexture()));
            blur_renders[i].display();

            //vertical blur
            blur_shader.setUniform("blur_direction", sf::Glsl::Vec2(1.0, 0.0));
            blur_renders[i].draw(sf::Sprite(blur_renders[i].getTexture()), shader_states);
            blur_renders[i].display();

            //horizontal blur
            blur_shader.setUniform("blur_direction", sf::Glsl::Vec2(0.0, 1.0));
            blur_renders[i].draw(sf::Sprite(blur_renders[i].getTexture()), shader_states);
            blur_renders[i].display();
        }

        //load blur_renders[0] into assemble_render so we can add the other blurs ontop of it
        shader_states.shader = &multiply_shader;
        multiply_shader.setUniform("multiply", blur_weight);
        assemble_render.clear();
        assemble_render.draw(sf::Sprite(blur_renders[0].getTexture()), shader_states);
        assemble_render.display();

        //adding the rest ontop creating a final blur
        shader_states.shader = &assemble_shader;
        assemble_shader.setUniform("add_weight", blur_weight);
        for (int i = 1; i < blur_renders.size(); ++i) {
            assemble_shader.setUniform("add_texture", blur_renders[i].getTexture());
            assemble_render.draw(sf::Sprite(assemble_render.getTexture()), shader_states);
            assemble_render.display();
        }

        //final result; scene + blur
        assemble_shader.setUniform("add_weight", 1.0f);
        assemble_shader.setUniform("add_texture", assemble_render.getTexture());
        assemble_render.draw(sf::Sprite(scene_render.getTexture()), shader_states);
        assemble_render.display();

        window.clear();
        window.draw(sf::Sprite(assemble_render.getTexture()));
        window.display();
    }
}

int main() {
    try {
        run();
    }
    catch (std::exception e) {
        std::cerr << "caught exception - - - " << e.what() << '\n';
        return 1;
    }
    return 0;
}