2015-07-30 9 views
5

Così ho un effetto acqua applicata a un'immagine rettangolare che è la mia acqua per applicare una funzione d'onda peccato attraverso di esso. Si applica solo per questo TextureRegion:LibGdx Esiste un modo per applicare lo shader a una sezione del batch sprite per un effetto acqua?

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, x, y, width, height - gs.gsm.rm.water_top.getRegionHeight()); 
    g.end(); 
    g.setShader(null); 
    g.begin(); 
} 

Imgur

voglio aggiungere questo effetto per tutto nel rettangolo rosso. Stavo pensando di lavaggio del SpriteBatch e tagliando fuori l'immagine della regione, applicando la distorsione poi ridisegnare sopra l'originale, poi finire il resto del mio thread di rendering.

UPDATE: Così la mia soluzione ha funzionato ... sorta. È incredibilmente lento. Sembra corretto ma rende il gioco ingiocabile e lento. (potrebbe essere un po 'difficile da notare in gif, ma si guarda bene nel gioco.) gif

Nuovo codice:

Water.java

public void draw(SpriteBatch g) { 
    g.end(); 
    updateshaders(); 
    //Distortion 
    coords = gs.cam.project(new Vector3(x,y,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, 
      x,y, 
      width,height- gs.gsm.rm.water_top.getRegionHeight()/4); 
     g.end(); 
    } 
    //SURFACE WAVES 
    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.end(); 
    //BACK TO NORMAL 
    g.setShader(null); 
    g.begin(); 
    g.draw(gs.gsm.rm.water, x, y, width, height - gs.gsm.rm.water_top.getRegionHeight()); 
} 

UPDATE: ho provato La soluzione di Tenfour04 e ha funzionato ... in qualche modo. Mentre c'è un effetto di distorsione e il gioco gira a pieno regime, la distorsione rende lo sfondo mostrato. Questo perché lo shader viene applicato alla struttura, non solo entro i confini della regione che afferro utilizzando:

scr = new TextureRegion(((PlayGameState) gs).frameBuffer.getColorBufferTexture(), 
      (int)coords.x, (int)coords.y, 
      (int)(width * scaleX),(int)((height - gs.gsm.rm.water_top.getRegionHeight()/4) * scaleY)); 

werid

risposta

3

su ogni fotogramma, si sta scartando e ricreare una pixmap e una Texture, e quindi catturare lo schermo su quella texture. Si tratta di un'operazione estremamente lenta da ripetere più e più volte.

Invece, è possibile rendere il gioco direttamente ad un buffer persistenti fuori schermo telaio.

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, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), 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, -1, 2, 2 
    spriteBatch.end(); 

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

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

E quindi modificare la classe d'acqua come di seguito. Se ricordo bene, la trama dello schermo è capovolta, quindi potresti dover capovolgere alcuni dei tuoi calcoli. Non ho controllato i tuoi calcoli qui sotto per sapere se hai già un account per quello.

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

    if(scr != null){ 
     g.setShader(waterShader2); 
     g.begin(); 
     g.draw(scr, 
      x,y, 
      width,height- gs.gsm.rm.water_top.getRegionHeight()/4); 
     g.end(); 
    } 
    //SURFACE WAVES 
    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.end(); 
    //BACK TO NORMAL 
    g.setShader(null); 
    g.begin(); 
    g.draw(gs.gsm.rm.water, x, y, width, height - gs.gsm.rm.water_top.getRegionHeight()); 
} 

Una cosa che è non ottimizzata in questa procedura è che si finisce per disegnare tutti i pixel dietro lo strato d'acqua almeno due volte. Una volta per disegnarlo sul frame buffer, e poi una seconda volta quando il frame buffer viene disegnato sullo schermo. Ciò può essere ottimizzato in seguito (se sei fill rate bound) disegnando la roba dietro acqua solo nelle aree della vista che sono coperti da acqua, e poi sostituendo la fase di disegno del tessuto frame buffer allo schermo con disegno la roba di sfondo normalmente. Sarebbe bello solo se hai costruito correttamente un frame buffer RGBA8888, che non è supportato su tutti gli Android.

+0

L'unica cosa è che ho dovuto ricollocare la matrice di proiezione sulla fotocamera prima che l'acqua venga pescata, inoltre ho dovuto prendere una regione della texture del frame buffer per raggiungere il mio obiettivo –

+0

La vostra soluzione è stata eccezionale per la parte performance , ma come posso assicurarmi che lo shader sia applicato solo alla "TextureRegion" nella casella rossa? –

+0

L'intera trama viene disegnata sullo schermo e quindi la classe dell'acqua ridisegna le regioni che richiedono l'effetto. Immaginavo che tu avessi già quel pezzo che lavorava con la texture delle dimensioni dello schermo che ricevevi da ScreenUtils. Non sei sicuro di come funziona la tua classe d'acqua a causa di tutte le variabili. – Tenfour04

Problemi correlati