OpenGL ES之十二——地球仪和VR图

概述

这是一个系列的Android平台下OpenGl ES介绍,从最基本的使用最终到VR图的展示的实现,属于基础篇。(后面针对VR视频会再有几篇文章,属于进阶篇)

OpenGL ES之一——概念扫盲
OpenGL ES之二——Android中的OpenGL ES概述
OpenGL ES之三——绘制纯色背景
OpenGL ES之四——绘制点,线,三角形
OpenGL ES之五——相机和投影,绘制等腰三角形
OpenGL ES之六——绘制矩形和圆形
OpenGL ES之七——着色器语言GLSL
OpenGL ES之八——GLES20类和Matrix类
OpenGL ES之九——相机和投影
OpenGL ES之十——纹理贴图(展示一张图片)
OpenGL ES之十一——绘制3D图形
OpenGL ES之十二——地球仪和VR图

本篇概述

前面的所有文章其实只是一小部分铺垫,从这篇文章开始才真正的进入正题,也就是做一些看起来有意思的事情,最终我们要实现VR图展示。先从一个个的概念起。

天空盒、天空穹、天空球和VR

天空盒(Sky Box)是放到场景中的一个立方体,经常是由六个面组成的立方体,并经常会随着视点的移动而移动。我们可以想象成从没有底的多面体内部看多面体的内表面
天空穹(Sky Dome)与天空盒类似,只不过它将是天空盒除底面以外的五个面换成了一个曲面,可以理解成一个半球。从球内部看球内表面,由于无论如何我们没有办法忽略由于摄像机的架设带来一定区域我们是拍摄不到的,所以总有一些区域我们要用已有的图像弥补上(比如放个logo图)。

VR视频(VR图)的本质是想让人有一种身临其境的感觉,而这种感觉就要把我们平时拍摄的单个方向变成所有方向的。所以就从一台单镜头的摄像机变成了多镜头360度几乎无死角拍摄的摄像机,然后将各个镜头的图像拼接起来呈现在我们面前就ok了。我们所做的就是将这些图像展示出来。
下面我们就从画个地球开始。

一 不会动的地球仪(看球的外表面)

下面的绘制过程其实也挺简单的,只要上一篇的绘制球体懂了,再在原来的基础之上加入纹理的绘制。

比较关键的几个点
  1. 球表面顶点坐标的计算
  2. 纹理顶点坐标的计算
  3. 着色器中变换矩阵

接下来上代码

1.1顶点着色器

#version 300 es
in vec4 aPosition;
in vec2 aCoordinate;
//顶点着色器
uniform mat4 uProjMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uModelMatrix;
uniform mat4 uRotateMatrix;
out vec2 vCoordinate;

void main(){
    gl_Position=uProjMatrix*uViewMatrix*uModelMatrix*aPosition;
    vCoordinate=aCoordinate;
}

1.2 片段着色器

#version 300 es

precision highp float;
uniform sampler2D uTexture;
in vec2 vCoordinate;
out vec4 vFragColor;
void main(){
    vec4 color=texture(uTexture,vCoordinate);
    vFragColor=color;
}

1.3 渲染器

public class EarthMapRenderer implements GLSurfaceView.Renderer {
    private static final String TAG = "EarthMapRenderer";
    private static final int BYTES_PER_FLOAT = 4;
    //顶点位置缓存
    private FloatBuffer vertexBuffer;
    //纹理顶点位置缓存
    private FloatBuffer mTexVertexBuffer;
    //渲染程序
    private int mProgram;

    //图片生成的位图
    private Bitmap mBitmap;
    //纹理id
    private int textureId;

    //向量个数
    private int vCount;

    //相关属性id
    private int mHProjMatrix;
    private int mHViewMatrix;
    private int mHModelMatrix;
    private int mHUTexture;
    private int mHPosition;
    private int mHCoordinate;

    //相机矩阵
    private final float[] mViewMatrix = new float[16];
    //投影矩阵
    private final float[] mProjectMatrix = new float[16];

    private final float[] mModelMatrix = new float[16];


