网站排名推广视频建设网站首页

当前位置: 首页 > news >正文

网站排名推广,视频建设网站首页,wordpress的登陆地址,重庆三环建设监理咨询有限公司网站事物的本质是事物本身所固有的、深藏于‌现象背后并决定或支配现象的方面‌。 还记得我们上一篇绘制的三角形吗#xff0c;我们确实能够顺利用OpenGL ES绘制出图形了#xff0c;这是一个好的开始#xff0c;但这还远远不够。我们定义的坐标是正三角形#xff0c;但是绘制出… 事物的本质是事物本身所固有的、深藏于‌现象背后并决定或支配现象的方面‌。 还记得我们上一篇绘制的三角形吗我们确实能够顺利用OpenGL ES绘制出图形了这是一个好的开始但这还远远不够。我们定义的坐标是正三角形但是绘制出来三角形却拉升了横屏显示会压缩。 为了方便大家我们将OpenGL ES坐标系图再次贴出。OpenGL ES的坐标系是一个正方形他的四个顶点分别对应GLSurfaceView的四个顶点这个是定死的我们无法改变那要怎么才能让我的三角形变成等边三角形呢既然坐标系为正方形那么我们让GLSurfaceView也为正方形是否可行呢

  1. 方式一设置GLSurfaceView宽高相等 注意这里有个误区就是OpenGL ES坐标顶点对应的是GLSurfaceView的四个顶点而不是屏幕的四个顶点。所以好多文章说变形拉升什么的甚至是官方文档都和手机屏幕扯上关系。我现在可以明确的告诉大家的是这和手机的屏幕一点关系也没有。 那为什么又要说和屏幕有关系其本质是将GLSurfaceView的宽高使用了match_parent导致GLSurfaceView大小和屏幕相同而已但是变形拉升只和GLSurfaceView的大小有关GLSurfaceView如果不是一个正方形那么画出的图形就会变形。 既然我们知道了上述缘由后最简单的方式就有了就是设置GLSurfaceView的宽高相等即可: com.android.xz.opengldemo.view.TriangleGLSurfaceViewandroid:layout_width400dpandroid:layout_height400dp/运行看看效果是否可行 不出我们所料果然是行得通的 但是世界的运行往往不是我们人为能控制的GLSurfaceView的宽高往往不是正方形他要和应用相结合他可能是游戏全屏界面也可能是某个显示视频的预览界面亦或是嵌到某个犄角旮旯充当不重要的视图这个时候我们就引入了下面的方式。
  2. 方式二修改顶点坐标数据 当GLSurfaceView的宽高不一致的时我们该如何是好就比如我们现在GLSurfaceView是全屏的。 我们来分析下GLSurfaceView目前全屏后视图高被拉升了原本三角形的top顶点到底边的垂直距离是0.866也就是说我们按照GLSurfaceView拉升比缩放这个距离是不是也是可行的 // 三角形三个点的坐标逆时针绘制static float triangleCoords[] { // 坐标逆时针顺序0.0f, 0.616f, 0.0f, // top-0.5f, -0.25f, 0.0f, // bottom left0.5f, -0.25f, 0.0f // bottom right};好了开始动手干我们在Triangle类中surfaceChanged方法中重新计算缩放后Y的坐标点如下 public void surfaceChanged(int width, int height) {// 设置OpenGL ES画布大小GLES20.glViewport(0, 0, width, height);float radio (float) width / height;triangleCoords new float[]{ // 坐标逆时针顺序0.0f, 0.616f * radio, 0.0f, // top-0.5f, -0.25f * radio, 0.0f, // bottom left0.5f, -0.25f * radio, 0.0f // bottom right};// 初始化形状坐标的顶点字节缓冲区ByteBuffer bb ByteBuffer.allocateDirect(// (number of coordinate values * 4 bytes per float)triangleCoords.length * 4);// use the device hardwares native byte orderbb.order(ByteOrder.nativeOrder());// create a floating point buffer from the ByteBuffervertexBuffer bb.asFloatBuffer();// add the coordinates to the FloatBuffervertexBuffer.put(triangleCoords);// set the buffer to read the first coordinatevertexBuffer.position(0); }查看效果 其实在没有看到效果时我已经猜到最后肯定会绘制出正三角形当然看了效果我们也就踏实了。 OpenGL ES绘图变形其本质无非就是要解决View的宽高比和OpenGL ES正方形坐标系的一个变换如果View是正方形无需变换长方形该缩放缩放。 目前我们是绘制了一个三角形顶点只有三个修改顶点坐标倒还不是那么复杂。如果我们要绘制更加复杂的图像顶点有几十个上百个我们又该如何一个一个修改顶点的缩放比呢聪明的你可能又想到了我用for循环遍历修改啊那样就会方便很多那我只能说你这是小聪明OpenGL ES为我们提供了一个大聪明的方式矩阵变换
  3. 方式三矩阵变换 我们知道OpenGL ES的世界里是三维空间我们要对三维空间中的点进行缩放、平移、旋转实际在数学中有一个好的方式就是用矩阵来计算。而矩阵的知识是大学线性代数中的这个基础需要读者自己去补。 空间中点缩放变换建议看下这篇文章【深度好文】3D坐标系下的点的转换矩阵平移、缩放、旋转、错切 3.1 自定义矩阵缩放方法 接下来我们使用矩阵相乘变换点的坐标定义缩放方法如下 public static void scale(float[] coords, int stride, float sx, float sy, float sz) {float[] scaleM {sx, 0, 0,0, sy, 0,0, 0, sz};for (int i 0; i coords.length; i stride) {float x coords[i];float y coords[i 1];float z coords[i 2];coords[i] scaleM[0] * x;coords[i 1] scaleM[4] * y;coords[i 2] scaleM[8] * z;} }修改surfaceChanged缩放坐标代码 public void surfaceChanged(int width, int height) {// 设置OpenGL ES画布大小GLES20.glViewport(0, 0, width, height);scale(triangleCoords, 3, 1, (float) width / height, 1);// 初始化形状坐标的顶点字节缓冲区ByteBuffer bb ByteBuffer.allocateDirect(// (number of coordinate values * 4 bytes per float)triangleCoords.length * 4);// use the device hardwares native byte orderbb.order(ByteOrder.nativeOrder());// create a floating point buffer from the ByteBuffervertexBuffer bb.asFloatBuffer();// add the coordinates to the FloatBuffervertexBuffer.put(triangleCoords);// set the buffer to read the first coordinatevertexBuffer.position(0); }运行查看效果也可得到正三角形。 3.2 使用GLSL缩放 上面方式固然可行但是大量顶点计算都在CPU端了如何使用GPU程序去并行计算 OpenGL ES也提供了矩阵相乘的方式在三维图形学中一般使用的是4阶矩阵。在DirectX中使用的是行向量如[xyzw]所以与矩阵相乘时向量在前矩阵在后。OpenGL中使用的是列向量如[xyzx]T所以与矩阵相乘时矩阵在前向量在后我们最终通过“变换矩阵”来得到我们想要的向量。 修改顶点着色器代码并定义变换矩阵如下 // 顶点着色器代码 private final String vertexShaderCode // 传入变换矩阵uniform mat4 uMVPMatrix; attribute vec4 vPosition; void main() { // 变换矩阵与顶点坐标相乘等到新的坐标 gl_Position uMVPMatrix * vPosition; };/*** Shader程序中矩阵属性的句柄*/ private int vPMatrixHandle;// 最终变化矩阵 private final float[] mMVPMatrix new float[16];记得在surfaceCreated中获取矩阵属性句柄 public void surfaceCreated() {…// 获取绘制矩阵句柄vPMatrixHandle GLES20.glGetUniformLocation(mProgram, uMVPMatrix); }在surfaceChanged中设置缩放矩阵 public void surfaceChanged(int width, int height) {// 设置OpenGL ES画布大小GLES20.glViewport(0, 0, width, height);float radio (float) width / height;float[] scaleMatrix new float[]{1, 0, 0, 0,0, radio, 0, 0,0, 0, 1, 0,0, 0, 0, 1};// 将缩放矩阵拷贝到变换矩阵中System.arraycopy(scaleMatrix, 0, mMVPMatrix, 0, scaleMatrix.length);}在draw方法中将缩放矩阵传给OpenGL ES程序 public void draw() {…// 将缩放矩阵传递给着色器程序GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mMVPMatrix, 0);// 画三角形GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);// 禁用顶点阵列GLES20.glDisableVertexAttribArray(positionHandle); }运行查看效果也可得到正三角形。 本文到现在已经讲了很多种方式可以正确绘制正三角形但是到现在还没有到正真要讲的内容。我们发现将缩放矩阵传入着色器程序貌似已经完成了最终的目的如果我们仅仅只是为了把三角形画正那么到这里应该就结束了。世界的运行往往出乎我们的意料现在可能要求你缩放但是未来可能还会平移、二维旋转、三维旋转、镜像等等操作如果要我们去定义各种矩阵那简直是灾难于是下面的方式就应运而生了。
  4. 相机和投影 投影OpenGL 中主要有两种投影模式分别是正交投影和透视投影相机相机视图顾名思义就相当于站在相机的角度观察某个物体相机会看到投影到近平面的物体 4.1 投影 OpenGL提供了两种投影变换矩阵如下
    透视投影
    学过素描的应该都知道透视图的概念符合人眼习惯呈现近大远小的效果。 /*** param m 生成的投影矩阵,float[44] param mOffset 填充时候起始的偏移量* param left 近平面left边的x坐标* param right 近平面right边的x坐标* param bottom 近平面bottom边的y坐标* param top 近平面top边的y坐标* param near 近平面距离摄像机的距离* param far 远平面距离摄像机的距离/ public static void frustumM(float[] m, int mOffset,float left, float right, float bottom, float top,float near, float far) { }正交投影
    该投影方式图像大小不会随着距离变化而变化 /**
    param m 生成的投影矩阵,float[44] param mOffset 填充时候起始的偏移量* param left 近平面left边的x坐标* param right 近平面right边的x坐标* param bottom 近平面bottom边的y坐标* param top 近平面top边的y坐标* param near 近平面距离摄像机的距离* param far 远平面距离摄像机的距离/ public static void orthoM(float[] m, int mOffset,float left, float right, float bottom, float top,float near, float far) { }不管是正交投影还是透视投影最终都是将视景体内的物体投影在近平面上这也是 3D 坐标转换到 2D 坐标的关键一步。而近平面上的坐标接着也会转换成归一化设备坐标再映射到屏幕视口上。为了解决之前的图像拉伸问题就是要保证近平面的宽高比和视口的宽高比一致而且是以较短的那一边作为 1 的标准让图像保持居中。 4.2 相机 相机位置设置 /**** param rm 生成的摄像机矩阵float[16] param rmOffset 填充时候的起始偏移量* param eyeX 摄像机x坐标* param eyeY 摄像机y坐标* param eyeZ 摄像机z坐标* param centerX 观察目标点的x坐标* param centerY 观察目标点的y坐标* param centerZ 观察目标点的z坐标* param upX 摄像机up向量在x上的分量* param upY 摄像机up向量在y上的分量* param upZ 摄像机up向量在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) { }eyeXeyeYeyeZ摄像机坐标。centerXcenterYcenterZ:观察点坐标和摄像机坐标一起决定了摄像机的观察方向即向量(centerX - eyeX, centerY - eyeY, centerZ - eyeZ)。观察方向不朝向视景体是无法看到的。upXupYupZ:摄像机up向量。相对于人眼观察物体中人头的朝向头的朝向影响了最后的成像。同样以图来说明 当up向量为Y的正方向时正如我们头顶对着天花板所以观察到的物体是正的投影在近平面的样子就是正的如右图 当up向量为X正方向时正如我们向右90度歪着脑袋去看这个三角形看到的三角形就会是向左旋转了90度的三角形 再比如up向量如果为Z轴正方向就相当于仰着头去看这个三角形但是因为我们的up向量和观察方向平行了所以我们什么也看不到就比如仰着头去看你身前的物体时你什么也看不到。 所以在设置up向量时一般总是设置为(0,1,0)这是大多数观察时头朝上的方向。注意up向量的大小无关紧要有意义的只有方向。 4.3 near、far的取值范围规定 正交投影时摄像机可位于视景体中间此时near 0far 0近平面位于视点后面Z轴正方向远平面位于视点前面Z轴负方向正交投影时视景体也可位于视点后面(Z轴正方向)此时near 0, far 0正交投影时far 和 near没有规定的大小关系既可以far near 也可以 far near只要物体在视景体内都可以被观察到。透视投影时farnear0我们不考虑其他情况我们默认就在Z轴上看物体 当centerZ - eyeZ0时近平面nearZ坐标eyeZnear远平面farZ坐标eyeZfar; 当centerZ - eyeZ0时近平面nearZ坐标eyeZ-near远平面farZ坐标eyeZ-far; 我们要保证物体Z坐标在nearZ和farZ之间就能看到也就是物体在视景里就能看到。 4.4 构造模型矩阵 根据上面的理论知识我们不用再手动构造一个缩放矩阵了我们定义如下三个矩阵并进行变换 public class Triangle {…// vPMatrix是“模型视图投影矩阵”的缩写// 最终变化矩阵private final float[] mMVPMatrix new float[16];// 投影矩阵private final float[] mProjectionMatrix new float[16];// 相机矩阵private final float[] mViewMatrix new float[16];public void surfaceChanged(int width, int height) {// 设置OpenGL ES画布大小GLES20.glViewport(0, 0, width, height);float ratio;if (width height) {ratio (float) width / height;// 横屏使用// 透视投影特点物体离视点越远呈现出来的越小。离视点越近呈现出来的越大// 该投影矩阵应用于对象坐标Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);} else {ratio (float) height / width;// 竖屏使用// 透视投影特点物体离视点越远呈现出来的越小。离视点越近呈现出来的越大// 该投影矩阵应用于对象坐标Matrix.frustumM(mProjectionMatrix, 0, -1, 1, -ratio, ratio, 3, 7);}Matrix.setLookAtM(mViewMatrix, 0,0, 0, 3f,0f, 0f, 0f,0f, 1.0f, 0.0f);// Calculate the projection and view transformationMatrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);} }说明我们使用frustumM做透视矩阵时我们要把近平面宽高和屏幕宽高比例对应我们以较短的一边作为1按比例拉升即可。而farnear0遵循此原则即可。 设置相机setLookAtM参数我们只需要注意eyeZ的取值根据上面的说明正确取值。上面方法取值为3那么近平面的nearZ坐标eyeZ-near0和物体z坐标重合看到的物体比例正好不会变大也不会缩小。 最后我们用multiplyMM方法将投影矩阵和相机矩阵转换为最终的矩阵然后传给着色器程序即可。 运行程序也可得到正三角形。 相机和投影概念实际上是为3D模型准备的现在我们把他用在2D图形上着实有中降维打击大炮打苍蝇的感觉。但是我们不得不了解这个强大的工具为将来遇到的3D场景变换做准备。 最后 我们都知道独孤九剑剑法的最高境界是无招。上述介绍的几种方式都可谓是剑招当我们了解了事物运行的本质后这几种方式皆可为我所用在适当的场景下选择合适的方式甚至可以创造招式。 《黑客帝国》中尼奥复活后了解了虚拟世界的本质整个世界的运行不过就是一串串数字。原来难以翻越的高山现在也只是眼下的风景。还记的我们第一篇章吗Android OpenGLES2.0开发一艰难的开始现在的我觉得脚下有路、心中有光也期待未来会更美好。 参考 https://juejin.cn/post/6844903614838751240https://cloud.tencent.com/developer/article/1015587https://www.nxrte.com/jishu/13722.html