HJ详解Android OpenGL ES2.0 (例程)

这次主要围绕这代码来补充一些GLES知识.

绘制正方体,球形以及旋转

在前面我们绘制了2D对象,他们的3D扩展也非常容易,具体的代码以上传至我的github,详见level0部分代码.这里我们主要谈谈之前没有讨论的视角坐标系设置,以及物体的旋转.

在前面的学习中,我们设置的长方形坐标是(-1,-1)(1,-1)(-1,1)(1,1),按理说他应该是一个正方形,但是由于显示时x,y方向的坐标比例不一样,看以来就是一个长方形,我们可以通过比例变换,将长方形的宽扩张,高度保持不变,来达到x,y方向的比例保持一直

// 设置投影矩阵
Matrix.frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far);

我们设置left,right,bottom,top可以设置显示的x,y比例,near,far与摄像机坐标系的位置有关,如果摄像机坐标到物体的距离小于near,或者大于far,物体将是不可见的.near还与摄像机的焦距有关.(暂且这样理解吧)

摄像机坐标系,涉及到摄像机的位置[x,y,z],摄像机的roll,pitch,yaw,以及焦距,上面我们已经设置了焦距

Matrix.setLookAtM(float[] rm, int rmOffset, float eyeX, float eyeY, float eyeZ,
            float centerX, float centerY, float centerZ, float upX, float upY,
            float upZ);

上面的函数设置了摄像机的坐标[eyeX,eyeY,eyeZ],由摄像机坐标和目标位置[centerX,centerY,centerZ]实际上确定了yaw和picth,通过upX,upY,upZ设置roll.

通过 投影矩阵与视角矩阵相乘得到一个联合矩阵,这个联合矩阵再与旋转矩阵相乘就可以旋转物体,与缩放矩阵相乘,就能缩放物体,与平移矩阵相乘,可以平移物体.将最终的矩阵传入Shader,在Shader中与顶点的位置相乘.实现各种效果

Matrix.multiplyMM(mVPMatrix,0,mProjectMatrix,0,mViewMatrix,0); // 投影矩阵与摄像机矩阵相乘 得到  视角矩阵
Matrix.setRotateM(mRotateMatrix,0,mAlpha,0,1,0); // 设置一个旋转矩阵 绕 y 轴旋转 mAlpha 度
Matrix.multiplyMM(mat,0,mVPMatrix,0,mRotateMatrix,0); // 视角矩阵与旋转矩阵相乘 得到最终矩阵
    private final String vertexShaderCode =
            "uniform mat4 vMatrix; " +
            "varying vec4 vColor;" +
            "attribute vec4 vPosition;" +
            "void main(){" +
            "    gl_Position=vMatrix*vPosition;" +
            "    float color;" +
            "        color=(vPosition.z+1.0)/2.0;" +
            "    vColor=vec4(color,color,color,1.0);" +
            "}";

我们看上面这段代码,vMatrix就是实现了各种变换的矩阵,我在有的地方看到,OpenGL中还有一个物体坐标系,而OpenGL ES2.0应该是没有这个的,其实也没有必要,所有的物体乘以对应的矩阵,就能实现各自的坐标了,我们引申一下可以看到,不管是摄像机坐标变换,还是物体平移缩放旋转,最终改变的都是物体的坐标,也就是说,OpenGL ES中摄像机坐标是固定的,通过改变物体的相对位置,改变视图中的效果.

这一节的内容包含一些数学知识,矩阵相关的知识请查阅<<线性代数>>,投影相关的可以参考<<计算机视觉中的多视图几何>>,如果对理论要求不高,可以多实践一下.

图像滤镜

例程在我的github,详见level1部分代码.主要是对GLSL的一些应用,不涉及其他新知识.看程序应该没什么理解不了的.

压缩纹理

例程在我的github,详见level2部分代码.

这个例程我们使用了ETC1压缩纹理,由于ETC1不支持RGBA,我们将Alpha单独存储,使用两个纹理单元,分别存储RGB和A通道,在fragmentShader中将两个纹理合并.

FBO

例程在我的github,详见level3和level4部分代码.

我们前面的例子全部都将图像绘制到了手机屏幕上,对于有些情况,我们并不想绘制到屏幕上,这时我们只要生成一个FrameBuffer,往FrameBuffer上挂载Texture2D和RenderBuffer,在渲染时指明使用我们新生成的FrameBuffer,简简单单的就重定向了输出.

Level3中的代码只改了FBORender部分,依然依赖于GLSurfaceView提高的EGL环境.Level3中我们彻底摒弃了GLSurfaceView,自己构建EGL环境.

EGL环境推荐的博文:https://blog.csdn.net/u012515661/article/details/55213460

相机预览

例程在我的github,详见level5部分代码.

相机的程序这里就不多说了,相机预览的格式输出默认是NV21,一般直接预览我们会将相机图像输出到SurfaceView,TextureView等上,因为我们要先处理,所以我们让相机图像输出到SurfaceTexture上,SurfaceTexture又能够将图像更新到纹理单元

 private static final String fragmentSharderCode =
            "#extension GL_OES_EGL_image_external : require\n"+ // 注意这里必须加上\n
            "precision mediump float;" +
            "uniform samplerExternalOES vTexture;" +
            "varying vec2 aCoord;" +
            "void main() {" +
            "   gl_FragColor = texture2D( vTexture, aCoord);" +
            "}";

最简单的一个NV21纹理的fragmentSharder如上,使用Android中的GLES扩展,使用samplerExternalOES纹理.不要忘记require后面的\n.

Level6是一个综合性的代码,使用FrameBuffer重定向了纹理输出,使用自建EGL代替GLSurfaceView,使用SurfaceView作为最终的输出界面.并且还在GPU中将NV21格式转换成RGBA格式.

图像混合

例程在我的github,详见level7部分代码.

在压缩纹理部分我们已经完成了两个图层的混合,实际上GLES自带了图层混合功能,这个例程展示了如何使用Blend,例程很简单,看看代码就能理解

光照

例程在我的github,详见level8部分代码.

OpenGL ES1.0是固定管线,自带了一些函数设置光照,OpenGL ES2.0是是可编程管线,没有这些函数,我们通过公式计算每个点的亮度,实际上也就是计算每个顶点/面的RGBA值,现在返回看看我们的Level0中的球形,是不是已经很像光照效果了,真正的光照相关,只是颜色的计算公式不一样.

如果你想实现真正的遮挡阴影效果,暂时没有这个例程,难度也比较大

待续

加载3D模型,也是以前知识的总结,主要需要了解3D模型文件的内容含义

我也刚刚入门GLES,以后如有更深入接触,再做补充

最后一个Demo,例程在我的github,详见level9部分代码.(加载的Pikachu面部有裂缝,我也有点烦去找问题的原因了,哪位要是知道了为什么还请告诉我,不胜感激).

猜你喜欢

转载自blog.csdn.net/HUAJUN998/article/details/81203737