java – LibGdx有没有办法将着色器应用于sprite批处理的一部分以获得水效果?

前端之家收集整理的这篇文章主要介绍了java – LibGdx有没有办法将着色器应用于sprite批处理的一部分以获得水效果?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

所以我有一个水效应应用于矩形图像,这是我的水,以应用一个sin波函数.它仅适用于此TextureRegion:

Water.java

public  void updateshaders(){

    float dt = Gdx.graphics.getDeltaTime();

    if(waterShader != null){

        time += dt ;
        float angle = time * (2 * MathUtils.PI);
        if (angle > (2 * MathUtils.PI))
            angle -= (2 * MathUtils.PI);

        Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA,GL20.GL_ONE_MINUS_SRC_ALPHA);
        Gdx.gl20.glEnable(GL20.GL_BLEND);
        waterShader.setUniformMatrix("u_projTrans",gs.cam.combined);

        waterShader.begin();
        waterShader.setUniformf("timedelta",-angle);
        waterShader.end();
    }
}
@Override
public void draw(SpriteBatch g) {
    g.end();
    updateshaders();
    g.setProjectionMatrix(gs.cam.combined);
    g.setShader(waterShader);
    g.begin();
    g.draw(gs.gsm.rm.water_top,x,y + height - gs.gsm.rm.water_top.getRegionHeight(),width,gs.gsm.rm.water_top.getRegionHeight());
    g.draw(gs.gsm.rm.water,y,height - gs.gsm.rm.water_top.getRegionHeight());
    g.end();
    g.setShader(null);
    g.begin();
}

我想将此效果添加到红色矩形中的所有内容.我正在考虑刷新SpriteBatch并剪切出该区域的图像,应用失真然后将其重绘在原始图像上,然后完成渲染线程的其余部分.

更新:
所以我的解决方案有效……有点儿.它非常慢.它看起来是正确的,但使游戏无法播放和缓慢.
(在gif中可能会有点难以注意,但它在游戏中看起来很不错.)

代码

Water.java

public void draw(SpriteBatch g) {
    g.end();
    updateshaders();
    //Distortion
    coords = gs.cam.project(new Vector3(x,0));
    if(scr != null){scr.getTexture().dispose();}
    scr = ScreenUtils.getFrameBufferTexture(
            (int)coords.x,(int)coords.y,(int)(width * scaleX),(int)((height - gs.gsm.rm.water_top.getRegionHeight() / 4) * scaleY));

    if(scr != null){
        g.setShader(waterShader2);
        g.begin();
        g.draw(scr,height- gs.gsm.rm.water_top.getRegionHeight() / 4);
        g.end();
    }
    //SURFACE WAVES
    g.setShader(waterShader);
    g.begin();
    g.draw(gs.gsm.rm.water_top,gs.gsm.rm.water_top.getRegionHeight());
    g.end();
    //BACK TO NORMAL
    g.setShader(null);
    g.begin();
    g.draw(gs.gsm.rm.water,height - gs.gsm.rm.water_top.getRegionHeight());
}

更新:
我尝试了Tenfour04的解决方案,它起作用了… sorta.虽然存在失真效果,并且游戏以完全FPS运行,但失真会使背景显示出来.这是因为着色器正在应用于纹理,而不仅仅是在我使用的区域范围内:

    scr = new TextureRegion(((PlayGameState) gs).frameBuffer.getColorBufferTexture(),(int)coords.x,(int)((height - gs.gsm.rm.water_top.getRegionHeight() / 4) * scaleY));
最佳答案
在每一帧上,您将丢弃并重新创建Pixmap和Texture,然后将屏幕捕获到该纹理.这是一个非常缓慢的操作,一遍又一遍地重复.

相反,您可以将游戏直接渲染到持久的屏幕外帧缓冲区.

private FrameBuffer frameBuffer;
private final Matrix4 idt = new Matrix4();

public void render() {
    if (frameBuffer == null){
        //Normally this would go in the resize method,but that causes issues on iOS 
        //because resize isn't always called on the GL thread in iOS. So lazy load here.
        try {
            frameBuffer = new FrameBuffer(Format.RGBA8888,Gdx.graphics.getWidth(),Gdx.graphics.getHeight(),false);
        } catch (GdxRuntimeException e){ 
            frameBuffer = new FrameBuffer(Format.RGB565,false);
            //RGBA8888 not supported on all devices. You might instead want to turn off 
            //the water effect if it's not supported,because 565 is kinda ugly.
        }
    }

    frameBuffer.begin();
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    //draw everything that is behind the water layer here
    frameBuffer.end();

    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    //Draw the frame buffer texture to the screen:
    spriteBatch.setProjectionMatrix(idt);
    spriteBatch.begin();
    spriteBatch.draw(frameBuffer.getColorBufferTexture(),-1,1,2,-2); //IIRC,you need to vertically flip it. If I remembered wrong,then do -1,2
    spriteBatch.end();

    water.draw(spriteBatch,frameBuffer.getColorBufferTexture());

    //draw stuff that is in front of the water here
}

然后修改您的水类如下.如果我没记错的话,屏幕纹理是颠倒的,所以你可能不得不翻转一些数学.我没有在下面检查你的数学,知道你是否已经考虑到了这一点.

public void draw(SpriteBatch g,Texture scr) {
    updateshaders();
    //Distortion
    coords = gs.cam.project(new Vector3(x,0));

    if(scr != null){
        g.setShader(waterShader2);
        g.begin();
        g.draw(scr,height - gs.gsm.rm.water_top.getRegionHeight());
}

在这个过程中没有优化的一件事是你最终将水层后面的所有像素绘制至少两次.一旦在帧缓冲区上绘制它,然后第二次将帧缓冲区绘制到屏幕上.这可以在以后优化(如果你是填充率),只需在水面覆盖的视图区域中绘制背后的水,然后将绘制帧缓冲区纹理的步骤替换为绘制背景的东西通常.如果您成功构建了一个RGBA8888帧缓冲区,这只会看起来很好,而且并不是所有的Androids都支持.

原文链接:https://www.f2er.com/java/437161.html

猜你在找的Java相关文章