Маскировка буфера трафарета LibGDX / OpenGL не работает

#android #opengl-es #libgdx #stencil-buffer

#Android #opengl-es #libgdx #трафарет-буфер

Вопрос:

Я использую LibGDX для визуализации функций 2d типа тумана войны. Это включает в себя рисование темного прямоугольника по всей карте с прозрачными отверстиями в нем, где вы можете видеть карту ниже. Я пытаюсь использовать буфер трафаретов OpenGL для создания круговых масок, но, похоже, я не могу правильно понять логику.

Приведенный ниже код правильно рисует темный прямоугольник (туман), но круговые маски не наносятся по трафарету. т. е. вся карта темная.

 batch.end();   Gdx.gl.glClear(GL_STENCIL_BUFFER_BIT);  Gdx.gl.glEnable(GL20.GL_STENCIL_TEST);  Gdx.gl.glColorMask(false, false, false, false);  //always write the clipped holes to the stencil  Gdx.gl.glStencilFunc(GL20.GL_ALWAYS, 0x1, 0xffffffff);  Gdx.gl.glStencilMask(0xFF);  Gdx.gl.glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);   shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);  shapeRenderer.setProjectionMatrix(gameUi.camera.combined);    //test circle  shapeRenderer.circle(0, 0, 1000, 100);   shapeRenderer.end();    Gdx.gl.glEnable(GL20.GL_BLEND);  Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);  Gdx.gl.glColorMask(true, true, true, true);  Gdx.gl.glEnable(GL20.GL_STENCIL_TEST);  //only draw the fog of war where stencil is 0  Gdx.gl.glStencilFunc(GL_EQUAL, 0x0, 0xffffffff);  Gdx.gl.glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);    //draw fog of war  shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);  shapeRenderer.setProjectionMatrix(gameUi.camera.combined);  shapeRenderer.setColor(radarFog);  int dimension = gameUi.mapConfig.getDimension();  shapeRenderer.rect(-dimension,-dimension,dimension*3,dimension*3);  shapeRenderer.end();   Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);   batch.begin();  

Ответ №1:

Один из способов добиться эффекта «тумана войны» -сохранить Framebuffer прозрачные пиксели, в которые вы записываете, а затем нарисовать этот буфер поверх вашего игрового представления:

введите описание изображения здесь

Шаги более или менее:

  1. Игра в жеребьевку
  2. Обновить буфер тумана войны
  3. Нарисуйте туман войны на вершине игры
     // Draw the game batch.setProjectionMatrix(camera.combined); batch.begin();  batch.draw(map, 0, 0); batch.end();  // Draw the fog of war to a previously initialized FrameBuffer without first clearing the buffer fogOfWarBuffer.begin();   shapeRenderer.setProjectionMatrix(camera.combined);  shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);  shapeRenderer.setColor(1, 1, 1, 0);  shapeRenderer.circle(position.x, position.y, 64, 16);  shapeRenderer.end(); fogOfWarBuffer.end();  // Draw the FrameBuffer as a texture batch.begin();  batch.draw(fogOfWarRegion, 0, 0, camera.viewportWidth, camera.viewportHeight); batch.end();  

Я знаю, что технически это не ответ на ваш вопрос о буферах трафаретов, но это простой способ достичь тумана войны.

Полный исходный код для анимации выше;

 import com.badlogic.gdx.Game; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.glutils.FrameBuffer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2;  public class FogOfWarGame extends Game {  OrthographicCamera camera;  ShapeRenderer shapeRenderer;  SpriteBatch batch;  Texture map;  FrameBuffer fogOfWarBuffer;  TextureRegion fogOfWarRegion;  Vector2 position;  Vector2 direction = new Vector2(1.0f, 0.0f);   @Override  public void create() {  position = new Vector2(Gdx.graphics.getWidth()/2.0f, Gdx.graphics.getHeight()/2.0f);  camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());  camera.position.set(camera.viewportWidth / 2.0f, camera.viewportHeight / 2.0f, 0.0f);  camera.update();   map = new Texture("zelda.png");   fogOfWarBuffer = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);  fogOfWarBuffer.begin();  {  Gdx.gl.glClearColor(1.0f, 0.5f, 0.8f, 1.0f);  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);  }  fogOfWarBuffer.end();   fogOfWarRegion = new TextureRegion(fogOfWarBuffer.getColorBufferTexture());  fogOfWarRegion.flip(false, true);   shapeRenderer = new ShapeRenderer();  batch = new SpriteBatch();  }   @Override  public void render() {  position.add(direction);  if (MathUtils.random() gt; 0.9f)  direction.rotateDeg(MathUtils.random(-45, 45));   // Draw the game  batch.setProjectionMatrix(camera.combined);  batch.begin();  batch.draw(map, 0, 0);  batch.end();   // Draw the fog of war to a previously initialized FrameBuffer without first clearing the buffer  fogOfWarBuffer.begin();  shapeRenderer.setProjectionMatrix(camera.combined);  shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);  shapeRenderer.setColor(1, 1, 1, 0);  shapeRenderer.circle(position.x, position.y, 64, 16);  shapeRenderer.end();  fogOfWarBuffer.end();   // Draw the FrameBuffer as a texture  batch.begin();  batch.draw(fogOfWarRegion, 0, 0, camera.viewportWidth, camera.viewportHeight);  batch.end();  } }  

Комментарии:

1. Это не работает на меня. Я использую API scene2d, и даже при настройке матрицы проекции круг отображается не в нужном месте. Возможно, это как-то связано с тем, что мой буфер кадров должен быть намного больше экрана, в то время как ваш пример просто соответствует экрану. Это также кажется намного медленнее, чем трафарет.

2. Это хороший момент, @TheShoeShiner, этот метод плохо работает, когда карта намного больше окна.

Ответ №2:

Наконец-то я смог заставить это работать с трафаретами. Были некоторые проблемы с трафаретными операциями, которые я использовал, но приведенный ниже код работает для меня.

 //enable stencil and depth testing  gl.glEnable(GL_STENCIL_TEST);  gl.glEnable(GL_DEPTH_TEST);  //enable blending because our fog has alpha  Gdx.gl.glEnable(GL20.GL_BLEND);  Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);  //stencil op which writes to stencil when test passes  gl.glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);  //clear stencil buffer  gl.glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);  //always pass the stencil test and mask to accept all values  gl.glStencilFunc(GL_ALWAYS, 1, 0xFF);  gl.glStencilMask(0xFF);   //draw our cut out circles  drawCircles();   //change stencil function to only match where stencil was written  gl.glStencilFunc(GL_NOTEQUAL, 1, 0xFF);  //change mask so nothing is written to stencil  gl.glStencilMask(0x00);    drawFog();   //disable features  gl.glDisable(GL_STENCIL_TEST);  gl.glDisable(GL_DEPTH_TEST);  gl.glDisable(GL_BLEND);