    public EarthMapRenderer() {
        calculateAttribute();
    }

    //计算顶点坐标和纹理坐标
    private void calculateAttribute(){
        float radius = 1.0f; // 球的半径
        double angleSpan = Math.PI / 90f; // 将球进行单位切分的角度
        ArrayList<Float> alVertix = new ArrayList<>();
        ArrayList<Float> textureVertix = new ArrayList<>();
        for (double vAngle = 0; vAngle < Math.PI; vAngle = vAngle + angleSpan){

            for (double hAngle = 0; hAngle < 2*Math.PI; hAngle = hAngle + angleSpan){
                float x0 = (float) (radius* Math.sin(vAngle) * Math.cos(hAngle));
                float y0 = (float) (radius* Math.sin(vAngle) * Math.sin(hAngle));
                float z0 = (float) (radius * Math.cos((vAngle)));

                float x1 = (float) (radius* Math.sin(vAngle) * Math.cos(hAngle + angleSpan));
                float y1 = (float) (radius* Math.sin(vAngle) * Math.sin(hAngle + angleSpan));
                float z1 = (float) (radius * Math.cos(vAngle));

                float x2 = (float) (radius* Math.sin(vAngle + angleSpan) * Math.cos(hAngle + angleSpan));
                float y2 = (float) (radius* Math.sin(vAngle + angleSpan) * Math.sin(hAngle + angleSpan));
                float z2 = (float) (radius * Math.cos(vAngle + angleSpan));

                float x3 = (float) (radius* Math.sin(vAngle + angleSpan) * Math.cos(hAngle));
                float y3 = (float) (radius* Math.sin(vAngle + angleSpan) * Math.sin(hAngle));
                float z3 = (float) (radius * Math.cos(vAngle + angleSpan));


                float s0 = (float) (-hAngle / Math.PI/2);
                float s1 = (float) (-(hAngle + angleSpan)/Math.PI/2);

                float t0 = (float) (vAngle / Math.PI);
                float t1 = (float) ((vAngle + angleSpan) / Math.PI);

                alVertix.add(x1);
                alVertix.add(y1);
                alVertix.add(z1);
                alVertix.add(x0);
                alVertix.add(y0);
                alVertix.add(z0);
                alVertix.add(x3);
                alVertix.add(y3);
                alVertix.add(z3);

                textureVertix.add(s1);// x1 y1对应纹理坐标
                textureVertix.add(t0);
                textureVertix.add(s0);// x0 y0对应纹理坐标
                textureVertix.add(t0);
                textureVertix.add(s0);// x3 y3对应纹理坐标
                textureVertix.add(t1);

                alVertix.add(x1);
                alVertix.add(y1);
                alVertix.add(z1);
                alVertix.add(x3);
                alVertix.add(y3);
                alVertix.add(z3);
                alVertix.add(x2);
                alVertix.add(y2);
                alVertix.add(z2);

                textureVertix.add(s1);// x1 y1对应纹理坐标
                textureVertix.add(t0);
                textureVertix.add(s0);// x3 y3对应纹理坐标
                textureVertix.add(t1);
                textureVertix.add(s1);// x2 y3对应纹理坐标
                textureVertix.add(t1);
            }
        }
        vCount = alVertix.size() / 3;
        vertexBuffer = convertToFloatBuffer(alVertix);
        mTexVertexBuffer = convertToFloatBuffer(textureVertix);
    }

