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

Author Topic: Question About GL States  (Read 3899 times)

0 Members and 2 Guests are viewing this topic.

Josh

  • Newbie
  • *
  • Posts: 19
    • View Profile
Question About GL States
« on: January 01, 2013, 04:00:24 pm »
I'm using an sf::VertexArray and a multisample shader to draw the tile map in my game. This works fine, unless I try to draw something before it. When I do so, it messes with the texture coordinates in the multisampler. I found this page, which mentions differing coordinates. Sure, enough, when I manually reset the GL_TEXTURE matrix before the shader, it works perfectly. However, I don't do any custom openGL rendering in my program, so why does SFML not handle the states properly itself? I'm happy to manually reset the texture matrix, but was just curious as to why I have to.

Aditionally I can call push/popGLState around each draw call and it works as well, however, calling resetGLStates does not work.

I also tried the fix listed here, saying I should use sf::Shader::CurrentTexture, but that also did not work.

Also, should mention, I'm using one of the latest versions of SFML (Checking commits since my copy and they're all API updates, or irrelevant).

Anyway, here is a minimal app that demonstrates the issue (full app including shader attached):
#include <SFML/Graphics.hpp>

//Only included/linked to manually set GL_TEXTURE matrix to identity
#include <GL/glew.h>
#pragma comment(lib, "opengl32.lib")

int main() {

        //Render window
    sf::RenderWindow  window( sf::VideoMode( 800, 600, 32 ), "GL States Issue" );

        //Multisampler textures
        sf::Texture prime1Tex;
        sf::Texture prime2Tex;
        sf::Texture prime3Tex;
        prime1Tex.loadFromFile("prime1.png");
        prime2Tex.loadFromFile("prime2.png");
        prime3Tex.loadFromFile("prime3.png");

        //Mutlisample shader - Samples from 3 repeating prime textures to significantly reduce tiling patterns
        sf::Shader multisamplerShader;
        multisamplerShader.loadFromFile("multisampler.sfx", sf::Shader::Fragment);
        sf::Vector2f size;
        multisamplerShader.setParameter("prime1Tex", prime1Tex);//sf::Shader::CurrentTexture);
        size.x = 1.0f / prime1Tex.getSize().x;
        size.y = 1.0f / prime1Tex.getSize().y;
        multisamplerShader.setParameter("prime1Size", size);
        multisamplerShader.setParameter("prime2Tex", prime2Tex);
        size.x = 1.0f / prime2Tex.getSize().x;
        size.y = 1.0f / prime2Tex.getSize().y;
        multisamplerShader.setParameter("prime2Size", size);
        multisamplerShader.setParameter("prime3Tex", prime3Tex);
        size.x = 1.0f / prime3Tex.getSize().x;
        size.y = 1.0f / prime3Tex.getSize().y;
        multisamplerShader.setParameter("prime3Size", size);

        //Random extra sprite to draw
    sf::Sprite proj;
        sf::Texture projTex;
        projTex.loadFromFile("randomsprite.png");
        proj.setTexture(projTex);

        //Vertex array storing tile data.
        sf::VertexArray tileDetails;
        tileDetails.setPrimitiveType(sf::Triangles);
        tileDetails.append(sf::Vertex(sf::Vector2f(20.0f, 20.0f), sf::Vector2f(20.0f,20.0f)));
        tileDetails.append(sf::Vertex(sf::Vector2f(20.0f, 500.0f), sf::Vector2f(20.0f,500.0f)));
        tileDetails.append(sf::Vertex(sf::Vector2f(500.0f, 500.0f), sf::Vector2f(500.0f,500.0f)));

        bool run = true;
        while(run)
        {
                window.clear(sf::Color::Cyan);

                //Removing all these push/popGLState calls causes the multisample shader to mess up
                //window.pushGLStates();
                window.draw(proj);
                //window.popGLStates();

                //Just using this works
                glMatrixMode(GL_TEXTURE);
                glLoadIdentity();
               
                //Just using this does not work
                //window.resetGLStates();

                //window.pushGLStates();
                //Even trying the fix of setting the first texture in the shader to sf::Shader::CurrentTexture, and
                //then passing it in through sf::RenderStates does not work. It changes how the shader messes up however
                sf::RenderStates states;
                //states.texture = &prime1Tex;
                states.shader = &multisamplerShader;
                window.draw(tileDetails, states);
                //window.popGLStates();
               
                window.display();
               
                sf::Event Event;
               
                while (window.pollEvent(Event))
                {
                        if ((Event.type == sf::Event::KeyPressed && (Event.key.code == sf::Keyboard::Escape)))  {run = false; }
                }
        }
}
 

