Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: [SOLVED] Dissolve Sprite Shader Not Working When Using Sprite Sheet  (Read 5685 times)

0 Members and 1 Guest are viewing this topic.

DylanCaruso99

  • Newbie
  • *
  • Posts: 5
    • View Profile
I have a shader that I am trying to use to create a dissolve effect. When I use the shader to draw a sprite which is 64x64 from a texture that is 64x64, it works seemingly perfectly. However, when I use the shader to draw a sprite which is 64x64 from a larger sprite sheet texture, it bugs out and the dissolve effect becomes stretched.

Here is what it looks like. 64x64 texture is on the left, 512x64 texture with 64x64 texture rect is on the right.



Here is my example code:
#include <SFML/Graphics.hpp>


int main()
{
        sf::Clock mClock;
        mClock.restart();
        bool increase = true;
        sf::Time dissolveTime = sf::Time::Zero;

        sf::RenderWindow window(sf::VideoMode(800, 600), "My window");

        sf::Shader dissolveShader;
        dissolveShader.loadFromFile("Dissolve.frag", sf::Shader::Type::Fragment);

        sf::Texture dissolveNoiseTexture;
        dissolveNoiseTexture.loadFromFile("DissolveNoise.png");

        dissolveShader.setUniform("noiseTexture", dissolveNoiseTexture);

        sf::Texture normalTexture;
        normalTexture.loadFromFile("Normal.png");
        sf::Sprite normalSprite(normalTexture);
        normalSprite.setOrigin(32.f, 32.f);
        normalSprite.move(-20.f, 0.f);

        sf::Texture spriteSheetTexture;
        spriteSheetTexture.loadFromFile("SpriteSheet.png");
        sf::Sprite spriteSheetSprite(spriteSheetTexture);
        spriteSheetSprite.setTextureRect(sf::IntRect(0, 0, 64, 64));
        spriteSheetSprite.setOrigin(32.f, 32.f);
        spriteSheetSprite.move(20.f, 0.f);

        sf::View view = window.getDefaultView();
        view.zoom(0.1f);
        view.setCenter(0.f, 0.f);
        window.setView(view);

        while (window.isOpen())
        {
                float dissolveAmount;

                if (increase)
                {
                        dissolveTime += mClock.getElapsedTime();
                        dissolveAmount = dissolveTime.asSeconds() / 3.f; //3 Seconds to dissolve up

                        if (dissolveAmount >= 1.f)
                        {
                                increase = false;
                        }
                }
                else
                {
                        dissolveTime -= mClock.getElapsedTime();
                        dissolveAmount = dissolveTime.asSeconds() / 3.f; //3 Seconds to dissolve down

                        if (dissolveAmount <= 0.f)
                        {
                                increase = true;
                        }
                }

                dissolveShader.setUniform("dissolveAmount", dissolveAmount);
                mClock.restart();

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

                window.clear(sf::Color(128, 128, 128));
                window.draw(normalSprite, &dissolveShader);
                window.draw(spriteSheetSprite, &dissolveShader);
                window.display();
        }

        return 0;
}

Here is the shader called Dissolve.frag that I am using:

uniform sampler2D source;
uniform sampler2D noiseTexture;
uniform float dissolveAmount;


void main()
{
        vec2 textureCoordinates = gl_TexCoord[0].xy;
       
        float edgeThickness = 0.1;
        vec4 edgeColor = vec4(255, 0, 0, 100);
        float noiseTiling = 0.8;
       
        vec4 originalTexture = vec4(0.0);
        originalTexture += texture2D(source, textureCoordinates);
        vec4 dissolveNoise = vec4(0.0);
        dissolveNoise += texture2D(noiseTexture, textureCoordinates * noiseTiling);
        float remappedDissolve = dissolveAmount * (1.01 + edgeThickness) - edgeThickness;
        vec4 step1 = step(remappedDissolve, dissolveNoise);
        vec4 step2 = step(remappedDissolve + edgeThickness, dissolveNoise);
        vec4 edge = step1 - step2;
        edge.a = originalTexture.a;
        vec4 edgeColorArea = edge * edgeColor;
        originalTexture.a *= step1.r;
        vec4 combinedColor = mix(originalTexture, edgeColorArea, edge.r);
        gl_FragColor = combinedColor;
}

