LibGDX / OpenGL 模板缓冲区屏蔽不工作
LibGDX / OpenGL Stencil Buffer masking not working
我正在使用 LibGDX 渲染 2d fog-of-war 类型的功能。这涉及在整个地图上绘制一个黑色矩形,其中有透明孔,您可以在其中看到下面的地图。我正在尝试使用 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();
实现 fog-of-war 效果的一种方法是保持 Framebuffer
写入透明像素,然后绘制该缓冲区位于您的游戏视图之上:
步骤大致是:
画游戏
更新 fog-of-war 缓冲区
在游戏顶部绘制迷雾-war
// 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();
我知道这在技术上无法回答您关于模板缓冲区的问题,但这是实现 fog-of-war.
的简单方法
上面动画的完整源代码;
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() > 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();
}
}
我终于能够让它与模板一起工作。我使用的模板操作存在一些问题,但下面的代码对我有用。
//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);
我正在使用 LibGDX 渲染 2d fog-of-war 类型的功能。这涉及在整个地图上绘制一个黑色矩形,其中有透明孔,您可以在其中看到下面的地图。我正在尝试使用 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();
实现 fog-of-war 效果的一种方法是保持 Framebuffer
写入透明像素,然后绘制该缓冲区位于您的游戏视图之上:
步骤大致是:
画游戏
更新 fog-of-war 缓冲区
在游戏顶部绘制迷雾-war
// 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();
我知道这在技术上无法回答您关于模板缓冲区的问题,但这是实现 fog-of-war.
的简单方法上面动画的完整源代码;
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() > 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();
}
}
我终于能够让它与模板一起工作。我使用的模板操作存在一些问题,但下面的代码对我有用。
//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);