如何用像素完美缩放/移动来固定SFML中顶点阵列中的垂直伪像线?

时间:2019-05-06 01:58:52

标签: c++ sfml

我一直在仅使用SFML和C ++进行2D自上而下的游戏,该游戏具有图块映射。在放大和缩小或以不同的缩放级别移动渲染视图时,无法消除垂直线伪影的图块。我在下面附上问题的图片;红色圆圈。

Problem with gap between squares

[edit]

有很多因素导致此错误不一致。 如果仅使用一个图块中的tile_atlas,则不会出现任何伪像。如果我不使用顶点数组将每个纹理映射到图块。我没有看到任何伪像,但是在屏幕上显示相同数量的图块时,它的速度要慢10到15倍。我尝试找到不会造成伪影的缩放级别,并且有一些。但水平几乎是随机的,并且会放大和缩小,而不是平滑且断断续续。

我尝试了许多教程和论坛“修复程序”,但这些方法并没有完全起作用。我已经完全重写了底层瓦片引擎4次,但无济于事。

https://en.sfml-dev.org/forums/index.php?topic=15747.0

topic = 14504

topic = 5952

topic = 13637.15

https://www.sfml-dev.org/tutorials/2.5/graphics-view.php

https://www.binpress.com/creating-city-building-game-with-sfml/

https://www.sfml-dev.org/tutorials/2.5/graphics-vertex-array.php

[edit]

我已经阅读了Terreria缩放问题,该修复程序可以制作超大纹理,然后缩放或多个纹理,每个缩放级别一个。似乎无遗。我正在寻找正确实现缩放的程序化方法。

这是我发布的最后一次修改代码的尝试,否则我将需要更改语言。

我认为主要问题来自程序的放大/缩小功能。 我已经尝试了许多变化/尝试使它能够工作在+ 0.5f偏移,+ 0.375f偏移而不是像素完美的情况下

if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
        {
            float zoom_in = 0.995f;

            float nz = last_sq * zoom_in;
            nz = std::floor(nz);
            float now = nz / last_sq;
            if (nz <= 10)
                continue;
            last_sq = nz;

            std::cout << now << std::endl;

            cam.zoom(now);
        }


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
        {
            cam.move(0.f, -0.02f);
            float x = cam.getCenter().x;
            float y = cam.getCenter().y;
            x = std::floor(x);
            y = std::floor(y);
            //std::cout << "x: " << x << "\ty: " << y << std::endl;
            cam.setCenter(x, y);
        }

这是完整的代码。

main.cpp


#include "chunk_map.h"
#include "tile_atlas.h"
#include <vector>
//#include "main.h"
#include "map.h"
#include <iostream>

#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/View.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Window/Keyboard.hpp>

#include "animation_handler.h"