Here is links to the 64x64 image, 512x64 image, and the noise texture respectively:
https://i.imgur.com/H2fPEQd.png
https://i.imgur.com/ygFTifT.png
https://i.imgur.com/hn9kRoK.png

How can I make it so that the shader works consistently on sprites regardless of their texture sizes? Shouldn't the size of the texture be irrelevant since it's not being rendered at all due to having a texture rect? I am new to GLSL so I don't understand how this works. I'm trying to get this shader to work by reverse engineering it from a Godot Tutorial.



« Last Edit: October 27, 2021, 03:48:57 am by DylanCaruso99 »

DylanCaruso99

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: Dissolve Sprite Shader Not Working When Using Sprite Sheet
« Reply #1 on: October 27, 2021, 03:38:03 am »
I managed to solve it. The problem was the scale between the two textures. One was 1x1, and the other was 8x1. After a lot of trial and error I found that I can solve this using textureSize in the shader. Here is the new dissolve shader:

uniform sampler2D source;
uniform sampler2D noiseTexture;
uniform float dissolveAmount;


void main()
{
        vec2 textureCoordinates = gl_TexCoord[0].xy;
       
        float edgeThickness = 0.1;
        vec4 edgeColor = vec4(255, 0, 0, 100);
        float noiseTiling = 0.8;
       
        vec2 textureScale = textureSize2D(source, 0) / 64.0;
       
        vec4 originalTexture = vec4(0.0);
        originalTexture += texture2D(source, textureCoordinates);
        vec4 dissolveNoise = vec4(0.0);
        dissolveNoise += texture2D(noiseTexture, textureCoordinates * textureScale * noiseTiling);
        float remappedDissolve = dissolveAmount * (1.01 + edgeThickness) - edgeThickness;
        vec4 step1 = step(remappedDissolve, dissolveNoise);
        vec4 step2 = step(remappedDissolve + edgeThickness, dissolveNoise);
        vec4 edge = step1 - step2;
        edge.a = originalTexture.a;
        vec4 edgeColorArea = edge * edgeColor;
        originalTexture.a *= step1.r;
        vec4 combinedColor = mix(originalTexture, edgeColorArea, edge.r);
        gl_FragColor = combinedColor;
}

DylanCaruso99

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: Dissolve Sprite Shader Not Working When Using Sprite Sheet
« Reply #2 on: October 27, 2021, 03:44:06 am »
Although this does technically solve this specific issue though, if I were to draw a drawable with several different sprites all with different sprite sheet sizes I would still have to somehow pass in that size for each drawable. I don't know if there is a way to just solve it based on treating all sprites the same regardless of texture rect.

fallahn

  • Sr. Member
  • ****
  • Posts: 459
  • Buns.
    • View Profile
    • Trederia
Re: [SOLVED] Dissolve Sprite Shader Not Working When Using Sprite Sheet
« Reply #3 on: October 27, 2021, 12:10:51 pm »
Your best bet is to add 2 uniforms for texture size and texture rect size, and set them each time you draw a sprite. This is probably also applicable if you try to build on mac (and possibly linux) as they require a core version of OpenGL and a declaration of #version 120 at the top of your shader. (Windows drivers often supply a 'compatibility' context which is why your current solution works for you) textureSize() requires #version 130 or greater, and would therefore become unavailable.

Quote
If you want to use the graphics module on OS X, you are limited to using a legacy context which implies OpenGL version 2.1.
https://www.sfml-dev.org/tutorials/2.5/window-opengl.php

https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/textureSize.xhtml

#version 120

uniform sampler2D source;
uniform sampler2D noiseTexture;
uniform float dissolveAmount;
uniform vec2 u_textureSize;
uniform vec2 u_rectSize;

void main()
{
        vec2 textureCoordinates = gl_TexCoord[0].xy;
       
        float edgeThickness = 0.1;
        vec4 edgeColor = vec4(255, 0, 0, 100);
        float noiseTiling = 0.8;
       
        vec2 textureScale = u_textureSize / u_rectSize;

        ...
}

 

DylanCaruso99

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: [SOLVED] Dissolve Sprite Shader Not Working When Using Sprite Sheet
« Reply #4 on: October 28, 2021, 06:43:12 am »
I see. I will keep that in mind.