#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
прозрачные пиксели, в которые вы записываете, а затем нарисовать этот буфер поверх вашего игрового представления:
Шаги более или менее:
- Игра в жеребьевку
- Обновить буфер тумана войны
- Нарисуйте туман войны на вершине игры
// 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);