OpenGL ES for Android (立方体贴图)

简介

我们前面学过纹理贴图,我们可以用6个2D纹理的纹理构建成立方体,而立方体贴图本身就是包含6个2D纹理的纹理,它优势在于可以通过一个方向向量来进行索引/采样。只要在立方体贴图的中心点,就能使用立方体的实际位置向量来对立方体贴图进行采样。

创建立方体贴图

与2D纹理区别不大,创建立方体贴图同样是创建纹理,只是我们的glBindTexture的方法的参数要变为GLES20.GL_TEXTURE_CUBE_MAP,然后同样要处理环绕和过滤方式。与2D纹理不同的是,它需要用6张图片来分别设置立方体的六个面,同样是使用GLUtils.texImage2D方法,它的target参数如下表

我们把创建立方体贴图的同样封装一下,代码如下:

    /**
     * 立方体贴图
     * @param context context
     * @param resIds  贴图集合,顺序是:
     *                <ul><li>右{@link GLES20#GL_TEXTURE_CUBE_MAP_POSITIVE_X}</li>
     *                <li>左{@link GLES20#GL_TEXTURE_CUBE_MAP_NEGATIVE_X}</li>
     *                <li>上{@link GLES20#GL_TEXTURE_CUBE_MAP_POSITIVE_Y}</li>
     *                <li>下{@link GLES20#GL_TEXTURE_CUBE_MAP_NEGATIVE_Y}</li>
     *                <li>后{@link GLES20#GL_TEXTURE_CUBE_MAP_POSITIVE_Z}</li>
     *                <li>前{@link GLES20#GL_TEXTURE_CUBE_MAP_NEGATIVE_Z}</li></ul>
     * @return int
     */
    public static int createTextureCube(Context context, int[] resIds) {
        if (resIds != null && resIds.length >= 6) {
            int[] texture = new int[1];
            //生成纹理
            GLES20.glGenTextures(1, texture, 0);
            checkGlError("glGenTexture");
            //生成纹理
            GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, texture[0]);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
            if (OpenGLVersion > 2 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES30.GL_TEXTURE_WRAP_R, GLES20.GL_CLAMP_TO_EDGE);
            }
 
            Bitmap bitmap;
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inScaled = false;
            for (int i = 0; i < resIds.length; i++) {
                bitmap = BitmapFactory.decodeResource(context.getResources(),
                        resIds[i], options);
                if (bitmap == null) {
                    LogUtil.w("Resource ID " + resIds[i] + " could not be decoded.");
                    GLES20.glDeleteTextures(1, texture, 0);
                    return 0;
                }
                GLUtils.texImage2D(GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, bitmap, 0);
                bitmap.recycle();
            }
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
            return texture[0];
        }
        return 0;
    }

着色器的代码同样需要修改,我们需要使用samplerCube接收我们的立方体贴图,使用textureCube来生成立方体贴图,同样的我们需要定义六个面的顶点坐标和纹理坐标。片段着色器的代码如下:

precision mediump float;
varying vec3 TexCoord; // 代表3D纹理坐标的方向向量
uniform samplerCube skybox; // 立方体贴图的纹理采样器
 
void main() {
    gl_FragColor = textureCube(skybox, TexCoord);
}

天空盒

想象一下我们创建好一个立方体贴图,我们把视角设置在立方体贴图的中间,这时我们移动视角方向,就像是在一个立方体的房间内向各个方向观望。我们可以在这个网站(http://www.custommapmakers.org/skyboxes.php)上找到各种素材,暂时我们使用下面的六张图片实现立方体贴图:

然后绘制一个盒子放到我们的视角内,关键的代码如下:

    @Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);
 
        drawTexture();
        GLES20.glDepthFunc(GLES20.GL_LEQUAL);
        drawSkyBox();
        GLES20.glDepthFunc(GLES20.GL_LESS);
    }

此时的效果如下图:

我们想要移动我们的视角,使用触摸事件的话有些繁琐了,这里我们使用手机自带的传感器中的旋转矢量功能,我们移动手机就能移动在天空盒中移动视角。在onSensorChanged中使用getRotationMatrixFromVector将旋转矢量转换为旋转矩阵,随后传入我们的Renderer中计算即可。

源码地址:
https://github.com/jklwan/OpenGLProject/blob/master/sample/src/main/java/com/chends/opengl/renderer/advanced/opengl/CubeMapsRenderer.java,可以自己测试效果。本章对应文档https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/06%20Cubemaps/

如有不足可以,相互交流,看过的可点个赞关注下。

猜你喜欢

转载自blog.csdn.net/ajsliu1233/article/details/106386279