如何正确渲染SkyBox?

时间:2016-05-26 13:30:55

标签: java libgdx

我使用此问题的解决方案使用6张图像绘制3D SKybox视图。问题可在此处找到。 LibGDX 0.9.9 - Apply cubemap in environment。该程序工作,但似乎使用了大量的处理能力,我的CPU风扇开始运行。我渲染3D天空盒的方式有问题,还是有更好的方法。我实现天空盒的方式是否正确。这是我的代码。

创建环境的类位于

之下
protected final Pixmap[] data = new Pixmap[6];  
protected ShaderProgram shader;

protected int u_worldTrans;
protected Mesh quad;
private Matrix4 worldTrans;
private Quaternion q;

protected String vertexShader = " attribute vec3 a_position; \n"+
        " attribute vec3 a_normal; \n"+
        " attribute vec2 a_texCoord0; \n"+          
        " uniform mat4 u_worldTrans; \n"+                   
        " varying vec2 v_texCoord0; \n"+
        " varying vec3 v_cubeMapUV; \n"+            
        " void main() { \n"+
        "     v_texCoord0 = a_texCoord0;     \n"+
        "     vec4 g_position = u_worldTrans * vec4(a_position, 1.0); \n"+
        "     v_cubeMapUV = normalize(g_position.xyz); \n"+
        "     gl_Position = vec4(a_position, 1.0); \n"+
        " } \n";

protected String fragmentShader = "#ifdef GL_ES \n"+
        " precision mediump float; \n"+
        " #endif \n"+           
        " uniform samplerCube u_environmentCubemap; \n"+            
        " varying vec2 v_texCoord0; \n"+
        " varying vec3 v_cubeMapUV; \n"+            
        " void main() {      \n"+
        "   gl_FragColor = vec4(textureCube(u_environmentCubemap, v_cubeMapUV).rgb, 1.0);   \n"+
        " } \n";

public String getDefaultVertexShader(){
    return vertexShader;
}

public String getDefaultFragmentShader(){
    return fragmentShader;
}

public EnvironmentCubemap (Pixmap positiveX, Pixmap negativeX, Pixmap positiveY, Pixmap negativeY, Pixmap positiveZ, Pixmap negativeZ) {
    data[0]=positiveX;
    data[1]=negativeX;

    data[2]=positiveY;
    data[3]=negativeY;

    data[4]=positiveZ;
    data[5]=negativeZ;

    init();   
}

public EnvironmentCubemap (FileHandle positiveX, FileHandle negativeX, FileHandle positiveY, FileHandle negativeY, FileHandle positiveZ, FileHandle negativeZ) {
    this(new Pixmap(positiveX), new Pixmap(negativeX), new Pixmap(positiveY), new Pixmap(negativeY), new Pixmap(positiveZ), new Pixmap(negativeZ));
}

//IF ALL SIX SIDES ARE REPRESENTED IN ONE IMAGE
public EnvironmentCubemap (Pixmap cubemap) {        
    int w = cubemap.getWidth();
    int h = cubemap.getHeight();
    for(int i=0; i<6; i++) data[i] = new Pixmap(w/4, h/3, Format.RGB888);
    for(int x=0; x<w; x++)
        for(int y=0; y<h; y++){
            //-X
            if(x>=0 && x<=w/4 && y>=h/3 && y<=h*2/3) data[1].drawPixel(x, y-h/3, cubemap.getPixel(x, y));
            //+Y
            if(x>=w/4 && x<=w/2 && y>=0 && y<=h/3) data[2].drawPixel(x-w/4, y, cubemap.getPixel(x, y));
            //+Z
            if(x>=w/4 && x<=w/2 && y>=h/3 && y<=h*2/3) data[4].drawPixel(x-w/4, y-h/3, cubemap.getPixel(x, y));
            //-Y
            if(x>=w/4 && x<=w/2 && y>=h*2/3 && y<=h) data[3].drawPixel(x-w/4, y-h*2/3, cubemap.getPixel(x, y));
            //+X
            if(x>=w/2 && x<=w*3/4 && y>=h/3 && y<=h*2/3) data[0].drawPixel(x-w/2, y-h/3, cubemap.getPixel(x, y));
            //-Z
            if(x>=w*3/4 && x<=w && y>=h/3 && y<=h*2/3) data[5].drawPixel(x-w*3/4, y-h/3, cubemap.getPixel(x, y));
        }
    cubemap.dispose();
    cubemap=null;
    init();     
}

private void init(){        
     shader = new ShaderProgram(vertexShader, fragmentShader);
        if (!shader.isCompiled())
            throw new GdxRuntimeException(shader.getLog());

     u_worldTrans = shader.getUniformLocation("u_worldTrans");

     quad = createQuad();      
     worldTrans = new Matrix4();         
     q = new Quaternion();

     initCubemap();
} 

