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图展示。先从一个个的概念起。
天空盒(Sky Box)
是放到场景中的一个立方体,经常是由六个面组成的立方体,并经常会随着视点的移动而移动。我们可以想象成从没有底的多面体内部看多面体的内表面
。
天空穹(Sky Dome)
与天空盒类似,只不过它将是天空盒除底面以外的五个面换成了一个曲面,可以理解成一个半球。从球内部看球内表面,由于无论如何我们没有办法忽略由于摄像机的架设带来一定区域我们是拍摄不到的,所以总有一些区域我们要用已有的图像弥补上(比如放个logo图)。
VR视频(VR图)的本质是想让人有一种身临其境的感觉,而这种感觉就要把我们平时拍摄的单个方向变成所有方向的。所以就从一台单镜头的摄像机变成了多镜头360度几乎
无死角拍摄的摄像机,然后将各个镜头的图像拼接起来呈现在我们面前就ok了。我们所做的就是将这些图像展示出来。
下面我们就从画个地球开始。
下面的绘制过程其实也挺简单的,只要上一篇的绘制球体懂了,再在原来的基础之上加入纹理的绘制。
比较关键的几个点
:接下来上代码
#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;
}
#version 300 es
precision highp float;
uniform sampler2D uTexture;
in vec2 vCoordinate;
out vec4 vFragColor;
void main(){
vec4 color=texture(uTexture,vCoordinate);
vFragColor=color;
}
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 alVertix = new ArrayList<>();
ArrayList 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 data){
float[] d=new float[data.size()];
for (int i=0;i
上面我们绘制了一个不会动的地球仪,显然没什么意思。下面我们利用手机上面的传感器来让我们的地球仪动起来。
手机上的传感器种类很多,有的手机支持有的不一定支持。下面我们先看一下有哪些类型:
//加速度传感器
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 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;
具体代码如下
#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;
}
#version 300 es
precision highp float;
uniform sampler2D uTexture;
in vec2 vCoordinate;
out vec4 vFragColor;
void main(){
vec4 color=texture(uTexture,vCoordinate);
vFragColor=color;
}
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 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);
}
}
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 alVertix = new ArrayList<>();
ArrayList 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 data){
float[] d=new float[data.size()];
for (int i=0;i
我们晃动手机就会有下面的结果
将上面查看球外表面的参数稍作修改即可,只将下面代码替换上面相应代码即可:
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图展示出来,后面文章中我们再将视频展示出来。