[attachment deleted by admin]
« Last Edit: January 01, 2013, 04:09:19 pm by Josh »

cire

  • Full Member
  • ***
  • Posts: 138
    • View Profile
Re: Question About GL States
« Reply #1 on: January 01, 2013, 11:26:20 pm »
Try this:

#include <SFML/Graphics.hpp>

int main() {

        //Render window
    sf::RenderWindow  window( sf::VideoMode( 800, 600, 32 ), "GL States Issue" );

        //Multisampler textures
        sf::Texture prime1Tex;
        sf::Texture prime2Tex;
        sf::Texture prime3Tex;
        prime1Tex.loadFromFile("prime1.png");
        prime2Tex.loadFromFile("prime2.png");
        prime3Tex.loadFromFile("prime3.png");

    prime1Tex.setRepeated(true) ;
    prime2Tex.setRepeated(true) ;
    prime3Tex.setRepeated(true) ;

    prime1Tex.setSmooth(true) ;
    prime2Tex.setSmooth(true) ;
    prime3Tex.setSmooth(true) ;

        //Mutlisample shader - Samples from 3 repeating prime textures to significantly reduce tiling patterns
        sf::Shader multisamplerShader;
        multisamplerShader.loadFromFile("multisampler.sfx", sf::Shader::Fragment);

    multisamplerShader.setParameter("prime1Tex", prime1Tex);//sf::Shader::CurrentTexture);
    multisamplerShader.setParameter("prime2Tex", prime2Tex);
    multisamplerShader.setParameter("prime3Tex", prime3Tex);

    multisamplerShader.setParameter("screenSize", sf::Vector2f(800,600)) ;

        //Random extra sprite to draw
    sf::Sprite proj;
        sf::Texture projTex;
        projTex.loadFromFile("randomsprite.png");
        proj.setTexture(projTex);

        //Vertex array storing tile data.
        sf::VertexArray tileDetails;
        tileDetails.setPrimitiveType(sf::Triangles);
        tileDetails.append(sf::Vertex(sf::Vector2f(20.0f, 20.0f), sf::Vector2f(20.0f,20.0f)));
        tileDetails.append(sf::Vertex(sf::Vector2f(20.0f, 500.0f), sf::Vector2f(20.0f,500.0f)));
        tileDetails.append(sf::Vertex(sf::Vector2f(500.0f, 500.0f), sf::Vector2f(500.0f,500.0f)));

        bool run = true;
        while(run)
        {
                window.clear(sf::Color::Cyan);

        window.draw(proj);
                window.draw(tileDetails, &multisamplerShader);
       
                window.display();
               
                sf::Event Event ;      
                while (window.pollEvent(Event))
                {
                        if ((Event.type == sf::Event::KeyPressed && (Event.key.code == sf::Keyboard::Escape)))  {run = false; }
                }
        }
}

With the shader changed to:
uniform sampler2D prime1Tex;
uniform sampler2D prime2Tex;
uniform sampler2D prime3Tex;

uniform vec2 screenSize ;

void main()
{
        // normalized screen coordinates.
        vec2 pixelPos = gl_FragCoord.xy / screenSize ;
               
        //Get the pixel data from each texture
        vec4 prime1Fragment = texture2D(prime1Tex, pixelPos);
        vec4 prime2Fragment = texture2D(prime2Tex, pixelPos);
        vec4 prime3Fragment = texture2D(prime3Tex, pixelPos);
       
        //Combine them together
        vec4 finalFragment = prime1Fragment * 0.33 + prime2Fragment * 0.33 + prime3Fragment * 0.33;    
        finalFragment.a = 1.0;
               
        gl_FragColor = finalFragment;
}

