调整大小时移动了灯光着色器 window

Light shader moved while resizing window

我一直在努力制作一个小的光着色器。 它工作得很好,我的意思是,光线会像预期的那样消失,它是我的角色周围的一个圆圈,随着它移动。 只有当调整大小事件不存在时,它才可能是完美的。

当 SFML 调整 window 的大小时,它会放大所有内容,但方式很奇怪。它扩大了一切 除了着色器 。 我尝试调整我的大小 window(我喜欢调整像素图游戏的大小,我觉得它最漂亮。所以我不想阻止调整大小事件)。

这是我的着色器:

    uniform vec3 light;

void main(void) {
    float distance = sqrt(pow(gl_FragCoord.x - light.x, 2) + pow(gl_FragCoord.y - light.y, 2));
    float alpha = 1.;

    if (distance <= light.z) {
        alpha = (1.0 / light.z) * distance;
    }
    gl_FragColor = vec4(0., 0., 0., alpha);

}

所以,问题是,我的 window 显示为 1280 x 736(以适应 32x32 纹理),而我有一个 1920 x 1080 显示器。当我将 window 放大以适合 1920 x 1080(包括标题栏)时,整个内容正确调整大小,一切正常,但着色器现在为 1920x1080(减去标题栏)。所以着色器需要不同的坐标(对于着色器来说,x = 32,y = 0 应该是 x = 48 y = 0)。

所以我想知道,是否可以将着色器与整个 window 一起放大?我应该使用事件或类似的东西吗?

感谢您的回答^^

编辑:这是一些照片: 所以这是调整大小之前的光着色器(它在播放器上到处都是黑暗的,就像它应该的那样)。 然后我调整 window 的大小,播放器没有移动,纹理适合整个 window,但光线移动了。

所以,为了正确解释,当我调整 window 的大小时,我希望一切都适合 window,所以它充满了纹理,但是当我这样做时,给我的坐标着色器是调整大小之前的着色器,如果我移动它,就好像我没有调整 window 大小一样移动,所以我的播放器再也不会亮了。

我不确定是否更清楚,但我尽力了。

EDIT2:这是我调用着色器的代码:

void Graphics::UpdateLight() {
    short radius = 65; // 265 on the pictures

    int x = m_game->GetPlayer()->GetSprite()->getPosition().x + CASE_LEN / 2; // Setting on the middle of the player sprite (CASE_LEN is a const which contains the size of a case (here 32))
    int y = HEIGHT - (m_game->GetPlayer()->GetSprite()->getPosition().y + CASE_LEN / 2); // (the "HEIGHT -" part was set because it seems that y = 0 is on the bottom of the texture for GLSL)

    sf::Vector3f shaderLight;
    shaderLight.x = x;
    shaderLight.y = y;
    shaderLight.z = radius;

    m_lightShader.setParameter("light", shaderLight);
}

您显示的代码片段实际上只更新了着色器坐标(快速浏览一下,它看起来不错)。该错误很可能发生在您实际绘制东西的地方。


我会使用完全不同的方法,因为一旦渲染多个事物、其他光源等,您的着色器方法可能会变得相当乏味。

因此,我建议您将光照贴图渲染为渲染纹理(本质上类似于 "black = no light, color = light of that color")。

我没有尝试用文字解释所有内容,而是编写了一个快速注释示例程序,它将在屏幕上绘制 window 并将一些光源移动到背景图像上(我使用了一个SFML 的着色器示例附带的):

除了启动路径中有一个名为 "background.jpg" 的文件外,没有其他要求。

请随意复制此代码或使用它来获取灵感。请记住,这没有经过优化,实际上只是一个快速编辑以显示总体思路。

#include <SFML/Graphics.hpp>
#include <vector>
#include <cmath>

const float PI = 3.1415f;

struct Light
{
    sf::Vector2f position;
    sf::Color color;
    float radius;
};

int main()
{
    // Let's setup a window
    sf::RenderWindow window(sf::VideoMode(640, 480), "SFML Lights");
    window.setVerticalSyncEnabled(false);
    window.setFramerateLimit(60);

    // Create something simple to draw
    sf::Texture texture;
    texture.loadFromFile("background.jpg");
    sf::Sprite background(texture);

    // Setup everything for the lightmap
    sf::RenderTexture lightmapTex;
    // We're using a 512x512 render texture for max. compatibility
    // On modern hardware it could match the window resolution of course
    lightmapTex.create(512, 512);
    sf::Sprite lightmap(lightmapTex.getTexture());
    // Scale the sprite to fill the window
    lightmap.setScale(640 / 512.f, 480 / 512.f);
    // Set the lightmap's view to the same as the window
    lightmapTex.setView(window.getDefaultView());

    // Drawable helper to draw lights
    // We'll just have to adjust the first vertex's color to tint it
    sf::VertexArray light(sf::PrimitiveType::TriangleFan);
    light.append({sf::Vector2f(0, 0), sf::Color::White});
    // This is inaccurate, but for demo purposes…
    // This could be more elaborate to allow better graduation etc.
    for (float i = 0; i  <= 2 * PI; i += PI * .125f)
        light.append({sf::Vector2f(std::sin(i), std::cos(i)), sf::Color::Transparent});

    // Setup some lights
    std::vector<Light> lights;
    lights.push_back({sf::Vector2f(50.f, 50.f), sf::Color::White, 100.f });
    lights.push_back({sf::Vector2f(350.f, 150.f), sf::Color::Red, 150.f });
    lights.push_back({sf::Vector2f(150.f, 250.f), sf::Color::Yellow, 200.f });
    lights.push_back({sf::Vector2f(250.f, 450.f), sf::Color::Cyan, 100.f });

    // RenderStates helper to transform and draw lights
    sf::RenderStates rs(sf::BlendAdd);

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

        bool flip = false; // simple toggle to animate differently

        // Draw the light map
        lightmapTex.clear(sf::Color::Black);
        for(Light &l : lights)
        {
            // Apply all light attributes and render it

            // Reset the transformation
            rs.transform = sf::Transform::Identity;

            // Move the light
            rs.transform.translate(l.position);

            // And scale it (this could be animated to create flicker)
            rs.transform.scale(l.radius, l.radius);

            // Adjust the light color (first vertex)
            light[0].color = l.color;

            // Draw the light
            lightmapTex.draw(light, rs);

            // To make things a bit more interesting
            // We're moving the lights
            l.position.x += flip ? 2 : -2;
            flip = !flip;
            if (l.position.x > 640)
                l.position.x -= 640;
            else if (l.position.x < 0)
                l.position.x += 640;
        }
        lightmapTex.display();

        window.clear(sf::Color::White);
        // Draw the background / game
        window.draw(background);
        // Draw the lightmap
        window.draw(lightmap, sf::BlendMultiply);
        window.display();
    }
}