    //动态数组转FloatBuffer
    private FloatBuffer convertToFloatBuffer(ArrayList<Float> data){
        float[] d=new float[data.size()];
        for (int i=0;i<d.length;i++){
            d[i]=data.get(i);
        }

        ByteBuffer buffer=ByteBuffer.allocateDirect(data.size()*4);
        buffer.order(ByteOrder.nativeOrder());
        FloatBuffer ret=buffer.asFloatBuffer();
        ret.put(d);
        ret.position(0);
        return ret;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //将背景设置为灰色
        GLES30.glClearColor(0.5f,0.5f,0.5f,1.0f);

        //编译顶点着色程序
        String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_earth_shader);
        int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
        //编译片段着色程序
        String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_earth_shader);
        int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
        //连接程序
        mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
        //在OpenGLES环境中使用程序
        GLES30.glUseProgram(mProgram);

        //编译glprogram并获取控制句柄
        mHProjMatrix=GLES20.glGetUniformLocation(mProgram,"uProjMatrix");
        mHViewMatrix=GLES20.glGetUniformLocation(mProgram,"uViewMatrix");
        mHModelMatrix=GLES20.glGetUniformLocation(mProgram,"uModelMatrix");
        mHUTexture=GLES20.glGetUniformLocation(mProgram,"uTexture");
        mHPosition=GLES20.glGetAttribLocation(mProgram,"aPosition");
        mHCoordinate=GLES20.glGetAttribLocation(mProgram,"aCoordinate");

        //加载纹理
        textureId = loadTexture(App.getInstance(), R.drawable.earth);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //设置绘制窗口
        GLES30.glViewport(0, 0, width, height);

        setSize(width,height);
    }

    public void setSize(int width,int height){
        //计算宽高比
        float ratio=(float)width/height;
        //透视投影矩阵/视锥
        Matrix.perspectiveM(mProjectMatrix,0,60,ratio,1f,300f);
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix, 0, 0f, 4f,2f, 0.0f, 0.0f,0f, 0f,0f, 1f);
        //模型矩阵
        Matrix.setIdentityM(mModelMatrix,0);
		
        //Matrix.rotateM(mViewMatrix,0,180,0,0,1);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //把颜色缓冲区设置为我们预设的颜色
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);

        GLES30.glUniformMatrix4fv(mHProjMatrix,1,false,mProjectMatrix,0);
        GLES30.glUniformMatrix4fv(mHViewMatrix,1,false,mViewMatrix,0);
        GLES30.glUniformMatrix4fv(mHModelMatrix,1,false,mModelMatrix,0);

        GLES30.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES30.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);

        GLES30.glEnableVertexAttribArray(mHPosition);
        GLES30.glVertexAttribPointer(mHPosition,3,GLES20.GL_FLOAT,false,0,vertexBuffer);

        GLES30.glEnableVertexAttribArray(mHCoordinate);
        GLES30.glVertexAttribPointer(mHCoordinate,2,GLES20.GL_FLOAT,false,0,mTexVertexBuffer);

        GLES30.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);

        GLES30.glDisableVertexAttribArray(mHCoordinate);
        GLES30.glDisableVertexAttribArray(mHPosition);
    }

    private int loadTexture(Context context, int resourceId) {
        final int[] textureIds = new int[1];
        //创建一个纹理对象
        GLES30.glGenTextures(1, textureIds, 0);
        if (textureIds[0] == 0) {
            Log.e(TAG, "Could not generate a new OpenGL textureId object.");
            return 0;
        }
        final BitmapFactory.Options options = new BitmapFactory.Options();
        //这里需要加载原图未经缩放的数据
        options.inScaled = false;
        mBitmap= BitmapFactory.decodeResource(context.getResources(), resourceId, options);
        if (mBitmap == null) {
            Log.e(TAG, "Resource ID " + resourceId + " could not be decoded.");
            GLES30.glDeleteTextures(1, textureIds, 0);
            return 0;
        }
        //绑定纹理到OpenGL
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0]);

        //设置默认的纹理过滤参数
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);

        //加载bitmap到纹理中
        GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, mBitmap, 0);

        //生成MIP贴图
        GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);

        //数据如果已经被加载进OpenGL,则可以回收该bitmap
        mBitmap.recycle();

        //取消绑定纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);

        return textureIds[0];
    }
}

1.4 原图

在这里插入图片描述

1.5 效果图

在这里插入图片描述

二 会乱动的地球仪

上面我们绘制了一个不会动的地球仪,显然没什么意思。下面我们利用手机上面的传感器来让我们的地球仪动起来。
手机上的传感器种类很多,有的手机支持有的不一定支持。下面我们先看一下有哪些类型:

//加速度传感器
public static final int TYPE_ACCELEROMETER = 1;
//磁场传感器
public static final int TYPE_MAGNETIC_FIELD = 2;
//方向传感器,已废弃
public static final int TYPE_ORIENTATION = 3;
//陀螺仪
public static final int TYPE_GYROSCOPE = 4;
//光线传感器,接听电话黑屏
public static final int TYPE_LIGHT = 5;
//压力传感器
public static final int TYPE_PRESSURE = 6;
//温度传感器,已废弃
public static final int TYPE_TEMPERATURE = 7;
//近程传感器(接听电话黑屏)
public static final int TYPE_PROXIMITY = 8;
//重力传感器
public static final int TYPE_GRAVITY = 9;
//线性加速度传感器
public static final int TYPE_LINEAR_ACCELERATION = 10;
//旋转矢量传感器
public static final int TYPE_ROTATION_VECTOR = 11;
//湿度传感器
public static final int TYPE_RELATIVE_HUMIDITY = 12;
//环境温度传感器
public static final int TYPE_AMBIENT_TEMPERATURE = 13;
//未校准磁力传感器
public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14;
//旋转矢量传感器,用来探测运动而不必受到电磁干扰的影响,因为它并不依赖于磁北极
public static final int TYPE_GAME_ROTATION_VECTOR = 15;
//未校准陀螺仪传感器
public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16;
//特殊动作触发传感器
public static final int TYPE_SIGNIFICANT_MOTION = 17;
//步行探测器
public static final int TYPE_STEP_DETECTOR = 18;
//计步器
public static final int TYPE_STEP_COUNTER = 19;
//地磁旋转矢量传感器
public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20;
//心率传感器
public static final int TYPE_HEART_RATE = 21;
//倾斜探测器,隐藏的systemApi
public static final int TYPE_TILT_DETECTOR = 22;
//唤醒手势传感器,隐藏的systemApi
public static final int TYPE_WAKE_GESTURE = 23;
//快速手势,隐藏的systemApi
public static final int TYPE_GLANCE_GESTURE = 24;
//设备抬起手势,隐藏的systemApi
public static final int TYPE_PICK_UP_GESTURE = 25;
//腕关节抬起手势,隐藏的systemApi
public static final int TYPE_WRIST_TILT_GESTURE = 26;
//设备方向传感器,隐藏的systemApi
public static final int TYPE_DEVICE_ORIENTATION = 27;
//6自由度姿态传感器
public static final int TYPE_POSE_6DOF = 28;
//静止探测器
public static final int TYPE_STATIONARY_DETECT = 29;
//手势传感器
public static final int TYPE_MOTION_DETECT = 30;
//心跳传感器
public static final int TYPE_HEART_BEAT = 31;
//传感器动态添加和删除,隐藏的systemApi
public static final int TYPE_DYNAMIC_SENSOR_META = 32;

利用旋转矢量传感器,让球动起来

步骤

1.利用旋转矢量传感器提供变换矩阵,将变换矩阵传入渲染器

private void initSensor() {
    sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
    mRotation=sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
    sensorManager.registerListener(new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            setRotateMatrix(event);
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    }, mRotation, SensorManager.SENSOR_DELAY_GAME);
}

private void setRotateMatrix(SensorEvent event) {
    SensorManager.getRotationMatrixFromVector(mRotateMatrix,event.values);
    renderer.setuRotateMatrix(mRotateMatrix);
}

2.变换矩阵(uRotateMatrix)作为总的变换矩阵的一部分传入顶点着色器程序

gl_Position=uProjMatrix*uRotateMatrix*uViewMatrix*uModelMatrix*aPosition;
具体代码如下

2.1 顶点着色器

#version 300 es
in vec4 aPosition;
in vec2 aCoordinate;
//顶点着色器
uniform mat4 uProjMatrix;
uniform mat4 uRotateMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uModelMatrix;
out vec2 vCoordinate;

void main(){
    gl_Position=uProjMatrix*uRotateMatrix*uViewMatrix*uModelMatrix*aPosition;
    vCoordinate=aCoordinate;
}