int main(int argc, char* argv[])
{
    sf::RenderWindow app(sf::VideoMode(600, 600), "Tilemap Example");
    // Hard set fps to monitor refresh rate.

    // textures to load.
    /*text_mgr.loadTexture("grass", "grass.png");
    text_mgr.loadTexture("high_grass", "high_grass.png");

    Animation staticAnim(0, 0, 1.0f);
    tileAtlas["grass"] = Tile(32, 1, text_mgr.getRef("grass"),{ staticAnim },
            TileType::GRASS, 50, 0, 1);

    tileAtlas["high_grass"] = Tile(32, 1, text_mgr.getRef("high_grass"),{ staticAnim },
        TileType::HIGH_GRASS, 100, 0, 1);*/

    //Map map;

    //map.load(50, 50, tileAtlas);

    #ifdef NDEBUG
    app.setVerticalSyncEnabled(true);
    std::cout << "#################\n";
    #endif // DEBUG
    //app.setVerticalSyncEnabled(true);
    sf::View cam = app.getDefaultView();

    tile_atlas atlas = tile_atlas();

    std::vector<chunk_map> map;

    for (int x = 0; x < 5; x++)
    {
        for (int y = 0; y < 5; y++)
        {
            map.push_back(chunk_map());
            map.back().set_texture(atlas.get_atlas());
            map.back().set_position(10 * x, 10 * y, 10 * (x + 1), 10 * (y + 1));
            map.back().load_tiles();
        }
    }

    sf::Clock clock;
    int checked = 0;
    int last_sq = 600;

    while (app.isOpen())
    {
        //sf::Time elapsed = clock.restart();
        //float dt = elapsed.asSeconds();

        sf::Event eve;
        while (app.pollEvent(eve))
            if (eve.type == sf::Event::Closed)
                app.close();


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::P))
            std::cout << "view x: " << cam.getSize().x << "\tview y: " << cam.getSize().y << std::endl;


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
        {
            float zoom_out = 1.005f;

            float nz = last_sq * zoom_out;
            nz = std::ceil(nz);
            float now = nz / last_sq;
            last_sq = nz;

            std::cout << now << std::endl;

            cam.zoom(now);
            //float x = cam.getCenter().x;
            //float y = cam.getCenter().y;
            //x = std::floor(x);
            //y = std::floor(y);
            ////std::cout << "x: " << x << "\ty: " << y << std::endl;
            //cam.setCenter(x, y);
        }


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
        {
            float zoom_in = 0.995f;

            float nz = last_sq * zoom_in;
            nz = std::floor(nz);
            float now = nz / last_sq;
            if (nz <= 10)
                continue;
            last_sq = nz;

            std::cout << now << std::endl;

            cam.zoom(now);
            //float x = cam.getCenter().x;
            //float y = cam.getCenter().y;
            //x = std::floor(x);
            //y = std::floor(y);
            ////std::cout << "x: " << x << "\ty: " << y << std::endl;
            //cam.setCenter(x, y);
        }


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
        {
            cam.move(0.f, -0.02f);
            float x = cam.getCenter().x;
            float y = cam.getCenter().y;
            x = std::floor(x);
            y = std::floor(y);
            //std::cout << "x: " << x << "\ty: " << y << std::endl;
            cam.setCenter(x, y);
        }


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        {
            cam.move(-0.02f, 0.f);
            float x = cam.getCenter().x;
            float y = cam.getCenter().y;
            x = std::floor(x);
            y = std::floor(y);
            //std::cout << "x: " << x << "\ty: " << y << std::endl;
            cam.setCenter(x, y);
        }


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
        {
            cam.move(0.f, 0.02f);
            float x = cam.getCenter().x;
            float y = cam.getCenter().y;
            x = std::ceil(x);
            y = std::ceil(y);
            //std::cout << "x: " << x << "\ty: " << y << std::endl;
            cam.setCenter(x, y);
        }


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
        {
            cam.move(0.02f, 0.f);
            float x = cam.getCenter().x;
            float y = cam.getCenter().y;
            x = std::ceil(x);
            y = std::ceil(y);
            //std::cout << "x: " << x << "\ty: " << y << std::endl;
            cam.setCenter(x, y);
        }


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
        {
            sf::Time elapsed = clock.getElapsedTime();
            float t = elapsed.asSeconds();
            int time = std::floor(t);

            if (checked < time)
            {
                checked = time;

                cam.move(0.01f, 0.f);
                float x = cam.getCenter().x;
                float y = cam.getCenter().y;
                x = std::ceil(x);
                y = std::ceil(y);
                std::cout << "x: " << x << "\ty: " << y << std::endl;
                cam.setCenter(x, y);
            }
        }


        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
            app.close();

        app.setView(cam);

    #ifdef _DEBUG
        app.clear();
    #endif // DEBUG
        //map.draw(app, dt);

        for (int i = 0; i < 25; i++)
        {
            app.draw(map.at(i));
        }

        app.display();
    }
}

chunk_map.h

#pragma once


#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/VertexArray.hpp>
#include <vector>


class chunk_map : public sf::Drawable
{
private:
    //change values of these to match your needs and improve performance
    enum { tilesize = 32, chunksize = 32};

    //tile size float
    float tile_size_float = 32.0f;

    // Draw chunk
    virtual void draw(sf::RenderTarget& target, sf::RenderStates states)const;

    // texture for chunk
    sf::Texture m_texture;

    // chunk dimensions
    int tiles_per_chunk_x;
    int tiles_per_chunk_y;

