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

Author Topic: [SOLVED] Incorrect vertex data in shader with certain drawables  (Read 2367 times)

0 Members and 1 Guest are viewing this topic.

fallahn

  • Sr. Member
  • ****
  • Posts: 464
  • Buns.
    • View Profile
    • Trederia
Apologies for the vague title, it's a little difficult to summarise in a few words. The problem is this: as far as I can tell drawables with more than a certain number for vertices have incorrect vertex data sent to the vertex shader. I noticed when trying to apply lighting to a scene that drawables such as sf::CircleShape or vertex arrays with a lot of vertices are not lit correctly in relation to the light source. sf::Sprites and quads created with a vertex array are lit fine, however. In this video the light position is set to the cursor:

http://www.youtube.com/watch?v=1ZLMhby3yEw

The only difference between the first and second half of the videos is how the drawables are constructed. Notice in the second half the (rather dark) drawable on the right is only lit when the light source is in the top left corner, as if the drawable was sat at 0, 0. The world transform relative to the light source is not accounted for. Here is a minimal example which draws a sprite and a circle shape. The effect is more noticable: when the mouse cursor is over the sprite a small red dot is drawn - but the mouse has to be in the top left corner for the dot to appear over the circle shape. Apologies for the length of the shader, I gut it best I could.

#define SFML_NO_DEPRECATED_WARNINGS
#include <SFML/Graphics.hpp>

static const std::string vertex =
"#version 120\n"

"uniform vec3 u_pointLightPosition;\n"

"varying vec3 v_eyeDirection;\n"
"varying vec3 v_pointLightDirection;\n"

"const vec3 cameraWorldPosition = vec3(400.0, 300.0, 1780.0);\n"

"void main()\n"
"{\n" \
"    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
"    gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n"
"    gl_FrontColor = gl_Color;\n"

"    vec3 viewVertex = vec3(gl_ModelViewMatrix * gl_Vertex);\n"
"    v_pointLightDirection = vec3(gl_ModelViewMatrix * vec4(u_pointLightPosition, 1.0)) - viewVertex;\n"
"    v_eyeDirection = ((gl_ModelViewMatrix * vec4(cameraWorldPosition, 1.0)).xyz - viewVertex);\n"
"}";

static const std::string fragment =
"#version 120\n"

"varying vec3 v_eyeDirection;\n"
"varying vec3 v_pointLightDirection;\n"

"vec4 diffuseColour;\n"

"vec3 calcLighting(vec3 normal, vec3 lightDirection, vec3 lightDiffuse, vec3 lightSpec, float falloff)\n"
"{\n"
"    float diffuseAmount = max(dot(normal, lightDirection), 0.0);\n"
"    diffuseAmount = pow((diffuseAmount * 0.5) + 0.5, 2.0);\n"
"    vec3 mixedColour = lightDiffuse * diffuseColour.rgb * diffuseAmount * falloff;\n"
"    return mixedColour;\n"
"}\n"

"void main()\n"
"{\n"
"    diffuseColour = gl_Color;\n"
"    vec3 normalVector = vec3(0.0, 0.0, 1.0);\n"
"    vec3 blendedColour = diffuseColour.rgb * vec3(0.2, 0.2, 0.2);\n"

"    vec3 pointLightDir = v_pointLightDirection * 0.001;\n"
"    float falloff = clamp(1.0 - sqrt(dot(pointLightDir, pointLightDir)), 0.0, 1.0);\n"
"    blendedColour += calcLighting(normalVector, normalize(v_pointLightDirection), vec3(1.0, 0.0, 0.0), vec3(1.0), falloff);\n"

"    gl_FragColor.rgb = blendedColour;\n"
"    gl_FragColor.a = diffuseColour.a;\n"
"}";

int main()
{
    sf::RenderWindow rw;
    rw.create({ 800, 600 }, "Buns");

    sf::Texture texture;
    texture.loadFromFile("whiteSquare.png");
    sf::Sprite sprite(texture);
    sprite.setPosition(100.f, 500.f);

    sf::CircleShape circle(50.f);
    circle.setTexture(&texture);
    circle.setPosition(600.f, 500.f);

    sf::Shader shader;
    shader.loadFromMemory(vertex, fragment);

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

        auto pos = rw.mapPixelToCoords(sf::Mouse::getPosition(rw));
        shader.setParameter("u_pointLightPosition", sf::Vector3f(pos.x, pos.y, 10.f));

        rw.clear(sf::Color::Blue);
        rw.draw(sprite, &shader);
        rw.draw(circle, &shader);
        rw.display();
    }

    return 0;
}
 

« Last Edit: April 08, 2016, 11:31:29 am by fallahn »

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1404
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: Incorrect vertex data in shader with certain drawables
« Reply #1 on: April 08, 2016, 02:29:09 am »
This is not a bug, this is a feature.™

You are mistakenly multiplying your light's position by the drawable's modelview matrix, thereby always transforming the light's position into the drawable's coordinate system. If your light is supposed to have its own modelview transformation, you will have to pass that in as a separate uniform, you can't make use of attribute data for this.

v_pointLightDirection = vec3(gl_ModelViewMatrix * vec4(u_pointLightPosition, 1.0)) - viewVertex;

// Should be

v_pointLightDirection = u_pointLightPosition - viewVertex;

The reason why your bug isn't made a bit more obvious is because of the way SFML tries to "optimize" drawables that consist of 4 vertices or less. For these kinds of drawables, the RenderTarget goes ahead and pre-transforms the vertex positions by the modelview matrix so that all quad/sprite/etc. vertex data is essentially always in world space when arriving in the vertex shader. Because the modelview matrix is left as the identity matrix when these "optimized" primitives are drawn, you don't notice the erroneous transformation you perform on your light's position in the vertex shader.

I've already been critical of this "optimization" often times in the past, since it doesn't really behave like an optimization for some people (including myself) and yet seem to have some kind of minor effect for others.

For reference, this is where the "optimization" code lives.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

fallahn

  • Sr. Member
  • ****
  • Posts: 464
  • Buns.
    • View Profile
    • Trederia
Re: Incorrect vertex data in shader with certain drawables
« Reply #2 on: April 08, 2016, 11:31:12 am »
Ah, OK, thanks for clearing that up. I had it in my head that the light should be in the drawable's space if I wanted it to be relative to it - it makes much more sense now (and, of course, probably would be more apparent if I were using modern shaders ;) )

 

anything