private void initCubemap(){
    //bind cubemap
    Gdx.gl.glBindTexture(GL.GL_TEXTURE_CUBE_MAP, 0);
    Gdx.gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL30.GL_RGB, data[0].getWidth(), data[0].getHeight(), 0, GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE, data[0].getPixels());
    Gdx.gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL30.GL_RGB, data[1].getWidth(), data[1].getHeight(), 0, GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE, data[1].getPixels());

    Gdx.gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL30.GL_RGB, data[2].getWidth(), data[2].getHeight(), 0, GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE, data[2].getPixels());
    Gdx.gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL30.GL_RGB, data[3].getWidth(), data[3].getHeight(), 0, GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE, data[3].getPixels());

    Gdx.gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL30.GL_RGB, data[4].getWidth(), data[4].getHeight(), 0, GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE, data[4].getPixels());
    Gdx.gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL30.GL_RGB, data[5].getWidth(), data[5].getHeight(), 0, GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE, data[5].getPixels());

    Gdx.gl.glGenerateMipmap(GL.GL_TEXTURE_CUBE_MAP);
    Gdx.gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR);

    Gdx.gl.glTexParameteri ( GL.GL_TEXTURE_CUBE_MAP, GL30.GL_TEXTURE_MIN_FILTER,GL30.GL_LINEAR_MIPMAP_LINEAR );     
    Gdx.gl.glTexParameteri ( GL.GL_TEXTURE_CUBE_MAP, GL30.GL_TEXTURE_MAG_FILTER,GL30.GL_LINEAR );
    Gdx.gl.glTexParameteri ( GL.GL_TEXTURE_CUBE_MAP, GL30.GL_TEXTURE_WRAP_S, GL30.GL_CLAMP_TO_EDGE );
    Gdx.gl.glTexParameteri ( GL.GL_TEXTURE_CUBE_MAP, GL30.GL_TEXTURE_WRAP_T, GL30.GL_CLAMP_TO_EDGE );   

    Gdx.gl.glGenerateMipmap(GL30.GL_TEXTURE_CUBE_MAP);
}



public void render(Camera camera){

    //SPECIAL THANKS TO Jos van Egmond 
    camera.view.getRotation( q, true );
    q.conjugate();

    ///////////////////////////////////    
    worldTrans.idt();
    worldTrans.rotate(q);

    shader.begin();     
    shader.setUniformMatrix(u_worldTrans, worldTrans.translate(0, 0, -1));

    quad.render(shader, GL30.GL_TRIANGLES);
    shader.end();
}

public Mesh createQuad(){
    Mesh mesh = new Mesh(true, 4, 6, VertexAttribute.Position(), VertexAttribute.  ColorUnpacked(), VertexAttribute.TexCoords(0));
        mesh.setVertices(new float[] 
        {-1f, -1f, 0, 1, 1, 1, 1, 0, 1,
        1f, -1f, 0, 1, 1, 1, 1, 1, 1,
        1f, 1f, 0, 1, 1, 1, 1, 1, 0,
        -1f, 1f, 0, 1, 1, 1, 1, 0, 0});
        mesh.setIndices(new short[] {0, 1, 2, 2, 3, 0});
        return mesh;
}

@Override
public void dispose() {
    shader.dispose();
    quad.dispose();
    for(int i=0; i<6; i++) 
        data[i].dispose();
}

在我的主要java类中,我有

   env = new EnvironmentCubemap(Gdx.files.internal("assets/skybox/back.jpg"), Gdx.files.internal("assets/skybox/front.jpg"),
                    Gdx.files.internal("assets/skybox/top.jpg"), Gdx.files.internal("assets/skybox/bottom.jpg"),
                    Gdx.files.internal("assets/skybox/left.jpg"), Gdx.files.internal("assets/skybox/right.jpg"));

和我的渲染方法

modelBatch.begin(cam);
        modelBatch.flush();
        modelBatch.render(instance);
        env.render(modelBatch.getCamera());
        modelBatch.end();

        // Stage
        stage.act();
        stage.draw();

1 个答案:

答案 0 :(得分:2)

不要使用立方体贴图来制作像天空盒这样的东西,这只会让你感到复杂并且无法获得任何东西。只需在建模应用程序中创建一个框(或者如果您愿意,可以通过创建6个矩形来使用ModelBuilder,您不应该这样做并在其上映射纹理。就像您使用的任何其他型号一样。不要忘记你想从内部看它,所以根据你的建模应用,你可能需要翻转法线或顶点绕组。确保它足够大(如果需要,你可以使用不同的相机)。如果您要移动相机很多,那么您可能希望盒子跟随相机,这样您就无法到达天空的尽头。最后,确保在不指定环境的情况下渲染它,因此它不会受到任何照明等的影响。

当然,无论您是否使用立方体贴图,使用天空框都有其局限性。因此,我个人推荐使用skysphere或skydome。本教程也可能有所帮助:https://xoppa.github.io/blog/loading-a-scene-with-libgdx/