簡體   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