The texture coordinates in gl_TextCoord in a fragment shader are normalized.   I think the "correct" way to do this would probably involve a vertex shader, but the results looked reasonable to me.

Josh

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: Question About GL States
« Reply #2 on: January 02, 2013, 02:47:51 am »
 I tried using your suggestion, however, that ignores the texture coordinates passed through in the vertex array. Additionally, it stretches all three of the textures so they're as big as the screen, which defeats the point of the multisampler. (The multisampler essentially infinitely tiles 3 prime sized textures over the game world, then the tiles act as a clipping mask. The reason for using prime sized textures is so that their lowest common denominator is the product of their dimensions, and thus won't get repetition for many, many pixels. Stretching the textures to be the screen size obviously destroys this behaviour)

However, I tried your suggestion of using the vertex shader, which worked.

This is the shader I used:
void main(){
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    gl_FrontColor = gl_Color;
}

So, basically just had to stop it multiplying the texcoord by gl_TextureMatrix[0].

Cheers


However, still kind of curious why drawing an extra object, using only SFML, requires me to mess around with resetting the texture coordinates. (i.e my original program, if I don't draw proj, then I don't need to reset the texture matrix). I was under the impression that one of the design goals for SFML was that if a user only used SFML (no custom opengl), then it would take care of all that lower level stuff for them, but clearly it's not taking care of something, since not drawing the proj sprite lets it work perfectly.
« Last Edit: January 02, 2013, 03:03:54 am by Josh »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Question About GL States
« Reply #3 on: January 02, 2013, 08:00:54 am »
You're right, it should work out of the box as long as you don't call OpenGL functions yourself.

Could you please provide a complete and minimal code that reproduces the problem? The code that you provide is small but:
- it's not minimal (we don't need it to do the same thing as your initial code, you can remove all these textures and keep only what's needed to trigger the bug)
- it's not complete (your shader is missing)
Laurent Gomila - SFML developer

Josh

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: Question About GL States
« Reply #4 on: January 02, 2013, 08:20:27 am »
No worries. Hope this is minimal enough:

#include <SFML/Graphics.hpp>
#include <GL/glew.h>
#pragma comment(lib, "opengl32.lib")

int main() {

        //Render window
    sf::RenderWindow  window( sf::VideoMode( 800, 600, 32 ), "GL States Issue" );

        //Texture to draw with shader
        sf::Texture shaderTex;
        shaderTex.loadFromFile("prime1.png");

        //Multisample shader
        sf::Shader multisamplerShader;
        multisamplerShader.loadFromFile("multisampler.sfx", sf::Shader::Fragment);
        //multisamplerShader.loadFromFile("vertexshader.sfx", "multisampler.sfx"); //Fixing the coordinates via the vertex shader

        sf::Vector2f size;
        multisamplerShader.setParameter("shaderTex", shaderTex);
        size.x = 1.0f / shaderTex.getSize().x;
        size.y = 1.0f / shaderTex.getSize().y;
        multisamplerShader.setParameter("shaderTexSize", size);

        //Random extra sprite to draw
    sf::Sprite proj;
        sf::Texture projTex;
        projTex.loadFromFile("randomsprite.png");
        proj.setTexture(projTex);

        //Vertex array that will be used with the shader
        sf::VertexArray shaderArray;
        shaderArray.setPrimitiveType(sf::Triangles);
        shaderArray.append(sf::Vertex(sf::Vector2f(0.0f, 0.0f), sf::Vector2f(0.0f,0.0f)));
        shaderArray.append(sf::Vertex(sf::Vector2f(0.0f, 500.0f), sf::Vector2f(0.0f,500.0f)));
        shaderArray.append(sf::Vertex(sf::Vector2f(500.0f, 500.0f), sf::Vector2f(500.0f,500.0f)));
        bool run = true;

        while(run)
        {
                window.clear(sf::Color::Cyan);

                window.draw(proj); //Drawing this messes things up

                window.draw(shaderArray, &multisamplerShader);
               
                window.display();
               
                sf::Event Event;
               
                while (window.pollEvent(Event))
                {
                        if ((Event.type == sf::Event::KeyPressed && (Event.key.code == sf::Keyboard::Escape)))  {run = false; }
                }
        }
}
 

