繁体   English   中英

调整大小时使精灵重复(libGDX)

[英]Making a sprite repeat when resized (libGDX)

我正在制作一个带有背景纹理的游戏,该纹理存储为精灵,因为我需要调整它的大小。 但是,当我调整它的大小时,它会改变图像的纵横比而不是重复它。 我为创建 Sprite 而传入的纹理将包裹设置为 Texture.TextureWrap.Repeat。

我目前的 class:

package com.lance.seajam;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;

public class Water extends ApplicationAdapter {
    private final Texture waterTexture;
    private final Texture noiseTexture;
    private SpriteBatch batch;
    private Sprite sprite;
    private ShaderProgram shaderProgram;

    private String vertexShaderString = Gdx.files.internal("shaders/water/mainvs.glsl").readString();
    private String fragmentShaderString = Gdx.files.internal("shaders/water/mainfs.glsl").readString();

    private float[] floatArrOf(float... A) {
        return A;
    }


    private void compileShader() {
        shaderProgram = new ShaderProgram(vertexShaderString, fragmentShaderString);
        if (!shaderProgram.isCompiled()) {
            System.out.println(shaderProgram.getLog());
        }
    }

    public Water(SpriteBatch batch, String imgDir) {
        this.batch = batch;
        waterTexture = new Texture(Gdx.files.internal(imgDir + "/water.png"));
        noiseTexture = new Texture(Gdx.files.internal(imgDir + "/noise.png"));

        noiseTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat); // make the texture repeat
        waterTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat); // make the texture repeat

        waterTexture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
        noiseTexture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);

        sprite = new Sprite(waterTexture);
        sprite.setSize((float) Gdx.graphics.getWidth(), (float) Gdx.graphics.getHeight()); // setting sprite size to graphics size

        compileShader();
    }

    float time = 0f;
    public void Draw() {
        shaderProgram.setUniformf("u_noise_scale", 0.1f);
        shaderProgram.setUniform2fv("u_noise_scroll_velocity", floatArrOf(0.004f, 0.003f), 0, 2);
        shaderProgram.setUniformf("u_distortion", 0.04f);
        shaderProgram.setUniformf("u_time", time);

        noiseTexture.bind();

        batch.begin();
            time += Gdx.graphics.getDeltaTime(); // Gets how much seconds has passed

            Gdx.gl.glEnable(GL20.GL_BLEND);

            batch.setShader(shaderProgram);
            batch.draw(sprite, sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight());

            Gdx.gl.glDisable(GL20.GL_BLEND);
        batch.end();
    }
}

有什么方法可以在调整大小时将其更改为重复? 如果是这样,怎么做?

永远不要使用batch.draw(sprite, ...) 应始终使用sprite.draw(batch)绘制精灵。 在 libGDX 的早期有一个不幸的设计决定,使 Sprite 成为 TextureRegion 的子类,这使得调用batch.draw(sprite, ...)成为可能,但绝不应该这样做。 Sprite 已经定义了它的大小、比例、颜色等。当您通过调用batch.draw()将其视为 TextureRegion 时,所有这些信息都将被忽略,除非您还手动传递 Sprite 的这些相关属性。

在我看来,Sprite class 几乎不应该被使用,因为它将资产数据(纹理区域)与 model 数据(位置、大小、比例、旋转等)混为一谈。 它仅适用于需要高度优化这些属性的情况,例如在粒子系统中(这就是 libGDX 自己的 2D 粒子系统的方式)。

目前,您处于使用没有任何特定投影矩阵的共享 SpriteBatch 的危险区域,并且在resize()中没有做任何事情。 这意味着这个 class 完全忽略了屏幕尺寸,在不同的设备上看起来会完全不同。 如果 DPI 不同,即使是相同尺寸屏幕的两部手机看起来也会完全不同。

对于这个特定的着色器,它只是用一种效果填充屏幕,我认为使用单位矩阵并让着色器调整效果的大小是合适的。 您可以绘制纹理以覆盖身份空间中的 +/- 1 X 和 Y 正方形,并且无论屏幕尺寸如何,它都会填满屏幕。

要使纹理在这样的着色器中重复,您可以在将它传递给draw时使用大于 1 的 U2 和 V2 纹理坐标,或者您可以为顶点着色器传递一个统一的u_textureRepeat 2 元素数组以乘以纹理坐标。 第二种方法可能更容易正确。 如果需要缩放不同的次数,它还允许您对第二个噪波纹理使用不同的值。

