0
\$\begingroup\$

I have a problem with SFML. This code does not work:

uniform sampler2D texture;

varying vec2 v_texCoords;

float offset = 1.0/32;

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

  if (col.a == 1)
    gl_FragColor = col;
  else {
    float au = texture2D(texture, vec2(v_texCoords.x , v_texCoords.y -offset )).a;
    float ad = texture2D(texture, vec2(v_texCoords.x , v_texCoords.y + offset)).a;
    float al = texture2D(texture, vec2(v_texCoords.x -offset, v_texCoords.y)).a;
    float ar = texture2D(texture, vec2(v_texCoords.x + offset, v_texCoords.y)).a;

    if (au.a != 1 || ad.a != 1 || al.a != 1 || ar.a != 1)
      gl_FragColor = vec4(0.0, 0.0, 0.0, 1);
    else
      gl_FragColor = col;
  }
}

How can I make my outline shader work in SFML?

\$\endgroup\$
  • \$\begingroup\$ "It does not work" does not help us help you. What are you trying to do, what do you get instead, and what have you tried to fix the issue? \$\endgroup\$ – Vaillancourt May 2 '17 at 13:46
  • \$\begingroup\$ I want the function to retrieve alfe of adjacent pixels and if one of the adjacent pixels has alfe equal to 1 and the pixel on which we are now has alfe = 0 it has to be changed to black full P.s. sorry for my bad English \$\endgroup\$ – Terrasil May 2 '17 at 13:53
  • \$\begingroup\$ ...and what do you get instead? \$\endgroup\$ – Vaillancourt May 2 '17 at 14:10
  • 2
    \$\begingroup\$ Perhaps uploading a screenshot of what you get and another of what you'd like to get instead would help us help you :) \$\endgroup\$ – Vaillancourt May 2 '17 at 14:16
  • 2
    \$\begingroup\$ Please use the 'code sample' tool from the edit toolbar to format your code. And while you're at it, you could add to the question the other elements you mentioned in the comments :) (including adding the images). \$\endgroup\$ – Vaillancourt May 2 '17 at 14:39
1
\$\begingroup\$

I've tried to make the shader you asked about work; here is a version that I managed to make work:

#version 120
uniform sampler2D texture;

float offset = 1.0/32;


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

  if (col.a == 1)
    gl_FragColor = col;
  else {
    float au = texture2D(texture, vec2(v_texCoords.x , v_texCoords.y - offset)).a;
    float ad = texture2D(texture, vec2(v_texCoords.x , v_texCoords.y + offset)).a;
    float al = texture2D(texture, vec2(v_texCoords.x - offset, v_texCoords.y)).a;
    float ar = texture2D(texture, vec2(v_texCoords.x + offset, v_texCoords.y)).a;

    if (au == 1.0 || ad == 1.0 || al == 1.0 || ar == 1.0)
      gl_FragColor = vec4(0.0, 0.0, 0.0, 1);
    else
      gl_FragColor = col;
  }
}

Now, I don't think that's exclusively the issue at hand, so I'll try to help you with some other aspects of debugging, or at least, how I came up with this code.

How to find assets.

There are several ways to make sure you find the assets of your project.

  • The most simple way is to hardcode the path to your assets folder and use it as a base path. That's not clean, but it gets the job done when developing.
  • You can move the assets folder in your Debug/Release folder. That's not clean, but it gets the job done when developing.
  • In the project properties, you can change the Debugging > Working directory to a folder that you know your files will be in.
  • In the project properties, you can change the Linker > General > Output File to specify a location in your assets folder.
  • You can pass the path of assets folder as a command-line argument and use argc/argv in the int main() method, with setting the project property Debugging > Command Arguments to the appropriate path.

Being able to access your assets is the first step, and really, you should not try to debug your shader or add new features before this has been taken out of the way.

How to debug your shader code.

We'll start off by assuming that you have a program that can load your graphics and both of your shaders (the vertex and the fragment) (there is a complete example at the end of this answer). And since you did not provide a vertex shader, let's assume that you used the one provided as a minimal vertex shader on SFML's website.

void main()
{
    // transform the vertex position
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

    // transform the texture coordinates
    gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;

    // forward the vertex color
    gl_FrontColor = gl_Color;
}

Looking at the console when executing the program, we get these errors:

Failed to compile fragment shader:
WARNING: 0:2: '' :  #version directive missing
WARNING: 0:5: 'assign' : implicit type conversion allowed from GLSL 1.20
ERROR: 0:10: 'assign' : l-value required "v_texCoords" (cannot modify a varying)
ERROR: 0:11: 'vec4' : syntax error syntax error

Error: Could not load shaders

To fix the warnings, we'll add #version 120 at the top of the shader files. To fix the second warning, we'll remove the varying vec2 v_texCoords; that is in the fragment shader and use the 'built-in' gl_TexCoord in the code instead: vec2 v_texCoords = gl_TexCoord[0].xy;.

