Android系统中的坐标与矩阵体系

1,007 阅读7分钟

前言

Android 系统中,不管任何一种View,其测量、滑动、绘制、布局都是按照父View左上角原点进行的,这种就是Android 默认的2D坐标体系,除此之外,Android还支持基于open gl es的3D坐标体系以及Camera 3D坐标体系。

关于坐标

实际上,Android系统中,如果设计Camera和open gl,我们可能会遇到总共4种坐标系统,因此,使用的时候需要提前了解。

2.1 Canvas 2D坐标系

正常情况下,Canvas 2D坐标系就是一张普通的画板,当然,其本身和软件绘制与硬件绘制也有关系,软件绘制时,Canvas的大小可能会大于View本身,因为Canvas时共享的,而硬件绘制是每个View拥有单独的RenderNode,从而避免了硬件绘制性能问题,以及Canvas 过大的问题。

  • 左上角为(0,0)点
  • 三角函数计算时顺时旋转
  • y轴向下为正,x向左为正
  • 向量表示(x,y)
  • 坐标轴取值范围任意

企业微信20231112-130233@2x.png

2.2 Shader 坐标

Shader 坐标是最容被遗忘的,因为其本身作为画笔的一部份,不属于Canvas 直接调用,但其实Shader也是有坐标和范围大小,而且还能通过Canvas Matrix后者setLocalMatrix进行变幻。

Shader可以理解为印染工具,不会因为图形的绘制位置的偏移而偏移(排除Canvas 矩阵变换)。

2.3 Camera 3D坐标系

  • 左上角为(0,0)点
  • 属于3D坐标系统,但z轴向屏幕内方向
  • 三角函数计算时逆时针旋转
  • y轴向上为正,x向左为正,z轴向内为正
  • 向量表示(x,y,z)
  • 坐标轴取值范围任意

f3005ccc8de1e5293c68762d02acecac.png

2.4 open gl 顶点坐标系(世界坐标系)

  • View中心点为原点
  • 属于3D坐标系,但存在两种坐标系统,左手坐标系和右手坐标系,左手坐标系看似和Camera坐标系
  • 三角函数计算按逆时针旋转
  • y轴向上为正,x向左为正,z轴取决于使用哪种坐标系统,一般的都是使用右手坐标系统。
  • 默认正对(0,0,0,1)点,其中第四个分量为w,此分量表示顶点到Camera的缩放比例,注意不表示Camera的位置
  • 向量表示(x,y,z,w),其中w分量会用来做点的缩放, (x/w,y/w,z/w),从而实现距离的调整
  • 坐标轴取值范围-1.0到1.0

企业微信20231112-133000@2x.png

2.5 open gl 纹理坐标系

纹理本身就是材质的一部份,所谓纹理可以理解为对材质的坐标表示形式,通俗一点的讲,这种坐标系每张图片自身坐标系。

  • 左下角为(0,0)点
  • 三角函数计算时逆时旋转
  • y轴向上为正,x向右为正
  • 向量表示(x,y) 或(S,T)
  • 坐标轴取值范围-1.0到1.0

企业微信20231112-135839@2x.png

关于矩阵

在Android系统中,矩阵实际上也分为2种android.graphics.Matrix 和android.opengl.Matrix ,前者为2D坐标系和Camera 3D坐标系所使用,当然有些图片处理也使用前者。

android.graphics.Matrix 和android.opengl.Matrix 相比,主要有以下特点:

3.1 graphics专门用来处理Android自身的坐标转换,当然,在开发过程中,通常调用matrix#preTranslate来对齐Camera和View的旋转点,而后者需要利用Matrix#multipleMM 修改投影矩阵,最终在Shader中修改位置向量。

3.2 graphics默认是三阶矩阵,向量为(x,y,z),而open gl是4阶矩阵,向量为(x,y,z,w)

3.3 矩阵表示形式存在差异,graphic控制点相对固定,而open gl 复杂一些。

912181-6edf7955de7d48fb.webp

