使用GLSL可以实现Photoshop“运动模糊”吗?

问题描述 投票:1回答:2

我正在使用制作带有精灵的2D游戏并尝试获得此效果:

enter image description here

我在示例中使用了Photoshop的“运动模糊”。如您所见,效果是方向性的。

我的游戏使用纸质精灵,因此将其作为后期效果更容易,而不是模糊每个精灵上的每个设备组合。

是否可以使用着色器获得此效果?一个例子将不胜感激。

glsl shader sfml blur
2个回答
1
投票

在这里,我发布了一个晚上我能做的最好的结果。你问的时候我用着色器做了。我没有实现角度,但不是太难。

这是cpp文件的一部分:

...
if (!shader.loadFromFile("dMotionBlur_v05.frag", sf::Shader::Fragment)){
...

window.clear(sf::Color(120,120,120));

// Passing parameters to shader.
shader.setUniform("dir", dir); //direction of blur
shader.setUniform("nSamplesF", (float)std::atoi(argv[3])); // number of samples
shader.setUniform("radius", (float)std::atof(argv[4]) ); //radius of blur

window.draw(sprite, &shader);
window.display();
...

这是片段着色器:

uniform sampler2D u_texture;
uniform float nSamplesF;
uniform float radius;
uniform vec2 dir;

void main(){
    vec2 tc = gl_TexCoord[0].xy;
    float blur = radius;
    float hstep = dir.x;
    float vstep = dir.y;
    float total = 0.0;
    int nSamplesI = int(nSamplesF);

    vec4 sum = vec4(0.0);

    for (int i=1; i<=nSamplesI; i++){
        float floatI = float(i);
        float counter = nSamplesF-floatI+1.0;

        float p = floatI/nSamplesF;
        float tO = (p * 0.1783783784) + 0.0162162162;
        total += tO;

        sum += texture2D(u_texture, vec2(tc.x - counter*blur*hstep, tc.y - counter*blur*vstep)) * tO;
    }

    sum += texture2D(u_texture, vec2(tc.x, tc.y)) * 0.2270270270;

    for (int i=nSamplesI; i>=1; i--){
        float floatI = float(i);
        float counter = nSamplesF-floatI+1.0;

        float p = floatI/nSamplesF;
        float tO = (p * 0.1783783784) + 0.0162162162;
        total += tO;

        sum += texture2D(u_texture, vec2(tc.x + counter*blur*hstep, tc.y + counter*blur*vstep)) * tO;
    }

    gl_FragColor =  gl_Color * (sum/total);
}

我将entire code上传到我的存储库,以便您可以下载并尝试。您可以设置x / Y方向,样本和模糊半径。 X / Y方向是0-1。您可以使用样本数量。模糊半径非常敏感,您可以尝试使用0.01

我的例子是:

$ ./sfml-app [x(0-1)] [Y(0-1] [number of sample] [ radius blur]

一些照片:


1
投票

Generating points over a line

一点点在手动方面,但我可以建议一种方法。 让我们想象一个圆,在一个确定的角度上由一条线交叉。在这一行上,我们放置了一些随机点。

像这样的东西:

enter image description here 半径为100,角度为45º(PI / 4弧度)和100个随机点的圆

然后,无论每个点都在哪里,我们将绘制一个纹理的精灵,带有一些alpha(透明度)。结果将如下所示:

enter image description here

更改圆的半径和点数,结果可能会有所不同


Further explanation

在我的例子中,我使用了一个代表BlurredSprite的类。它将持有这些点,并且sf::Texture可以绘制。

class BlurredSprite : public sf::Drawable, sf::Transformable {
    public:
        BlurredSprite(const sf::Texture &t, float radius, float angle, unsigned int points = 30) :
            m_texture(t)
        {
            auto getPointOverLine = [=]{
                auto angleOverX = angle - std::_Pi*0.5;  // Angle 0 is a vertical line, so I rotate it 45 degrees clockwise (substract)
                if (rand() % 2){
                    angleOverX += std::_Pi;   // Half of the points will be on the "first quadrant", the other half over the "third", if you consider (0,0) the center of the circle
                }
                auto l = radius * ((rand() * 1.f) / RAND_MAX);
                auto x = cos(angleOverX) * (l);
                auto y = sin(angleOverX) * (l);

                return sf::Vector2f(x, y);
            };

            while (m_points.size() < points){
                m_points.push_back(getPointOverLine());
            }

        }
    private:
        std::vector<sf::Vector2f> m_points;
        sf::Texture m_texture;
};

使用getPointOverLine lambda,我在该行上创建了一个随机点,但可以使用其他选项来执行此操作。事实上,你传播这些点的方式将影响最终结果。

我需要覆盖draw方法,以便在窗口上创建此类Drawable。我也覆盖了setPosition方法(来自sf::Transformable)因为,如果你移动它,你应该用它移动所有的点。

public:
    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const{
        auto it = m_points.begin();
        while (it != m_points.end()){    
            sf::Sprite sp(m_texture);
            auto col = sp.getColor();
            auto rect = sp.getTextureRect();
            auto orig = sp.getOrigin() + sf::Vector2f(rect.width, rect.height)*0.5f;
            col.a = 25;

            sp.setColor(col);
            sp.setOrigin(orig);
            sp.setPosition(*it);
            target.draw(sp);

            it++;
        }
    }

    virtual void setPosition(sf::Vector2f pos){
        sf::Transformable::setPosition(pos);
        for (int i = 0; i < m_points.size(); ++i){
            m_points[i] = pos + m_points[i];
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.