Trying to run the program will give us some new errors:

Failed to compile fragment shader:
ERROR: 0:22: 'a' : field selection requires structure, vector, or matrix on left hand side
ERROR: 0:22: 'a' : field selection requires structure, vector, or matrix on left hand side
ERROR: 0:22: 'a' : field selection requires structure, vector, or matrix on left hand side
ERROR: 0:22: 'a' : field selection requires structure, vector, or matrix on left hand side

Here it's because we're using a float like it were a vec4. Notepad++ is not as nice as Visual Studio to edit code :P (no intelli-sense). Changing if (au.a != 1 || ad.a != 1 || al.a != 1 || ar.a != 1) to if (au != 1 || ad != 1 || al != 1 || ar != 1), we get more errors, but not as messages. Now the alpha part is all black. What's wrong with the code?

The condition that we have means that any neighbour texel that is transparent will have 'this' texel coloured black. That's not right; let's change if (au != 1 || ad != 1 || al != 1 || ar != 1) to if (au == 1.0 || ad == 1.0 || al == 1.0 || ar == 1.0). Build, run, and we get what we expect!

The last small step is to make our shader work with any size of texture by allowing a broader variety of texture size: instead of hardcoding the size of the sprite in the shader, let's use an external parameter: replace float offset = 1.0/32; with uniform float textureOffset;, and all the variables in the code.


The end result to have a border around the sprite (a stroke, an outline), can look like this:

The main.cpp:

#include <SFML/Graphics.hpp>
#include <iostream>
#include <string>

int
main( int argc, char** argv )
{

  std::string assetsFolderPath = "C:\\Users\\alex\\Desktop\\test140614\\assets\\";
  std::string backgroundPath = assetsFolderPath + "maxresdefault.jpg";
  std::string gameSpritePath = assetsFolderPath + "latest.png";
  std::string shaderVert = assetsFolderPath + "main.vert";
  std::string shaderFrag = assetsFolderPath + "main.frag";

  sf::RenderWindow window( sf::VideoMode( 1024, 622 ), "Game", sf::Style::Default );

  sf::Texture background;
  sf::Sprite backgroundImage;

  if ( !background.loadFromFile( backgroundPath ) )
    std::cout << "Error: Could not load background image" << std::endl;

  backgroundImage.setTexture( background );

  sf::Texture gameSprite;
  sf::Sprite gameSpriteImage;
  if ( !gameSprite.loadFromFile( gameSpritePath ) )
    std::cout << "Can't find the image" << std::endl;
  gameSpriteImage.setTexture( gameSprite );
  gameSpriteImage.setPosition( 50.0f, 500.0f );

  sf::Shader shader;
  if ( !shader.loadFromFile( shaderVert, shaderFrag ) )
    std::cout << "Error: Could not load shaders" << std::endl;

  shader.setUniform( "textureOffset", 1.0f / static_cast<float>( gameSprite.getSize().x ) );
  shader.setUniform( "texture", sf::Shader::CurrentTexture );

  int xOffset = 0;
  bool isXoffsetGoingUp = true;
  while ( window.isOpen() )
  {

    sf::Event Event;
    while ( window.pollEvent( Event ) )
    {
      switch ( Event.type )
      {
      case sf::Event::Closed:
        window.close();
        break;
      }
    }

    if ( isXoffsetGoingUp )
    {
      xOffset++;
      if ( xOffset > 500 )
        isXoffsetGoingUp = false;
    }
    else
    {
      xOffset--;
      if ( xOffset <= 0 )
        isXoffsetGoingUp = true;
    }

    gameSpriteImage.setPosition( xOffset, 500.0f );


    window.clear();
    window.draw( backgroundImage );
    window.draw( gameSpriteImage, &shader );
    window.display();
    sf::sleep( sf::microseconds(1000000/60) );
  }


  return 0;
}

main.vert: this is the exact same as the one posted above.

main.frag:

#version 120
uniform sampler2D texture;

uniform float textureOffset;

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

  if (col.a == 1)
    gl_FragColor = col;
  else {
    float au = texture2D(texture, vec2(v_texCoords.x , v_texCoords.y - textureOffset)).a;
    float ad = texture2D(texture, vec2(v_texCoords.x , v_texCoords.y + textureOffset)).a;
    float al = texture2D(texture, vec2(v_texCoords.x - textureOffset, v_texCoords.y)).a;
    float ar = texture2D(texture, vec2(v_texCoords.x + textureOffset, v_texCoords.y)).a;

    if (au == 1.0 || ad == 1.0 || al == 1.0 || ar == 1.0)
      gl_FragColor = vec4(0.0, 0.0, 0.0, 1);
    else
      gl_FragColor = col;
  }
}
\$\endgroup\$
  • \$\begingroup\$ As a side note, I think you could completely drop the "default" vertex shader and only use that fragment shader, SFML will supply the vertex shader for you. \$\endgroup\$ – Vaillancourt Jun 14 '17 at 12:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.