您需要一些恒定的重复次数值,以便您可以按屏幕比例进行缩放。 您可能还希望以相同的方式处理您的噪声标度,或者它会在不同的纵横比屏幕上以不同的方式拉伸。

您需要在设置其制服之前绑定您的着色器。 它现在可能正在工作,但是当您的应用程序中有其他着色器时,它会导致问题。

您需要将噪声纹理专门绑定到单元 1。您当前正在将其绑定到任意单元(无论与glActiveTexture交互的最后一件事是什么),您不应该把它留给机会。

使用 SpriteBatch 时不要使用glEnableglDisable进行混合。 SpriteBatch 已经在内部处理了它的混合。

把它们放在一起看起来像这样(我是从 memory 开始的,没有经过测试,所以请原谅语法错误):

public class Water extends ApplicationAdapter implements Disposable {
    private static final float TEXTURE_REPEATS_HORIZONTAL = 5f;
    private static final float NOISE_SPEED_HORIZONTAL = 0.004f;
    private static final Matrix4 IDT = new Matrix4();

    private final Texture waterTexture;
    private final Texture noiseTexture;
    private final SpriteBatch batch;
    private final ShaderProgram shaderProgram;

    private final String vertexShaderString = Gdx.files.internal("shaders/water/mainvs.glsl").readString();
    private final String fragmentShaderString = Gdx.files.internal("shaders/water/mainfs.glsl").readString();

    private void compileShader() {
        shaderProgram = new ShaderProgram(vertexShaderString, fragmentShaderString);
        if (!shaderProgram.isCompiled()) {
            System.out.println(shaderProgram.getLog());
        }
    }

    public Water(SpriteBatch batch, String imgDir) {
        this.batch = batch;
        waterTexture = new Texture(Gdx.files.internal(imgDir + "/water.png"));
        noiseTexture = new Texture(Gdx.files.internal(imgDir + "/noise.png"));

        noiseTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat); // make the texture repeat
        waterTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat); // make the texture repeat

        waterTexture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
        noiseTexture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);

        compileShader();
    }

    @Override
    public void dispose() {
        waterTexture.dispose();
        noiseTexture.dispose();
        shaderProgram.dispose();
    }

    float time = 0f;
    public void Draw() {
        time += Gdx.graphics.getDeltaTime(); // Gets how much seconds has passed

        shaderProgram.bind();

        float screenRatio = ((float)Gdx.graphics.getWidth()) / Gdx.graphics.getHeight();
        shaderProgram.setUniform2f("u_texCoordScale", TEXTURE_REPEATS_HORIZONTAL, TEXTURE_REPEATS_HORIZONTAL / screenRatio);
        shaderProgram.setUniformf("u_noise_scale", 0.1f);
        shaderProgram.setUniform2f("u_noise_scroll_velocity", NOISE_SPEED_HORIZONTAL, NOISE_SPEED_HORIZONTAL / screenRatio);
        shaderProgram.setUniformf("u_distortion", 0.04f);
        shaderProgram.setUniformf("u_time", time);

        noiseTexture.bind(1);

        batch.begin();
            batch.setProjectionMatrix(IDT);
            batch.enableBlending();
            batch.setShader(shaderProgram);
            batch.draw(waterTexture, -1f, 1f, 2f, -2f); // screen-covering square for identity matrix
        batch.end();
        batch.setShader(null); // reset to default for other classes that use same batch
    }
}

注意我实现了 Disposable。 由于这个 class 实例化了一些 Disposable 对象,它还必须负责处理它们。 并且无论哪个 class 实例化这个必须负责处理它。 否则您将有 memory 泄漏。

另请注意,使用shaderProgram.setUniformf通常比使用setUniformfv更容易。 当为各种不相关的参数使用单个数组时,您只需要后者。

在你的顶点着色器的某个地方,你应该有类似v_texCoords = aTexCoord0;的东西。 您应该为uniform vec2 u_texCoordScale添加统一,并在此行上将其相乘,使其为v_texCoords = aTexCoord0 * u_texCoordScale; . 如果您最终需要不同的噪声坐标比例,您可以在 class 中创建相关代码,以计算不同的均匀值以乘以噪声的纹理坐标。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM