前言

说到矩阵变换,我们第一时间想到的就是大学时代的线性代数这些复杂的东西,突然有了一种令人从入门到放弃的念头,不慌,作为了一个应用层的CV工程师,
在实际应用中线性代数哪些复杂的计算根本不用我们自己去算,绝大部分情境下直接使用Matrix这个类或者glm这个库即可。

关于矩阵与向量的相关知识,矩阵的加减乘除等规则,这里就不展开细说,感兴趣的同学自行查阅线性代数即可,不过这些规则忘记了也没关系,反正有API可用。

我们知道在Opengl中有很多中坐标系,在Opengl中矩阵的一大作用就是将坐标从一个坐标系转换到另一个坐标系下,同时还可以通过矩阵实现一些形变的效果,
今天我们就使用矩阵的方式搭配Opengl ES实现平移、缩放、旋转等一些形变变换的效果。

通常来说在Opengl ES中的矩阵都是一个4X4的矩阵,也就是一个包含16个元素的一维数组。

下面以Matrix这个类介绍一下矩阵变换的一些常用方法。下面介绍的矩阵变换所参考的坐标系统都是一样的,均是下图这个:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p8RRqdaf-1682242193379)(https://flyer-blog.oss-cn-shenzhen.aliyuncs.com/%E7%9F%A9%E9%98%B5%E5%8F%98%E6%8D%A2%E5%8F%82%E8%80%83%E5%9D%90%E6%A0%87%E7%B3%BB.png)]

单位矩阵

所谓的单位矩阵就是左上角到右下角对角线值均为1的矩阵,又成为单元矩阵。使用Matrix.setIdentityM方法可以将一个矩阵变为单位矩阵。

矩阵平移

矩阵平移所使用的方法是Matrix.translateM

需要注意的是在Opengl在顶点坐标的值是在-1到1之间,因此translateX的范围可以为-2到2。为什么呢?因为-1到1的距离是2,因此往最多可以往左移动2,同理,最多可以往右移动2。

矩阵旋转

矩阵旋转所使用的方法是Matrix.rotateM,其中第三个参数是表示选旋转的角度,后面的三个参数xyz代表的是绕那个轴旋转,绕那个轴旋转就把那个轴的参数设置成1,其他轴设置成0即可。

矩阵缩放

矩阵缩放所使用的方法是Matrix.scaleM

组合矩阵的写法

假如有以下形变步骤,先绕Z轴旋转90度,再向X轴平移0.5,最后X轴缩放0.9倍,那么最终这个形变矩阵该如何计算呢?是以下这个写法吗?

Matrix.rotateM(mvpMatrix, 0, 90, 0, 0, 1);
Matrix.translateM(mvpMatrix, 0, 0.5, 0, 0);
Matrix.scaleM(mvpMatrix, 0, 0.9, 1f, 0f);

不是的,组合矩阵的写法有一个规则,这个规则大家一定要记住:

在组合矩阵时,先进行缩放操作,然后是旋转,最后才是位移,但是写法需要反正写,也就是先写translateM,然后rotateM,最后scaleM

如果不这样写会发生什么呢?例如顺着写,先写scaleM,然后是rotateM,最后写translateM,测试时就会出现问题,旋转超过180度之后再移动,就会出现移动方向相反的情况。

因此以上例子正确的写法应该是这样子的:

Matrix.translateM(mvpMatrix, 0, 0.5, 0, 0);
Matrix.rotateM(mvpMatrix, 0, 90, 0, 0, 1);
Matrix.scaleM(mvpMatrix, 0, 0.9, 1f, 0f);

show me code

在Opengl ES中可以使用mat4来表示一个4X4的矩阵,我们将总的变换矩阵在CPU中计算好之后以uniform的形式传递到着色器中去。
在顶点着色器中将矩阵与顶点坐标相乘的结果作为新的顶点输出坐标即可完成矩阵变换。

以下是MatrixTransformOpengl.cpp的详细代码:

// 顶点着色器
static const char *ver = "#version 300 es\n""in vec4 aPosition;\n""in vec2 aTexCoord;\n""out vec2 TexCoord;\n""uniform mat4 mvpMatrix;\n""void main() {\n""  TexCoord = aTexCoord;\n""  gl_Position = mvpMatrix * aPosition;\n""}";// 片元着色器
static const char *fragment = "#version 300 es\n""precision mediump float;\n""out vec4 FragColor;\n""in vec2 TexCoord;\n""uniform sampler2D ourTexture;\n""void main()\n""{\n""    FragColor = texture(ourTexture, TexCoord);\n""}";// 使用绘制两个三角形组成一个矩形的形式(三角形带)
// 第一第二第三个点组成一个三角形,第二第三第四个点组成一个三角形
const static GLfloat VERTICES[] = {1.0f,-1.0f, // 右下1.0f,1.0f, // 右上-1.0f,-1.0f, // 左下-1.0f,1.0f // 左上
};// 贴图纹理坐标(参考手机屏幕坐标系统,原点在左上角)
//由于对一个OpenGL纹理来说,它没有内在的方向性,因此我们可以使用不同的坐标把它定向到任何我们喜欢的方向上,然而大多数计算机图像都有一个默认的方向,它们通常被规定为y轴向下,X轴向右
const static GLfloat TEXTURE_COORD[] = {1.0f,1.0f, // 右下1.0f,0.0f, // 右上0.0f,1.0f, // 左下0.0f,0.0f // 左上
};MatrixTransformOpengl::MatrixTransformOpengl():BaseOpengl() {initGlProgram(ver,fragment);positionHandle = glGetAttribLocation(program,"aPosition");textureHandle = glGetAttribLocation(program,"aTexCoord");textureSampler = glGetUniformLocation(program,"ourTexture");matrixHandle = glGetUniformLocation(program,"mvpMatrix");
}MatrixTransformOpengl::~MatrixTransformOpengl() noexcept {LOGD("MatrixTransformOpengl析构函数");
}void MatrixTransformOpengl::setMvpMatrix(float *mvp) {for (int i = 0; i < 16; ++i) {mvpMatrix[i] = mvp[i];}
}void MatrixTransformOpengl::setPixel(void *data, int width, int height, int length) {LOGD("texture setPixel");imageWidth = width;imageHeight = height;glGenTextures(1, &textureId);// 激活纹理,注意以下这个两句是搭配的,glActiveTexture激活的是那个纹理,就设置的sampler2D是那个// 默认是0,如果不是0的话,需要在onDraw的时候重新激活一下?
//    glActiveTexture(GL_TEXTURE0);
//    glUniform1i(textureSampler, 0);// 例如,一样的glActiveTexture(GL_TEXTURE2);glUniform1i(textureSampler, 2);// 绑定纹理glBindTexture(GL_TEXTURE_2D, textureId);// 为当前绑定的纹理对象设置环绕、过滤方式glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);// 生成mip贴图glGenerateMipmap(GL_TEXTURE_2D);glBindTexture(GL_TEXTURE_2D, textureId);// 解绑定glBindTexture(GL_TEXTURE_2D, 0);
}void MatrixTransformOpengl::onDraw() {//    glViewport(0,0,imageWidth,imageHeight);glClearColor(0.0f, 1.0f, 0.0f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(program);// 激活纹理glActiveTexture(GL_TEXTURE2);glUniform1i(textureSampler, 2);// 绑定纹理glBindTexture(GL_TEXTURE_2D, textureId);// 设置矩阵glUniformMatrix4fv(matrixHandle, 1, GL_FALSE,mvpMatrix);/*** size 几个数字表示一个点,显示是两个数字表示一个点* normalized 是否需要归一化,不用,这里已经归一化了* stride 步长,连续顶点之间的间隔,如果顶点直接是连续的,也可填0*/// 启用顶点数据glEnableVertexAttribArray(positionHandle);glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);// 纹理坐标glEnableVertexAttribArray(textureHandle);glVertexAttribPointer(textureHandle,2,GL_FLOAT,GL_FALSE,0,TEXTURE_COORD);// 4个顶点绘制两个三角形组成矩形glDrawArrays(GL_TRIANGLE_STRIP,0,4);glUseProgram(0);// 禁用顶点glDisableVertexAttribArray(positionHandle);if(nullptr != eglHelper){eglHelper->swapBuffers();}glBindTexture(GL_TEXTURE_2D, 0);
}

java层的MatrixActivity.java实例代码如下:

public class MatrixActivity extends BaseGlActivity {private MatrixTransformOpengl matrixTransformOpengl;// 遵守先缩放再旋转最后平移的顺序// 首先执行缩放,接着旋转,最后才是平移。这就是矩阵乘法的工作方式。private final float[] mvpMatrix = new float[16];// 因为在Opengl在顶点坐标的值是在-1到1之间,因此translateX的范围可以为-2到2。private float translateX = 0;private float scaleX = 1;private float rotationZ = 0;@Overridepublic int getLayoutId() {return R.layout.activity_gl_matrix;}@Overridepublic BaseOpengl createOpengl() {matrixTransformOpengl = new MatrixTransformOpengl();return matrixTransformOpengl;}@Overridepublic Bitmap requestBitmap() {BitmapFactory.Options options = new BitmapFactory.Options();// 不缩放options.inScaled = false;Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_boy, options);// 设置一下矩阵Matrix.setIdentityM(mvpMatrix, 0);matrixTransformOpengl.setMvpMatrix(mvpMatrix);return bitmap;}@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);findViewById(R.id.bt_translate).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (null != matrixTransformOpengl) {translateX += 0.1;if(translateX >=2 ){translateX = 0f;}updateMatrix();}}});findViewById(R.id.bt_scale).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (null != matrixTransformOpengl) {scaleX += 0.1;updateMatrix();}}});findViewById(R.id.bt_rotate).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (null != matrixTransformOpengl) {rotationZ += 10;updateMatrix();}}});findViewById(R.id.bt_reset).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (null != matrixTransformOpengl) {translateX = 0;scaleX = 1;rotationZ = 0;updateMatrix();}}});}private void updateMatrix() {Matrix.setIdentityM(mvpMatrix, 0);// 重点注释// 在组合矩阵时,先进行缩放操作,然后是旋转,最后才是位移,但是写法需要反正写,也就是先写translateM,然后rotateM,最后scaleM// 如果不这样写会发生什么呢?例如顺这写,先写scaleM,然后是rotateM,最后写translateM,测试时就会出现问题,旋转超过180度之后再移动,就会出现移动方向相反的情况Matrix.translateM(mvpMatrix, 0, translateX, 0, 0);Matrix.rotateM(mvpMatrix, 0, rotationZ, 0, 0, 1);Matrix.scaleM(mvpMatrix, 0, scaleX, 1f, 0f);matrixTransformOpengl.setMvpMatrix(mvpMatrix);myGLSurfaceView.requestRender();}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDgLbBPI-1682242193381)(https://flyer-blog.oss-cn-shenzhen.aliyuncs.com/opengles%E7%9F%A9%E9%98%B5%E5%8F%98%E6%8D%A2.gif)]

系列教程源码

学习福利

【Android 详细知识点思维脑图(技能树)】

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

详细整理扫描下方二维码直接领取;

Android架构视频+BAT面试专题PDF+学习笔记

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

Opengl ES之矩阵变换相关推荐

  1. [OpenGL ES 03]3D变换:模型,视图,投影与Viewport

    [OpenGL ES 03]3D变换:模型,视图,投影与Viewport 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循"署名-非商业用途-保持一致" ...

  2. OpenGL ES 简单教程

    OpenGL ES 简单教程 2014-04-24 13:35 佚名 apkbus 字号:T | T 什么是OpenGL ES?OpenGL ES (为OpenGL for Embedded Syst ...

  3. viewpager初始化fragment没有绘制_NDK OpenGL ES渲染系列 之 绘制三角形

    前言 新的知识学习都是循序渐进的,从基础到复杂.前面OpenGL ES概念 已经介绍了OpenGL ES的相关概念,这篇文章开始我们就正式开始OpenGL ES渲染系列第一站---绘制三角形.绘制三角 ...

  4. OpenGL ES简介(一)

    摘要: 概述 在聊Android的View渲染流程中,通常会有一个比较核心的步骤:通过OpeGL ES接口调用GPU接口通知GPU绘制图形.其完整的流程:UI对象---->CPU处理为多维图形, ...

  5. Android opengl es 3.0 + ndk 绘画涂鸦项目

    前言 写一个opengl es 3.0 + ndk 的绘画涂鸦项目,命名为白板哈哈哈,记录自己遇到的问题,顺便学到的知识整合一遍,算是对自己一段时间的总结. 项目地址:Whiteboard 如果对你有 ...

  6. 【OpenGL ES】凸镜贴图

    1 前言 正方形图片贴到圆形上 中将正方形图片上的纹理映射到圆形模型上,同理,也可以将圆形上的纹理映射到凸镜的球形曲面上.如下图,最左边的竖条是原图片的截面(纹理坐标),最右边的竖条是变换后的顶点模型 ...

  7. 【OpenGL ES】立方体贴图(6张图)

    1 前言 本文通过一个立方体贴图的例子,讲解三维纹理贴图的应用,案例中使用 6 张不同的图片给立方体贴图,图片如下: 本文涉及到的知识点主要包含:三维绘图.MVP 矩阵变换.纹理贴图,读者如果对 Op ...

  8. 从显示一张图片开始学习OpenGL ES

    前言 网上很多介绍OpenGL ES的文章,但由于OpenGL ES内容太多,所以这些文章难免过于臃肿杂乱,很难抓住重点,对于初学者来说最后还是云里雾里.很多人(包括笔者本人)开始深入了解OpenGL ...

  9. Android OpenGL ES 学习(十一) –渲染YUV视频以及视频抖音特效

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

最新文章

  1. 思念水饺吃成泡沫水饺(图)思念质量门
  2. 配置React的Babel 6和Webpack 2环境
  3. 设计优秀API的五大规则
  4. 【设计模式】Spring的核心IOC容器中用到的设计模式
  5. 【Java进阶】Elasticsearch应用之京东搜索
  6. numpy 转存为matlab_Numpy学习打卡task01
  7. python入门基础篇(三)序列切片,列表、元组推导式
  8. jeesite在eclipse中部署
  9. OpenVSLAM:日本先进工业科技研究所新开源视觉SLAM框架
  10. 买无线路由还是买无线AP?
  11. 生产者消费者_【Java面试】实现生产者消费者模式
  12. 两男子骑摩托车抢夺宴席礼金 警方:嫌疑人已被抓获
  13. 使用libevhtp搭建HTTPS SERVER(单向验证身份)
  14. 北大青鸟软件工程师ACCP4.0课程
  15. 地市级公安局实战通信指挥调度方案
  16. Libevent 源码文件结构分析
  17. 优启通安装linux系统,利用U盘启动盘优启通(pe)安装centos到旧笔记本上
  18. win10虚拟服务器安装xp,xp mode for windows10虚拟机安装教程(详细)
  19. credit author statement
  20. ibm aix_IBM AIX SAN Volume Controller更新和迁移

热门文章

  1. java method field_java_解析Java中的Field类和Method类,Field类 Field类中定义了一些方 - phpStudy...
  2. hadoop环境新手安装教程
  3. 全民分销时代,企业如何运营分销平台?
  4. 给定一个不多于5位的正整数,判断它是几位数,并输出。 输入
  5. 微信小程序014租房-房屋租赁合同系统
  6. 软件项目管理第4版课后习题[附解析]第六章
  7. 限时活动|凭徽章领披萨大奖,玩转Moonbeam治理论坛
  8. 电脑wifi已连接,登录QQ客户端有网,但打开网页没有网?
  9. R语言27-Prosper 贷款数据分析3
  10. matlab各次谐波含量,与谐波相关的基本概念