    //start x and y and ending x and y scaled to tile size. a.k.a. 
    // 1,1 = tile 1,1. 10,10, equals tile 10,10
    int chunk_start_x;
    int chunk_start_y;

    int chunk_end_x;
    int chunk_end_y;

    // Vertex array of positions of tiles in chunk
    std::vector<std::vector<sf::VertexArray> > m_chunks;

    // Append tiles.
    void append_tile(int gx, int gy, sf::VertexArray& garr);

public:
    chunk_map();
    ~chunk_map();
    void load_tiles();


    void set_texture(sf::Texture);
    void set_position(int chunk_start_x, int chunk_start_y,
        int chunk_end_x, int chunk_end_y);

};


chunk_map.cpp

#include "chunk_map.h"

#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/System/Vector2.hpp>
#include <SFML/Graphics/Vertex.hpp>

chunk_map::chunk_map()
{
}

chunk_map::~chunk_map()
{
}


void chunk_map::load_tiles()
{
    /*

        Tile loading this is were the tiles are added to the Quadrantics of the tilemap.
        this is the entire chunk_map loop

    */
    if ((chunk_end_x * chunk_end_y) == 0)//empty map - possibly forgotten to fill data struct
    {
        //to stop displaying at all after failed loading:
        tiles_per_chunk_x = 0;
        tiles_per_chunk_y = 0;
        m_chunks.clear();
        return;
    }

    chunk_map::tiles_per_chunk_x = (chunk_end_x / chunksize) + 1;
    chunk_map::tiles_per_chunk_y = (chunk_end_y / chunksize) + 1;
    m_chunks.assign(tiles_per_chunk_x, std::vector<sf::VertexArray>(tiles_per_chunk_y, sf::VertexArray(sf::Quads)));//ready up empty 2d arrays
    for (int iy = chunk_start_y; iy < chunk_end_y; ++iy)
    {
        for (int ix = chunk_start_x; ix < chunk_end_x; ++ix)
        {
            append_tile(ix, iy, m_chunks[ix / chunksize][iy / chunksize]);
        }
    }
}


void chunk_map::append_tile(int gx, int gy, sf::VertexArray& garr)
{
    /*

        This  is the specific tile vertex, broken from the other function to decrease complexitity.

    */

    int tile_selection_index_x = rand() % 2;
    int tile_selection_index_y = 0;

    float f_tx = tile_selection_index_x * tile_size_float;
    float f_ty = tile_selection_index_y * tile_size_float;


    sf::Vertex ver;

    //____________________________________________________________________________________________________________
    ver.position = sf::Vector2f(gx * tile_size_float, gy * tile_size_float);
    //texture in position of text atlas
    //top left corner
    //ver.texCoords = sf::Vector2f( 0.f, 0.f);
    ver.texCoords = sf::Vector2f(f_tx, f_ty);
    garr.append(ver);

    //____________________________________________________________________________________________________________
    ver.position = sf::Vector2f(gx * tile_size_float + tile_size_float, gy * tile_size_float);
    //texture in position of text atlas
    //top right corner
    //ver.texCoords = sf::Vector2f( tile_size_float, 0.f);
    ver.texCoords = sf::Vector2f(f_tx + tile_size_float, f_ty);
    garr.append(ver);

    //____________________________________________________________________________________________________________
    ver.position = sf::Vector2f(gx * tile_size_float + tile_size_float, gy * tile_size_float + tile_size_float);
    //texture in position of text atlas
    //bottom right corner
    //ver.texCoords = sf::Vector2f( tile_size_float,  tile_size_float);
    ver.texCoords = sf::Vector2f(f_tx + tile_size_float, f_ty + tile_size_float);
    garr.append(ver);

    //____________________________________________________________________________________________________________
    ver.position = sf::Vector2f(gx * tile_size_float, gy * tile_size_float + tile_size_float);
    //texture in position of text atlas
    //bottom left corner
    //ver.texCoords = sf::Vector2f( 0.f,  tile_size_float);
    ver.texCoords = sf::Vector2f(f_tx, f_ty + tile_size_float);
    garr.append(ver);
}