3.4 数组表示差异 graphic的数组表示形式以行为准,open gl 以列为准,

数组

123
456
789

转为运算矩阵如下:

123
456
789

基本上顺序和数组一致

open gl的是是取转置矩阵,当然运算的时候不影响 数组

1234
5678
9101112
13141516

转为运算矩阵

15913
261014
371115
481216

因此,构建不同模式的矩阵时,要注意构建矩阵时的顺序问题。

Graphic 矩阵变化

Graphic矩阵一般在使用Canvas时才会使用,可以通过translate、rotate等方法进行坐标转换,但是这里要注意的是,一个错误的理解是“坐标系转换”,实际上是坐标的Matrix投影的转换。

Matrix有很多方法,基本都是矩阵运算,但仍然存在几个方法 Matrix#setPolyToPoly、Matrix#invert、Matrix#mapXXX、Matrix#setValues

  • Matrix#setPolyToPoly 可以利用结果坐标和原始坐之间对应关系,生成结果投影矩阵
  • Matrix#invert 计算逆矩阵,输出逆矩阵
  • Matrix#mapXXX 通过现有矩阵计算出结果坐标,显然这类方法方便我们开发时观测数据变化
  • Matrix#setValues 主要用于手动生成矩阵,注意,这里必须是3x3的矩阵

open gl 矩阵变化

open gl使用的4x4的矩阵,相对来说易用性和理解上比较差一些,但好处是在Android中该Matrix类矩阵都是java实现的,反而更容易观察运算过程。当然其中也有一些比较重要的矩阵。

  • Matrix#setXXXM 在open gl矩阵类中,一般setXXXM的不是去修改矩阵,而是定义矩阵。例如setIdentityM定义单位矩阵,setLookAtM定义摄像机(眼睛)位置的矩阵,setRotateEulerM 定义初始旋转矩阵。
  • 在open gl中,矩阵初始转换都是代码完成,最后在顶点着色器中右乘4维向量,这样做有很多原因,比如gpu运算快。
  • open gl直线问题,我们知道open gl只能画(点、线、三角),但是“线”比较特殊,缺少一些中间过程(这里所谓的中间过程是 起点->终点 绘制时,点合三角会存在众多中间位置,gl_Position.x 也是动态变化的),但是线是不一样的,缺少这个过程,导致无法像点、圆、三角形那样实现过渡效果。
precision mediump float; 
uniform vec4 u_Color;
varying float x;

void main()
{
  if(x > 0.25){
     gl_FragColor = u_Color;
   }else if(x >= 0.0){
     gl_FragColor = vec4(0.0,1.0,0.0,1.0);
   } else if(x > -0.25){
      gl_FragColor = u_Color;
   }else if(x >= -0.6){
       gl_FragColor = vec4(0.5,0.5,0.0,0.5);
   }else {
     gl_FragColor = u_Color;
   }
}

企业微信20231112-205043@2x.png

从图上我们看到,除了正方形和点,线一直是红色,因此线条绘制相对特殊。

open gl es 归一化

open gl es 和 canvas 等坐标体系、矩阵体系是不同的,特别是open gl的顶点坐标范围(-1,1),因此open gl的绘制往往需要归一化处理,就是最大范围不超过1,最小不能超过-1,准确的表示是-1到1之间,这也是使用open gl时必须要注意的。

那么如何归一化呢,其实很简单

总结

以上就是完整的Android 系统坐标体系介绍,在我们实现特殊效果,免不了使用这些坐标系以及矩阵,因此,本篇对于学习自定义View,是一篇入门文章,但是也是必须了解的Android 绘制规则。

另外,上一篇文章我们了解了《 Egl Context 与 open gl 纹理关系》,也是一篇非常重要的基础内容,很多学习open gl 图像处理的同学,经常会很疑惑纹理为什么没有关联Egl Context,却能实现离屏渲染,原因就是通过线程进行了映射,底层将两者进行了关联。

本篇就到这里

附录

Android--利用camera打造3D效果
open gl 坐标系统
android matrix 最全方法详解与进阶