2.2 片段着色器

#version 300 es

precision highp float;
uniform sampler2D uTexture;
in vec2 vCoordinate;
out vec4 vFragColor;
void main(){
    vec4 color=texture(uTexture,vCoordinate);
    vFragColor=color;
}

2.3 Activity

public class SimpleShapeActivity extends AppCompatActivity {
    private GLSurfaceView glSurfaceView;
    private EarthMapRenderer renderer;
    private SensorManager sensorManager;
    private Sensor mRotation;
    private final float[] mRotateMatrix = new float[16];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.activity_simple_shape);
        initView();
        initSensor();
    }

    private void initSensor() {
    	//传感器获取并监听相关数据
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        //List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
        mRotation=sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
        sensorManager.registerListener(new SensorEventListener() {
            @Override
            public void onSensorChanged(SensorEvent event) {
                setRotateMatrix(event);
            }

            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {

            }
        }, mRotation, SensorManager.SENSOR_DELAY_GAME);
    }

    private void setRotateMatrix(SensorEvent event) {
        //Log.e("setRotateMatrix","setRotateMatrix");
        SensorManager.getRotationMatrixFromVector(mRotateMatrix,event.values);
        renderer.setuRotateMatrix(mRotateMatrix);
    }

    private void initView() {
        glSurfaceView = findViewById(R.id.simple_shape_gls);
        glSurfaceView.setEGLContextClientVersion(3);
        renderer = new EarthMapRenderer();
        glSurfaceView.setRenderer(renderer);
    }
}

2.4 渲染器

public class EarthMapRenderer implements GLSurfaceView.Renderer {
    private static final String TAG = "EarthMapRenderer";
    private static final int BYTES_PER_FLOAT = 4;
    //顶点位置缓存
    private FloatBuffer vertexBuffer;
    //纹理顶点位置缓存
    private FloatBuffer mTexVertexBuffer;
    //渲染程序
    private int mProgram;

    //图片生成的位图
    private Bitmap mBitmap;
    //纹理id
    private int textureId;

    //向量个数
    private int vCount;

    //相关属性id
    private int mHProjMatrix;
    private int mHViewMatrix;
    private int mHRotateMatrix;
    private int mHModelMatrix;
    private int mHPosition;
    private int mHCoordinate;

    //相机矩阵
    private final float[] mViewMatrix = new float[16];
    //投影矩阵
    private final float[] mProjectMatrix = new float[16];
	//根据传感器变化的矩阵
    private float[] uRotateMatrix = new float[16];
	
    private final float[] mModelMatrix = new float[16];

    public void setuRotateMatrix(float[] uRotateMatrix) {
        //Log.e("setuRotateMatrix","setuRotateMatrix");
        this.uRotateMatrix = uRotateMatrix;
    }

    public EarthMapRenderer() {
        calculateAttribute();
    }

