Opengl的结构和流程
glactivetexture和glbindtexture,前者用于选择一个texture unit,后者表示绑定一个texture,并且它作用于当前选择的texture unit。
可以这样简单的理解为:显卡中有N个纹理单元(具体数目依赖你的显卡能力),每个纹理单元(GL_TEXTURE0、GL_TEXTURE1等)都有GL_TEXTURE_1D、GL_TEXTURE_2D等,默认情况下当前活跃的纹理单元为0。glActiveTextue 并不是激活纹理单元,而是选择当前活跃的纹理单元。当绑定纹理目标时,所作用的是当前活跃的纹理单元。
上传cpu的memory到gpu上:
for (int i=0; i<s->planes; i++)
{
GLubyte* dst;
int k = (!!i);
// Bind current pbo
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, s->pbo_sub[i]);
dst = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
av_assert0(dst);
memcpy(dst, sub->data[i], sub->linesize[i]*sub->height>>k);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
// Select texture unit i as the active unit.
glActiveTexture(GL_TEXTURE0+i);
// bind the ith texture.
glBindTexture(GL_TEXTURE_2D, s->sub_texture[i]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, sub->height>>k);
glPixelStorei(GL_UNPACK_ROW_LENGTH, sub->linesize[i]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sub->width>>k, sub->height>>k, GL_RED, GL_UNSIGNED_BYTE, 0);
}
Opengl里的四维变换矩阵
对三维坐标做变换时常用到四维变换矩阵,右手坐标系,对于opengl来说,可以理解坐标轴位置为,X对应左,Y对应上,Z对应前。
如图,红色是对x轴坐标的变换,绿色是对y轴,紫色是对z轴。最右面的(m12,m13,m14)是用于translation transformation, glTranslatef().
PS:glTranslatef(x,y,z)函数是沿直线移动的函数。比如glTranslatef(1,-1,2)的意思,就是沿x轴方向移动1,沿y轴方向动-1,沿z轴方向移动2。注意是相对于当前所在位置进行移动。对于全景视频我们是不需要做移动的。
最后一个m15是用于齐次坐标,投影变换的时候会用到。
变换时,opengl向量为列向量,所以应该矩阵在左边,输入向量在右边
常见几种变换矩阵,比如x轴旋转:
坐标系平移:
缩放:
Opengl里的投影变换,以及MVP
Shader
private final String mVertexShader =
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"attribute vec4 aPosition;\n" +
"attribute vec4 aTextureCoord;\n" +
"varying vec2 vTextureCoord;\n" +
"void main() {\n" +
" gl_Position = uMVPMatrix * aPosition;\n" +
" vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
"}\n";
private final String mFragmentShader =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform samplerExternalOES sTexture;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
"}\n";
1. uSTMatrix,对输出图像坐标的变换,在全景视频播放时我们暂时不做变换,所以设置为单位矩阵即可。
2. aPosition,对应应该输入verticesBuffer,对应顶点在三维坐标系中的位置,每一个顶点由3个float值标识坐标,设置时第二个参数为3
GLES30.glVertexAttribPointer(maPositionHandle, 3, GLES30.GL_FLOAT, false, 0, mVerticesBuffer);
3. aTextureCoord,对应输入二维图像上的点,每一个点由2个float值标识坐标,且与mVerticesBuffer中的顶点按顺序一一对应
GLES30.glVertexAttribPointer(maTextureHandle, 2, GLES30.GL_FLOAT, false, 0, mTexCoordinateBuffer);
4. vTextureCoord,为了传给fragment shader的二维变量,由于uSTMatrix目前设置是单位矩阵,所以这里其值就是输入2D图像的坐标。
对于fragment shader,输入参数意义如下:
vTextureCoord,为了传递从vertex shader送来的2D图像坐标。
sTexture,是输入图像实际存储的texture,也就是实际数据的读取内容。它的类型samplerExternalOES将SurfaceTexture作为OpenGL外部纹理,使用GL_TEXTURE_EXTERNAL_OES作为纹理模板。对应输入的OES texture需要设置:
mTextureID = textures[0];
GLES30.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
MVP矩阵
opengl在画全景图时最后会调用glDrawElements或者glDrawArrays,而投影映射的shader中一般都会有一个MVP矩阵作为输入,uMVPMatrix。这个矩阵表示了画图时的视角。它由三个矩阵按PVM的顺序相乘得到: view矩阵 X model矩阵 X projection矩阵。
multiply(pMatrix, vMatrix, mvpMatrix); // p左乘v
multiply(mvpMatrix, mMatrix, mvpMatrix); // m左乘pv
它们的作用简单来说就是:
projection矩阵:用于最终投影出2D输出平面上的坐标
view矩阵:用于设定视角方向,也就是把world space变换到eye space
model矩阵:将物体的坐标从object space变换到画图时opengl用的3D空间,world space。如果本身输入就在opengl的空间,比如全景视频,则设置为单位矩阵即可,因为不需要这部变换。
projection矩阵
opengl在显示时,首先通在3D空间画出整个图像,对于没有特殊变换的全景视频来说就是画一个球。而显示时显示的是一个2D的图像,这时就需要投影变换,frustum。
函数原型:
/*
* @param m the float array that holds the output perspective matrix
* @param offset the offset into float array m where the perspective
* matrix data is written
* @param left
* @param right
* @param bottom
* @param top
* @param near
* @param far
*/
public static void frustumM(float[] m, int offset,
float left, float right, float bottom, float top,
float near, float far)
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -ratio, ratio, 1f, 10f);
第一个参数是输出的4x4 float 矩阵,mProjMatrix,作为投影转换矩阵。第二个参数作为偏移一般情况为0,也可以根据矩阵内存调整。第3~6个参数用于调整观测近平面的左右上下边缘,第五个参数near表示近面到视角的距离,最后一个参数表示远面到视角的距离。近面与远面投影直观理解见上面两图,小于近面或者大于远面的部分是不会投影出来的。
view矩阵
view矩阵相当于在3D空间中放置一个照相机,摆在某个位置,并且调整相机方向对准某个向量。需要调用的函数原型为:
/**
* Defines a viewing transformation in terms of an eye point, a center of
* view, and an up vector.
*
* @param rm returns the result
* @param rmOffset index into rm where the result matrix starts
* @param eyeX eye point X
* @param eyeY eye point Y
* @param eyeZ eye point Z
* @param centerX center of view X
* @param centerY center of view Y
* @param centerZ center of view Z
* @param upX up vector X
* @param upY up vector Y
* @param upZ up vector Z
*/
public static void setLookAtM(float[] rm, int rmOffset,
float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ, float upX, float upY,
float upZ)
//demo, default lookX=0, lookY=0, lookZ=-1
Matrix.setLookAtM(mVMatrix, 0,
0f, 0f, 0f,
lookX, lookY, lookZ,
0f, 1f, 0.0f);
输出矩阵为mVMatrix,同样是一个4x4 float矩阵。rmOffset参数是写入矩阵偏移,一般单独分给view矩阵的话设置0。eyeX,Y,Z分别表示照相机放在三维坐标中哪个点,对于全景视频来说就是原点(0,0,0)。centerX,Y,Z表示相机的朝向,它们和eye之间的向量就是相机角度,demo里输入朝向z轴负方向。最后三个参数upX,Y,Z表示相机自己头朝哪个方向,demo里设置相机房子XZ平面上,相机顶端正对着Y轴正方向。
model矩阵
对于全景视频来说,是直接贴在opengl的三维空间上的球,所以不需要进行object space到world space的转换,这个矩阵直接设置为单位矩阵就可以了。
Matrix.setIdentityM(mMMatrix, 0);
设置MVP给shader
在画图前,只需要按PVM的顺序相乘得到MVP矩阵,然后传递给shader,即可调用draw来画图了。
当然,别忘了先把vertex,coords还有indices算好传给shader。
muMVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
GLES30.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
indexBuffer.position(0);
GLES30.glDrawElements(GLES30.GL_TRIANGLES, mNumIndices, GLES30.GL_UNSIGNED_SHORT, indexBuffer);
参考
http://www.songho.ca/opengl/gl_transform.html#modelview
http://www.songho.ca/opengl/gl_projectionmatrix.html