void chunk_map::set_texture(sf::Texture t)
{
    /*

        Sets the texture data for this chunk map from the texture atlas.

    */

    m_texture = t;

    // TODO test this feature
    // Attempt to optimize tearing on zooming to a different view.
    //m_texture.setSmooth(true);
}


void chunk_map::set_position(int chunk_start_x, int chunk_start_y,
    int chunk_end_x, int chunk_end_y)
{
    /*

        Initialize the accordinates of the start of the chunk_map to the end.

    */
    chunk_map::chunk_start_x = chunk_start_x;
    chunk_map::chunk_start_y = chunk_start_y;

    chunk_map::chunk_end_x = chunk_end_x;
    chunk_map::chunk_end_y = chunk_end_y;
}


void chunk_map::draw(sf::RenderTarget& target, sf::RenderStates states)const
{
    /*

        The actual draw call to this specific chunk_map

    */


    // position variables for this draw.
    int left = 0;
    int right = 0;
    int top = 0;
    int bottom = 0;


    //get top left point of view
    sf::Vector2f temp = target.getView().getCenter() - (target.getView().getSize() / 2.f);


    //get top left point of view
    left = static_cast<int>(temp.x / (chunksize * tilesize));
    top = static_cast<int>(temp.y / (chunksize * tilesize));


    //get bottom right point of view
    temp += target.getView().getSize();
    right = 1 + static_cast<int>(temp.x / (chunksize * tilesize));
    bottom = 1 + static_cast<int>(temp.y / (chunksize * tilesize));


    //clamp these to fit into array bounds:
    left = std::max(0, std::min(left, tiles_per_chunk_x));
    top = std::max(0, std::min(top, tiles_per_chunk_y));
    right = std::max(0, std::min(right, tiles_per_chunk_x));
    bottom = std::max(0, std::min(bottom, tiles_per_chunk_y));


    //set texture and draw visible chunks:
    states.texture = &m_texture;



    for (int ix = left; ix < right; ++ix)
    {
        for (int iy = top; iy < bottom; ++iy)
        {
            target.draw(m_chunks[ix][iy], states);
        }
    }
}

tile_atlas.h

#pragma once
#include <SFML/Graphics/Texture.hpp>
class tile_atlas
{
private:
    sf::Texture atlas_texture;
public:
    tile_atlas();
    ~tile_atlas();
    sf::Texture& get_atlas();
};

tile_atlas.cpp

#include "tile_atlas.h"
#include <iostream>
#include <string>


tile_atlas::tile_atlas()
{
    std::string file_string = "tilemap_test.png";
    if (!atlas_texture.loadFromFile(file_string))
    {
        std::cout << "Failed loading file: " << file_string << std::endl;
        exit(1);
    }
}

tile_atlas::~tile_atlas()
{
}

sf::Texture& tile_atlas::get_atlas()
{
    return atlas_texture;
}

我正在尝试修复此代码以消除垂直伪像,因此无论移动视图还是放大/缩小,以上图像始终看起来像这样。

tilemap as expected



[答案代码]

使用@Mario的答案,这是我编写的代码(位于main.cpp的底部),用于完全修复工件。 这是显示示例的好链接。 https://www.sfml-dev.org/tutorials/2.5/graphics-draw.php#off-screen-drawing

    #ifdef _DEBUG
        app.clear();
    #endif // DEBUG
        //map.draw(app, dt);


        /*-----------------------------------------------------------*/
        // Draw the texture
        //rt.clear();

        rt.draw(map.at(0));
        rt.display();
        if (cam.getSize().x < 500)
        {
            rt.setSmooth(false);
        }
        else
        {
            rt.setSmooth(true);
        }
        //// get the target texture (where the stuff has been drawn)
        const sf::Texture& texture = rt.getTexture();

        sf::Sprite sprite(texture);

        app.draw(sprite);

        //app.draw(map.at(0));

        /*-----------------------------------------------------------*/

        app.display();

1 个答案:

答案 0 :(得分:1)

简单但有效:

在不缩放到渲染纹理的情况下将像素1:1渲染,然后将其放大。

确定正确的位置,缩放等可能有些棘手,但是可以做到。