    //计算顶点坐标和纹理坐标
    private void calculateAttribute(){
        float radius = 1.0f; // 球的半径
        double angleSpan = Math.PI / 90f; // 将球进行单位切分的角度
        ArrayList<Float> alVertix = new ArrayList<>();
        ArrayList<Float> textureVertix = new ArrayList<>();
        for (double vAngle = 0; vAngle < Math.PI; vAngle = vAngle + angleSpan){

            for (double hAngle = 0; hAngle < 2*Math.PI; hAngle = hAngle + angleSpan){
                float x0 = (float) (radius* Math.sin(vAngle) * Math.cos(hAngle));
                float y0 = (float) (radius* Math.sin(vAngle) * Math.sin(hAngle));
                float z0 = (float) (radius * Math.cos((vAngle)));

                float x1 = (float) (radius* Math.sin(vAngle) * Math.cos(hAngle + angleSpan));
                float y1 = (float) (radius* Math.sin(vAngle) * Math.sin(hAngle + angleSpan));
                float z1 = (float) (radius * Math.cos(vAngle));

                float x2 = (float) (radius* Math.sin(vAngle + angleSpan) * Math.cos(hAngle + angleSpan));
                float y2 = (float) (radius* Math.sin(vAngle + angleSpan) * Math.sin(hAngle + angleSpan));
                float z2 = (float) (radius * Math.cos(vAngle + angleSpan));

                float x3 = (float) (radius* Math.sin(vAngle + angleSpan) * Math.cos(hAngle));
                float y3 = (float) (radius* Math.sin(vAngle + angleSpan) * Math.sin(hAngle));
                float z3 = (float) (radius * Math.cos(vAngle + angleSpan));


                float s0 = (float) (-hAngle / Math.PI/2);
                float s1 = (float) (-(hAngle + angleSpan)/Math.PI/2);

                float t0 = (float) (vAngle / Math.PI);
                float t1 = (float) ((vAngle + angleSpan) / Math.PI);

                alVertix.add(x1);
                alVertix.add(y1);
                alVertix.add(z1);
                alVertix.add(x0);
                alVertix.add(y0);
                alVertix.add(z0);
                alVertix.add(x3);
                alVertix.add(y3);
                alVertix.add(z3);

                textureVertix.add(s1);// x1 y1对应纹理坐标
                textureVertix.add(t0);
                textureVertix.add(s0);// x0 y0对应纹理坐标
                textureVertix.add(t0);
                textureVertix.add(s0);// x3 y3对应纹理坐标
                textureVertix.add(t1);

                alVertix.add(x1);
                alVertix.add(y1);
                alVertix.add(z1);
                alVertix.add(x3);
                alVertix.add(y3);
                alVertix.add(z3);
                alVertix.add(x2);
                alVertix.add(y2);
                alVertix.add(z2);

                textureVertix.add(s1);// x1 y1对应纹理坐标
                textureVertix.add(t0);
                textureVertix.add(s0);// x3 y3对应纹理坐标
                textureVertix.add(t1);
                textureVertix.add(s1);// x2 y3对应纹理坐标
                textureVertix.add(t1);
            }
        }
        vCount = alVertix.size() / 3;
        vertexBuffer = convertToFloatBuffer(alVertix);
        mTexVertexBuffer = convertToFloatBuffer(textureVertix);
    }