My fragment shader:
//The textures to combine for the tile
uniform sampler2D shaderTex;
//Inverse size of each texture
uniform vec2 shaderTexSize;

void main()
{
        //Position of the current fragment
        vec2 truePos = gl_TexCoord[0].xy;
       
        //Grab the decimal part of the texture coordinate
        vec2 pos = vec2((truePos * shaderTexSize) - vec2(ivec2(truePos * shaderTexSize)));
               
        //Get the pixel data from texture
        vec4 fragment = texture2D(shaderTex, pos);
       
        gl_FragColor = vec4(fragment.rgb, 1.0);
}
 

And I'll include the vertex shader that I now use, for the sake of completeness:
void main(){
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    gl_FrontColor = gl_Color;
}

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Question About GL States
« Reply #5 on: January 02, 2013, 10:55:07 am »
I've found the problem. It's not exactly a bug in SFML, but it's definitely something that users shouldn't care about.

In fact there are two mistakes in your code.

1) Since the texture is a parameter of the shader, instead of being passed to the draw function, it is not bound to texture unit 0. Therefore, you mustn't use gl_TexCoord[0] but gl_TexCoord[1]. I know, you were not supposed to know that.

2) I've also managed to make it work the other way, by passing the texture to the draw call and associating sf::Shader::CurrentTexture to the shader parameter "shaderTex". The probleme in this case is your shader: the line which computes "pos" messes up the result, removing it solves the problem.
Laurent Gomila - SFML developer

Josh

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: Question About GL States
« Reply #6 on: January 02, 2013, 11:35:59 am »
1) Since the texture is a parameter of the shader, instead of being passed to the draw function, it is not bound to texture unit 0. Therefore, you mustn't use gl_TexCoord[0] but gl_TexCoord[1]. I know, you were not supposed to know that.

2) I've also managed to make it work the other way, by passing the texture to the draw call and associating sf::Shader::CurrentTexture to the shader parameter "shaderTex". The probleme in this case is your shader: the line which computes "pos" messes up the result, removing it solves the problem.

So, the first fix is just changing
vec2 truePos = gl_TexCoord[0].xy;
to
vec2 truePos = gl_TexCoord[1].xy;
Correct? If so, that doesn't work for me. The texture coordinates I'm passing in through the vertex array are being stored in gl_TexCoord[0] (If I don't draw the sprite and change the texture coordinates in the array, it definitely distorts the texture correctly). Whereas it seems to be just getting zeroes or something from gl_TexCoord[1].

Fix number 2 seems to work, as long as I only use one texture (and set the texture to repeat). As soon as I put it back to the three texture shader, it messes up, as I can't have 3 textures as the currentTexture. gl_TexCoord[1] and [2] both seem to be zeroed. Using gl_TexCoord[0] for all 3 textures draws the other two textures as the same dimensions as the first texture.

The reason I do the pos calculation is so that I can store a world position in the texture coordinate in each vertex. This world position and the texture dimensions are then used to grab "clip regions" of each three tiles as if they had been infinitely tiled across the game world. (Essentially a modulo calculation).

So unless there is some way to for me to store data in gl_TexCoord[1] and [2], using the same vertexarray, then it would appear neither of your fixes solve my original problem, only the reduced minimal code.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Question About GL States
« Reply #7 on: January 02, 2013, 11:55:53 am »
Quote
The texture coordinates I'm passing in through the vertex array are being stored in gl_TexCoord[0]
Ah... you're right, sorry, I overlooked that.

In fact, multiple texture units are used if multiple textures are bound to the shader, but only texture coordinates 0 are defined. So... this is actually a big mess ;D

You should use your own vertex shader until I fix this. However it's weird to pass a non-normalized gl_TexCoord[0] to the fragment shader, you should rather use a separate variable (a varying) if the values are not normalized texture coordinates.
Laurent Gomila - SFML developer

 

anything