Круг шейдера GLSL колеблется при изменении размера в LibGDX - PullRequest
1 голос
/ 15 мая 2019

В LibGDX я создал шейдер для создания эффекта перехода, при котором круг образуется поверх изображения, где все, что находится за пределами круга, черное, а все, что находится внутри круга, отображается как обычно.Этот круг начинается с большого размера и уменьшается до нуля.Но у меня есть проблема, поскольку круг сокращается, он колеблется вокруг.Я также обнаружил, что у меня возникла эта проблема при использовании ShadeRenderer для создания круга, который также уменьшался со временем перед использованием шейдера.Я думаю, что проблема как-то связана с числами с плавающей запятой или способом отображения кругов.В любом случае, мне нужно знать, как мне это исправить, чтобы я мог плавно сжимать круг?

Вот рисунок, демонстрирующий мою проблему с колеблющимся кругом (который мне нужно сгладить): мой колеблющийся круг

Вот мой код шейдера фрагмента:

varying vec4 v_color;
varying vec3 v_position;
varying vec2 v_texCoord0;

uniform vec2 u_resolution;
uniform sampler2D u_sampler2D;

uniform float u_radius;
uniform int u_hasCircle;

void main() {

    vec4 color = texture2D(u_sampler2D, v_texCoord0) * v_color;

    vec2 relativePosition = gl_FragCoord.xy / u_resolution - .5;
    relativePosition.x *= u_resolution.x / u_resolution.y;
    float len = length(relativePosition);

    if (u_hasCircle == 1 && len > u_radius) {
        color = vec4(0, 0, 0, 1);
    }

    gl_FragColor = color;
}

А вот мой код вершинного шейдера, который запускается до этого:

атрибутvec4 a_color;атрибут vec3 a_position;атрибут vec2 a_texCoord0;

равномерный mat4 u_projTrans;Равномерное vec3 u_distort;

varying vec4 v_color;
varying vec3 v_position;
varying vec2 v_texCoord0;

void main() {
    v_color = a_color;
    v_position = a_position;
    v_texCoord0 = a_texCoord0;
    gl_Position = u_projTrans * vec4(a_position, 1.0);
}

Когда я хочу перейти к запуску, u_hasCircle передается 1, в противном случае - 0. Когда переход выполняется, я начинаю с передачи u_radius 1, а затем постепенно уменьшаюзначение до 0, используя FloatAction в LibGDX.Я посылаю эти значения в шейдер один раз за кадр.

Вот соответствующий Java-код Libgdx, который взаимодействует с шейдером:

public class PlayWorld extends Group implements InputProcessor, Disposable
{
    //various members

    private PlayScreen screen;

    private OrthographicCamera camera;

    private FloatAction transitionToBattleAction;
    private final float TRANS_TO_BATTLE_DURATION = 10f;

    private float circleSize;
    private boolean hasCircle = false;

    public PlayWorld(PlayWorld playWorld) {
        this.playWorld = playWorld;

        camera = new OrthographicCamera();

        tiledMap = new TiledMapActor(camera);

        addActor(tiledMap);

        transitionToBattleAction = new FloatAction();
    }

    //function that triggers transition
    public void enterBattle() {
        transitionToBattleAction.reset();

        transitionToBattleAction.setStart(0);
        transitionToBattleAction.setEnd(1);
        transitionToBattleAction.setDuration(TRANS_TO_BATTLE_DURATION);

        addAction();
    }

    // this function gets called every frame
    @Override
    public void act(float delta) {
        super.act(delta);

        if (transitionToBattleAction.getValue() == 0) {

            //this function is defined in code shown below
            tiledMap.unsetCircleSize();

        } else if (transitionToBattleAction.getValue() < 1) {

            //this function is defined in code shown below
            tiledMap.setCircleSize(
                1 - transitionToBattleAction.getValue());

        } else if (transitionToBattleAction.getValue() == 1) {

            //this function is defined in code shown below
            tiledMap.setCircleSize(
                1 - transitionToBattleAction.getValue());

            transitionToBattleAction.restart();

            screen.getGame().setScreen("battle");

        } else if (transitionToBattleAction.getValue() > 1) {

            //this function is defined in code shown below
            tiledMap.unsetCircleSize();

          transitionToBattleAction.restart();
        }                
    }

    //this gets called whenever the window resizes
    public void resize(int width, int height) {
        // this function is defined in code shown below
        tiledMap.resize(width, height);
    }

    //various other methods
}

public class TiledMapActor extends Actor implements InputProcessor, Disposable
{
    //various variables

    private ShaderProgram shader;

    private OrthographicCamera camera;

    private TiledMap map;
    private OrthogonalTiledMapRenderer renderer;
    private float UNIT_SCALE = 1 / 16f;

    public TiledMapActor(OrthographicCamera camera) {
        super();

        this.camera = camera;

        map = new TmxMapLoader().load("myMap.tmx");
        renderer = new OrthogonalTiledMapRenderer(map, UNIT_SCALE);

        shader = new ShaderProgram(
            Gdx.files.internal("shaders/myshader.vsh"), 
            Gdx.files.internal("shaders/myshader.fsh");

        System.out.println(
            shader.isCompiled() ? 
                "shader compiled" : shader.getLog());

        renderer.getBatch().setShader(shader);

        shader.begin();
        shader.setUniformi("u_hasCircle", 0);
        shader.end();
    }

    // this is called every time the window changes size
    // from the PlayScreen class, see code above
    public void resize(int width, int height) {
        camera.viewportWidth = width;
        camera.viewportHeight = height;
        camera.update();

        shader.begin();
        shader.setUniformf("u_resolution", (float)width, (float)height);
        shader.end();
    }

    //this method is called from code above, seen PlayScreen class code
    //
    public void setCircleSize(float circleSize) {
        this.circleSize = circleSize;
        hasCircle = true;
        shader.begin();
        shader.setUniformf("u_radius", circleSize);
        shader.setUniformi("u_hasCircle", 1);
        shader.end();
    }

    //this method is called from code above, seen PlayScreen class code
    //
    public void unsetCircleSize() {
        hasCircle = false;
        shader.begin();
        shader.setUniformi("u_hasCircle", 0);
        shader.end();
    }

    // Various other methods
}

1 Ответ

1 голос
/ 15 мая 2019

Так что я нашел проблему, и тупой меня! Я повторно входил в сцену перехода каждый кадр, как только он начался. Я исправил это, используя логический флаг, чтобы сообщить мне, начался ли переход, и только начал переход, если этот логический флаг еще не установлен. Затем, позже, после завершения перехода, я установил для этого логического флага значение false, чтобы переход мог произойти снова!

...