    //动态数组转FloatBuffer
    private FloatBuffer convertToFloatBuffer(ArrayList<Float> data){
        float[] d=new float[data.size()];
        for (int i=0;i<d.length;i++){
            d[i]=data.get(i);
        }

        ByteBuffer buffer=ByteBuffer.allocateDirect(data.size()*4);
        buffer.order(ByteOrder.nativeOrder());
        FloatBuffer ret=buffer.asFloatBuffer();
        ret.put(d);
        ret.position(0);
        return ret;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //将背景设置为灰色
        GLES30.glClearColor(0.5f,0.5f,0.5f,1.0f);

        //编译顶点着色程序
        String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_earth_shader);
        int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
        //编译片段着色程序
        String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_earth_shader);
        int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
        //连接程序
        mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
        //在OpenGLES环境中使用程序
        GLES30.glUseProgram(mProgram);

        //编译glprogram并获取控制句柄(onSurfaceCreated时调用)
        mHProjMatrix=GLES30.glGetUniformLocation(mProgram,"uProjMatrix");
        mHRotateMatrix=GLES30.glGetUniformLocation(mProgram,"uRotateMatrix");
        mHViewMatrix=GLES30.glGetUniformLocation(mProgram,"uViewMatrix");
        mHModelMatrix=GLES30.glGetUniformLocation(mProgram,"uModelMatrix");
        mHPosition=GLES30.glGetAttribLocation(mProgram,"aPosition");
        mHCoordinate=GLES30.glGetAttribLocation(mProgram,"aCoordinate");

        //加载纹理
        textureId = loadTexture(App.getInstance(), R.drawable.earth);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //设置绘制窗口
        GLES30.glViewport(0, 0, width, height);

        setSize(width,height);
    }

    public void setSize(int width,int height){
        //计算宽高比
        float ratio=(float)width/height;
        //透视投影矩阵/视锥
        Matrix.perspectiveM(mProjectMatrix,0,60,ratio,1f,300f);
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix, 0, 0f, 4f,2f, 0.0f, 0.0f,0f, 0f,0f, 1f);
        //模型矩阵
        Matrix.setIdentityM(mModelMatrix,0);

        Matrix.setIdentityM(uRotateMatrix,0);
        //Matrix.rotateM(mViewMatrix,0,40,0,0,1);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        Log.e("onDrawFrame","uRotateMatrix"+uRotateMatrix[1]);
        //把颜色缓冲区设置为我们预设的颜色
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);

        GLES30.glUniformMatrix4fv(mHProjMatrix,1,false,mProjectMatrix,0);
        GLES30.glUniformMatrix4fv(mHRotateMatrix,1,false,uRotateMatrix,0);
        GLES30.glUniformMatrix4fv(mHViewMatrix,1,false,mViewMatrix,0);
        GLES30.glUniformMatrix4fv(mHModelMatrix,1,false,mModelMatrix,0);

        GLES30.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES30.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);

        GLES30.glEnableVertexAttribArray(mHPosition);
        GLES30.glVertexAttribPointer(mHPosition,3,GLES20.GL_FLOAT,false,0,vertexBuffer);

        GLES30.glEnableVertexAttribArray(mHCoordinate);
        GLES30.glVertexAttribPointer(mHCoordinate,2,GLES20.GL_FLOAT,false,0,mTexVertexBuffer);

        GLES30.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);

        GLES30.glDisableVertexAttribArray(mHCoordinate);
        GLES30.glDisableVertexAttribArray(mHPosition);
    }

    private int loadTexture(Context context, int resourceId) {
        final int[] textureIds = new int[1];
        //创建一个纹理对象
        GLES30.glGenTextures(1, textureIds, 0);
        if (textureIds[0] == 0) {
            Log.e(TAG, "Could not generate a new OpenGL textureId object.");
            return 0;
        }
        final BitmapFactory.Options options = new BitmapFactory.Options();
        //这里需要加载原图未经缩放的数据
        options.inScaled = false;
        mBitmap= BitmapFactory.decodeResource(context.getResources(), resourceId, options);
        if (mBitmap == null) {
            Log.e(TAG, "Resource ID " + resourceId + " could not be decoded.");
            GLES30.glDeleteTextures(1, textureIds, 0);
            return 0;
        }
        //绑定纹理到OpenGL
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0]);

        //设置默认的纹理过滤参数
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);

        //加载bitmap到纹理中
        GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, mBitmap, 0);

        //生成MIP贴图
        GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);

        //数据如果已经被加载进OpenGL,则可以回收该bitmap
        mBitmap.recycle();

        //取消绑定纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);

        return textureIds[0];
    }
}

2.5 结果

我们晃动手机就会有下面的结果
在这里插入图片描述

三 查看球的内表面

将上面查看球外表面的参数稍作修改即可,只将下面代码替换上面相应代码即可:

		Matrix.perspectiveM(mProjectMatrix,0,90,ratio,0f,300f);
        Matrix.setLookAtM(mViewMatrix, 0, 0f, 0.0f,0.0f, 0.0f, 0.0f,1.0f, 0f,1.0f, 0.0f);
上面我们将近平面也就是接受投影的面设置为0和球的球心在一起;同时我们将眼睛的位置也放在球心

下面是效果图,我们将世界地图贴在球内部去查看:
在这里插入图片描述

四 VR图

上面我们发现好奇怪,我们为什么要将世界地图贴在球里面呢,其实是为了我们的VR做准备。我们只需要将上面的世界地图换成我们要展示的一帧帧VR图像不就完成了VR视频的渲染了吗?我们先来一个简单的,就是将一个VR图展示出来,后面文章中我们再将视频展示出来。

只要将上面的世界地图换成VR图就好了

找了一张如下图(图片来自于网络):
在这里插入图片描述
由于图片自身分辨率比较低,所以展示出来的效果不是很好,如下图
在这里插入图片描述

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值