Libgdx/Java 使用相机更新 ShadowMap 和 Framebuffer
Libgdx/Java ShadowMap and Framebuffer update using Cameras
我正在开发一个小型角色扮演游戏,今天我尝试添加阴影。
我在这里看到这个教程很好:)
所以下面的问题:
来自教程站点的测试示例,工作正常。有一个摄像头,阴影在正确的地方。
当我使用我的玩家精灵的正交相机时,阴影不在它应该在的地方,当我移动时,阴影和我一起移动,他在显示屏上的同一个位置,但在世界中阴影正在移动 _/ 看起来是这样的:
SpriteSheet 应该投射阴影...但它们在错误的位置 :/
这里我没动:
天很黑,因为day/night周期...
这里我向左移动了一点点:
我不知道为什么它不起作用...我将正交相机从我的主 class 中移到了阴影 class 中。还是我错过了什么? :/
我的代码应该没问题,但如果有人想看这里是一些代码:
public class LightSystemShader {
private int lightSize = 500;
private float upScale = 1f; //for example; try lightSize=128, upScale=1.5f
SpriteBatch batch;
OrthographicCamera cam;
BitmapFont font;
FPSLogger fps;
TextureRegion shadowMap1D; //1 dimensional shadow map
TextureRegion occluders; //occluder map
FrameBuffer shadowMapFBO;
FrameBuffer occludersFBO;
Texture casterSprites;
Texture enmyAnimation;
Texture light;
ShaderProgram shadowMapShader, shadowRenderShader;
Array<Light> lights = new Array<Light>();
boolean additive = true;
boolean softShadows = true;
float xKoord = 600;
public static ShaderProgram createShader(String vert, String frag) {
ShaderProgram prog = new ShaderProgram(vert, frag);
if (!prog.isCompiled())
throw new GdxRuntimeException("could not compile shader: " + prog.getLog());
if (prog.getLog().length() != 0)
Gdx.app.log("GpuShadows", prog.getLog());
return prog;
}
public LightSystemShader() {
batch = new SpriteBatch();
ShaderProgram.pedantic = false;
//read vertex pass-through shader
final String VERT_SRC = Gdx.files.internal("data/pass.vert").readString();
// renders occluders to 1D shadow map
shadowMapShader = createShader(VERT_SRC, Gdx.files.internal("data/shadowMap.frag").readString());
// samples 1D shadow map to create the blurred soft shadow
shadowRenderShader = createShader(VERT_SRC, Gdx.files.internal("data/shadowRender.frag").readString());
//the occluders
casterSprites = new Texture("data/cat4.png");
//
enmyAnimation = new Texture("EnemyAnimations/BugIdleStand.png");
//the light sprite
light = new Texture("data/light.png");
cam = new OrthographicCamera(0,0);
cam.setToOrtho(false);
updateMaps();
font = new BitmapFont();
Gdx.input.setInputProcessor(new InputAdapter() {
public boolean touchDown(int x, int y, int pointer, int button) {
float mx = x;
float my = Gdx.graphics.getHeight() - y;
lights.add(new Light(mx, my, randomColor()));
return true;
}
public boolean keyDown(int key) {
if (key==Keys.SPACE){
clearLights();
return true;
} else if (key==Keys.A){
additive = !additive;
return true;
} else if (key==Keys.S){
softShadows = !softShadows;
return true;
}
return false;
}
});
clearLights();
}
public void renderLightSystemShader(OrthographicCamera screenCam){
screenCam = new OrthographicCamera(0,0);
screenCam.setToOrtho(false);
Gdx.gl.glClearColor(0,0,0,0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
float mx = Gdx.input.getX();
float my = Gdx.graphics.getHeight() - Gdx.input.getY();
if (additive)
batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE);
for (int i=0; i<lights.size; i++) {
Light o = lights.get(i);
if (i==lights.size-1) {
o.x = mx;
o.y = my;
}
renderLight(o, screenCam);
}
if (additive)
batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
//STEP 4. render sprites in full colour
batch.setProjectionMatrix(screenCam.combined);
batch.begin();
batch.setShader(null); //default shader
drawShaderBatch(batch);
//DEBUG RENDERING -- show occluder map and 1D shadow map
/*batch.setColor(Color.BLACK);
batch.draw(occluders, Gdx.graphics.getWidth()-lightSize, 0);
batch.setColor(Color.WHITE);
batch.draw(shadowMap1D, Gdx.graphics.getWidth()-lightSize, lightSize+5);
//DEBUG RENDERING -- show light
batch.draw(light, mx-light.getWidth()/2f, my-light.getHeight()/2f); //mouse
batch.draw(light, Gdx.graphics.getWidth()-lightSize/2f-light.getWidth()/2f, lightSize/2f-light.getHeight()/2f);
*/
//draw FPS
batch.end();
System.out.println(Gdx.graphics.getFramesPerSecond());
}
// draw Lights
void renderLight(Light o, OrthographicCamera screenCam) {
float mx = o.x;
float my = o.y;
screenCam = new OrthographicCamera(0,0);
screenCam.setToOrtho(false);
//STEP 1. render light region to occluder FBO
//bind the occluder FBO
occludersFBO.begin();
//clear the FBO
Gdx.gl.glClearColor(0f,0f,0f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//set the orthographic camera to the size of our FBO
screenCam.setToOrtho(false, occludersFBO.getWidth(), occludersFBO.getHeight());
//translate camera so that light is in the center
screenCam.translate(mx - lightSize/2f, my - lightSize/2f);
//update camera matrices
screenCam.update();
//set up our batch for the occluder pass
batch.setProjectionMatrix(screenCam.combined);
batch.setShader(null); //use default shader
batch.begin();
// ... draw any sprites that will cast shadows here ... //
batch.draw(casterSprites, 0, 0);
batch.draw(enmyAnimation,xKoord,600,300,100);
//end the batch before unbinding the FBO
batch.end();
//unbind the FBO
occludersFBO.end();
//STEP 2. build a 1D shadow map from occlude FBO
//bind shadow map
shadowMapFBO.begin();
//clear it
Gdx.gl.glClearColor(0f,0f,0f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//set our shadow map shader
batch.setShader(shadowMapShader);
batch.begin();
shadowMapShader.setUniformf("resolution", lightSize, lightSize);
shadowMapShader.setUniformf("upScale", upScale);
//reset our projection matrix to the FBO size
screenCam.setToOrtho(false, shadowMapFBO.getWidth(), shadowMapFBO.getHeight());
batch.setProjectionMatrix(screenCam.combined);
//draw the occluders texture to our 1D shadow map FBO
batch.draw(occluders.getTexture(), 0, 0, lightSize, shadowMapFBO.getHeight());
//flush batch
batch.end();
//unbind shadow map FBO
shadowMapFBO.end();
//STEP 3. render the blurred shadows
//reset projection matrix to screen
screenCam.setToOrtho(false);
batch.setProjectionMatrix(screenCam.combined);
//set the shader which actually draws the light/shadow
batch.setShader(shadowRenderShader);
batch.begin();
shadowRenderShader.setUniformf("resolution", lightSize, lightSize);
shadowRenderShader.setUniformf("softShadows", softShadows ? 1f : 0f);
//set color to light
batch.setColor(o.color);
float finalSize = lightSize * upScale;
//draw centered on light position
batch.draw(shadowMap1D.getTexture(), mx-finalSize/2f, my-finalSize/2f, finalSize, finalSize);
//flush the batch before swapping shaders
batch.end();
//reset color
batch.setColor(Color.WHITE);
//xKoord+=1;
}
void clearLights() {
lights.clear();
lights.add(new Light(Gdx.input.getX(), Gdx.graphics.getHeight()-Gdx.input.getY(), Color.WHITE));
}
static Color randomColor() {
float intensity = (float)Math.random() * 0.5f + 0.5f;
return new Color((float)Math.random(), (float)Math.random(), (float)Math.random(), intensity);
}
void drawShaderBatch(SpriteBatch batch){
batch.draw(casterSprites, 0, 0);
batch.draw(enmyAnimation,600,600,300,100);
}
void updateMaps(){
occludersFBO = new FrameBuffer(Format.RGBA8888, lightSize, lightSize, false);
occluders = new TextureRegion(occludersFBO.getColorBufferTexture());
occluders.flip(false, true);
//our 1D shadow map, lightSize x 1 pixels, no depth
shadowMapFBO = new FrameBuffer(Format.RGBA8888, lightSize, 1, false);
Texture shadowMapTex = shadowMapFBO.getColorBufferTexture();
//use linear filtering and repeat wrap mode when sampling
shadowMapTex.setFilter(TextureFilter.Linear, TextureFilter.Linear);
shadowMapTex.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
//for debugging only; in order to render the 1D shadow map FBO to screen
shadowMap1D = new TextureRegion(shadowMapTex);
shadowMap1D.flip(false, true);
}
public void doDispose(){
batch.dispose();
font.dispose();
shadowMapFBO.dispose();
shadowMapShader.dispose();
shadowRenderShader.dispose();
casterSprites.dispose();
enmyAnimation.dispose();
light.dispose();
occludersFBO.dispose();
}
}
我自己发现的 ^^ 我忘了将它添加到 void renderLights 中:
cam.position.set(positionX,positionY, 0);
我正在开发一个小型角色扮演游戏,今天我尝试添加阴影。 我在这里看到这个教程很好:)
所以下面的问题:
来自教程站点的测试示例,工作正常。有一个摄像头,阴影在正确的地方。
当我使用我的玩家精灵的正交相机时,阴影不在它应该在的地方,当我移动时,阴影和我一起移动,他在显示屏上的同一个位置,但在世界中阴影正在移动 _/ 看起来是这样的:
SpriteSheet 应该投射阴影...但它们在错误的位置 :/
这里我没动:
天很黑,因为day/night周期...
这里我向左移动了一点点:
我不知道为什么它不起作用...我将正交相机从我的主 class 中移到了阴影 class 中。还是我错过了什么? :/
我的代码应该没问题,但如果有人想看这里是一些代码:
public class LightSystemShader {
private int lightSize = 500;
private float upScale = 1f; //for example; try lightSize=128, upScale=1.5f
SpriteBatch batch;
OrthographicCamera cam;
BitmapFont font;
FPSLogger fps;
TextureRegion shadowMap1D; //1 dimensional shadow map
TextureRegion occluders; //occluder map
FrameBuffer shadowMapFBO;
FrameBuffer occludersFBO;
Texture casterSprites;
Texture enmyAnimation;
Texture light;
ShaderProgram shadowMapShader, shadowRenderShader;
Array<Light> lights = new Array<Light>();
boolean additive = true;
boolean softShadows = true;
float xKoord = 600;
public static ShaderProgram createShader(String vert, String frag) {
ShaderProgram prog = new ShaderProgram(vert, frag);
if (!prog.isCompiled())
throw new GdxRuntimeException("could not compile shader: " + prog.getLog());
if (prog.getLog().length() != 0)
Gdx.app.log("GpuShadows", prog.getLog());
return prog;
}
public LightSystemShader() {
batch = new SpriteBatch();
ShaderProgram.pedantic = false;
//read vertex pass-through shader
final String VERT_SRC = Gdx.files.internal("data/pass.vert").readString();
// renders occluders to 1D shadow map
shadowMapShader = createShader(VERT_SRC, Gdx.files.internal("data/shadowMap.frag").readString());
// samples 1D shadow map to create the blurred soft shadow
shadowRenderShader = createShader(VERT_SRC, Gdx.files.internal("data/shadowRender.frag").readString());
//the occluders
casterSprites = new Texture("data/cat4.png");
//
enmyAnimation = new Texture("EnemyAnimations/BugIdleStand.png");
//the light sprite
light = new Texture("data/light.png");
cam = new OrthographicCamera(0,0);
cam.setToOrtho(false);
updateMaps();
font = new BitmapFont();
Gdx.input.setInputProcessor(new InputAdapter() {
public boolean touchDown(int x, int y, int pointer, int button) {
float mx = x;
float my = Gdx.graphics.getHeight() - y;
lights.add(new Light(mx, my, randomColor()));
return true;
}
public boolean keyDown(int key) {
if (key==Keys.SPACE){
clearLights();
return true;
} else if (key==Keys.A){
additive = !additive;
return true;
} else if (key==Keys.S){
softShadows = !softShadows;
return true;
}
return false;
}
});
clearLights();
}
public void renderLightSystemShader(OrthographicCamera screenCam){
screenCam = new OrthographicCamera(0,0);
screenCam.setToOrtho(false);
Gdx.gl.glClearColor(0,0,0,0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
float mx = Gdx.input.getX();
float my = Gdx.graphics.getHeight() - Gdx.input.getY();
if (additive)
batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE);
for (int i=0; i<lights.size; i++) {
Light o = lights.get(i);
if (i==lights.size-1) {
o.x = mx;
o.y = my;
}
renderLight(o, screenCam);
}
if (additive)
batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
//STEP 4. render sprites in full colour
batch.setProjectionMatrix(screenCam.combined);
batch.begin();
batch.setShader(null); //default shader
drawShaderBatch(batch);
//DEBUG RENDERING -- show occluder map and 1D shadow map
/*batch.setColor(Color.BLACK);
batch.draw(occluders, Gdx.graphics.getWidth()-lightSize, 0);
batch.setColor(Color.WHITE);
batch.draw(shadowMap1D, Gdx.graphics.getWidth()-lightSize, lightSize+5);
//DEBUG RENDERING -- show light
batch.draw(light, mx-light.getWidth()/2f, my-light.getHeight()/2f); //mouse
batch.draw(light, Gdx.graphics.getWidth()-lightSize/2f-light.getWidth()/2f, lightSize/2f-light.getHeight()/2f);
*/
//draw FPS
batch.end();
System.out.println(Gdx.graphics.getFramesPerSecond());
}
// draw Lights
void renderLight(Light o, OrthographicCamera screenCam) {
float mx = o.x;
float my = o.y;
screenCam = new OrthographicCamera(0,0);
screenCam.setToOrtho(false);
//STEP 1. render light region to occluder FBO
//bind the occluder FBO
occludersFBO.begin();
//clear the FBO
Gdx.gl.glClearColor(0f,0f,0f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//set the orthographic camera to the size of our FBO
screenCam.setToOrtho(false, occludersFBO.getWidth(), occludersFBO.getHeight());
//translate camera so that light is in the center
screenCam.translate(mx - lightSize/2f, my - lightSize/2f);
//update camera matrices
screenCam.update();
//set up our batch for the occluder pass
batch.setProjectionMatrix(screenCam.combined);
batch.setShader(null); //use default shader
batch.begin();
// ... draw any sprites that will cast shadows here ... //
batch.draw(casterSprites, 0, 0);
batch.draw(enmyAnimation,xKoord,600,300,100);
//end the batch before unbinding the FBO
batch.end();
//unbind the FBO
occludersFBO.end();
//STEP 2. build a 1D shadow map from occlude FBO
//bind shadow map
shadowMapFBO.begin();
//clear it
Gdx.gl.glClearColor(0f,0f,0f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//set our shadow map shader
batch.setShader(shadowMapShader);
batch.begin();
shadowMapShader.setUniformf("resolution", lightSize, lightSize);
shadowMapShader.setUniformf("upScale", upScale);
//reset our projection matrix to the FBO size
screenCam.setToOrtho(false, shadowMapFBO.getWidth(), shadowMapFBO.getHeight());
batch.setProjectionMatrix(screenCam.combined);
//draw the occluders texture to our 1D shadow map FBO
batch.draw(occluders.getTexture(), 0, 0, lightSize, shadowMapFBO.getHeight());
//flush batch
batch.end();
//unbind shadow map FBO
shadowMapFBO.end();
//STEP 3. render the blurred shadows
//reset projection matrix to screen
screenCam.setToOrtho(false);
batch.setProjectionMatrix(screenCam.combined);
//set the shader which actually draws the light/shadow
batch.setShader(shadowRenderShader);
batch.begin();
shadowRenderShader.setUniformf("resolution", lightSize, lightSize);
shadowRenderShader.setUniformf("softShadows", softShadows ? 1f : 0f);
//set color to light
batch.setColor(o.color);
float finalSize = lightSize * upScale;
//draw centered on light position
batch.draw(shadowMap1D.getTexture(), mx-finalSize/2f, my-finalSize/2f, finalSize, finalSize);
//flush the batch before swapping shaders
batch.end();
//reset color
batch.setColor(Color.WHITE);
//xKoord+=1;
}
void clearLights() {
lights.clear();
lights.add(new Light(Gdx.input.getX(), Gdx.graphics.getHeight()-Gdx.input.getY(), Color.WHITE));
}
static Color randomColor() {
float intensity = (float)Math.random() * 0.5f + 0.5f;
return new Color((float)Math.random(), (float)Math.random(), (float)Math.random(), intensity);
}
void drawShaderBatch(SpriteBatch batch){
batch.draw(casterSprites, 0, 0);
batch.draw(enmyAnimation,600,600,300,100);
}
void updateMaps(){
occludersFBO = new FrameBuffer(Format.RGBA8888, lightSize, lightSize, false);
occluders = new TextureRegion(occludersFBO.getColorBufferTexture());
occluders.flip(false, true);
//our 1D shadow map, lightSize x 1 pixels, no depth
shadowMapFBO = new FrameBuffer(Format.RGBA8888, lightSize, 1, false);
Texture shadowMapTex = shadowMapFBO.getColorBufferTexture();
//use linear filtering and repeat wrap mode when sampling
shadowMapTex.setFilter(TextureFilter.Linear, TextureFilter.Linear);
shadowMapTex.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
//for debugging only; in order to render the 1D shadow map FBO to screen
shadowMap1D = new TextureRegion(shadowMapTex);
shadowMap1D.flip(false, true);
}
public void doDispose(){
batch.dispose();
font.dispose();
shadowMapFBO.dispose();
shadowMapShader.dispose();
shadowRenderShader.dispose();
casterSprites.dispose();
enmyAnimation.dispose();
light.dispose();
occludersFBO.dispose();
}
}
我自己发现的 ^^ 我忘了将它添加到 void renderLights 中:
cam.position.set(positionX,positionY, 0);