Libgdx/Java ShadowMap 和 Framebuffer 使用相机更新

Posted

技术标签:

【中文标题】Libgdx/Java ShadowMap 和 Framebuffer 使用相机更新【英文标题】:Libgdx/Java ShadowMap and Framebuffer update using Cameras 【发布时间】:2016-02-11 19:21:53 【问题描述】:

我正在开发一个小型 rpg 游戏,今天我尝试添加阴影。 我在这里看到了这个教程,很好:)

Pixel Perfect Shader

所以下面的问题:

来自教程站点的测试示例,工作正常。有一个摄像头,阴影在正确的位置。

一旦我使用我的播放器精灵中的正交相机,阴影就不会出现在它应该在的位置,当我移动阴影时,它会随着我移动,他在显示屏上的同一个位置,但在世界上,阴影正在移动_/ 看起来是这样的:

SpriteSheet 应该投射阴影......但它们在错误的位置:/

这里我没动:

由于昼夜循环,天很黑......

在这里我向左移动了一点:

我不知道为什么它不起作用...我将正交相机从我的主课中交给了影子课。还是我错过了什么? :/

我的代码应该没问题,但是如果有人想在这里看到一些代码:

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();






【问题讨论】:

【参考方案1】:

我自己发现的 ^^ 我忘了把它添加到 void renderLights :

cam.position.set(positionX,positionY, 0);

【讨论】:

以上是关于Libgdx/Java ShadowMap 和 Framebuffer 使用相机更新的主要内容,如果未能解决你的问题,请参考以下文章

引擎设计跟踪 ShadowMap 细节和分析

ShadowMap

Libgdx/Java:将 GPS 转换为像素坐标后,路线旋转 90°

Direct9学习之ShadowMap

《CSM and PCF》

如何使用 Libgdx/Java 绘制文本?