HoudiniVex笔记_P15_MatrixBasics矩阵基础

2023-11-01 07:30

本文主要是介绍HoudiniVex笔记_P15_MatrixBasics矩阵基础,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原视频:https://www.youtube.com/playlist?list=PLzRzqTjuGIDhiXsP0hN3qBxAZ6lkVfGDI
Bili:Houdini最强VEX算法教程 - VEX for Algorithmic Design_哔哩哔哩_bilibili

Houdini版本:19.5

1、矩阵类型

Houdini中有三种矩阵类型:2x2矩阵(2D旋转)、3X3矩阵(3D旋转)、4X4矩阵(平移、旋转、缩放),

eg.可在类型为detail的AttributeWrangle节点输入以下代码,创建不同的矩阵:

matrix2 mat2 = set(1,2,3,4);
matrix3 mat3 = set(1,2,3,4,5,6,7,8,9);
matrix mat4 = set(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);2@mat2 = mat2;
3@mat3 = mat3;
4@mat4 = mat4;

 结果为:

2、矩阵声明

使用大括号  {    }  时不能使用变量。

eg.可在类型为detail的AttributeWrangle节点输入以下代码,体验矩阵声明的方法:

float val = 1.0;matrix mat1 = set(val,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
matrix mat2 = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
matrix mat3 = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
matrix mat4 = set(set(val,2,3,4),set(5,6,7,8),set(9,10,11,12),set(13,14,15,16));4@mat1 = mat1;
4@mat2 = mat2;
4@mat3 = mat3;
4@mat4 = mat4;

结果:

3、单位矩阵

单位矩阵:仅对角线为1 ,其它为0,它的值=1。

 eg.可在类型为detail的AttributeWrangle节点输入以下代码创建单位矩阵:

matrix2 mat2 = ident();
matrix3 mat3 = ident();
matrix mat4 = ident();2@mat2 = mat2;
3@mat3 = mat3;
4@mat = mat4;

结果为:

4、行列式与逆矩阵

①2x2矩阵,用下面公式计算它的值,

如果值=0,表示该矩阵无逆矩阵,如≠0,表示该矩阵有逆矩阵。
矩阵与逆矩阵相乘,等于单位矩阵,

②3x3矩阵,可用下面方法计算它的行列式值,即对角线上的元素相乘红色相加的和 减去 蓝色相加的和,

 ③4X4矩阵与3x3矩阵相似。

eg.逆矩阵函数 invert(matrix) 、行列式计算函数determinant(matrix):

 可在类型为detail的AttributeWrangle节点输入以下代码,体验逆矩阵函数、行列式函数,

matrix mat = set(set(1,2,3,4),set(5,6,7,8),set(9,10,11,12),set(13,14,15,16));
matrix imat = invert(mat);      //转为逆矩阵float det = determinant(mat);   //矩阵行列式值matrix mat2 = set(set(3,2,3,20),set(5,11,7,8),set(9,10,11,12),set(3,14,15,16));
matrix imat2 = invert(mat2);    //转为逆矩阵float det2 = determinant(mat2); //矩阵行列式值f@det = det;                    //值为0,该矩阵没有逆矩阵
f@det2 = det2;                  //值不为0,该矩阵没有逆矩阵matrix cmat = mat * imat;       //矩阵与逆矩阵相乘
4@cmat = cmat;matrix cmat2 = mat2 * imat2;    //矩阵与逆矩阵相乘
4@cmat2 = cmat2;                //值接近单位矩阵

5、转置矩阵

将矩阵的行、列置换得到新的矩阵,即转置矩阵。常用于投影…等更复杂的转换。

对矩阵A进行转置,可得:如下图所示

转置矩阵的性质:和、点乘、逆矩阵
 

eg.可在类型为detail的AttributeWrangle节点输入以下代码,

matrix mat = set(set(1,2,3,4),set(5,6,7,8),set(9,10,11,12),set(13,14,15,16));4@mat = mat;4@tmat = transpose(mat);    //将矩阵行列置换

 结果为:

6.、矩阵与标量乘法运算

矩阵乘法主要有3种,矩阵与标量(float)相乘、矩阵与矩阵相乘、矩阵与向量相乘。

下面是Multiplication  with Saclar与标量相乘:

eg.可在类型为detail的AttributeWrangle节点输入以下代码,

matrix mat = set(set(1,2,3,4),set(5,6,7,8),set(9,10,11,12),set(13,14,15,16));mat = mat * 2.0;      //  mat = mat * chf('num');4@mat = mat;    //结果:略

7、与矩阵相乘

如前所述,矩阵包含位置、旋转、缩放等信息,相乘顺序不同,结果也不同。因为在移动、旋转、缩放的变换中,物体变换中心点是以(0, 0, 0)为变换中心,懂?(在Houdini中,矩阵与向量相乘,顺序不影响结果)。

①相同行列矩阵相乘:

②非相同行列矩阵相乘,左边矩阵行数为新矩阵行数,右边矩阵列数为新矩阵列数;

在Houdini中,2x3矩阵、3x2矩阵没有任何函数或定义,仅作理解

 
用这图应该更易理解些:

eg.可在类型为detail的AttributeWrangle节点输入以下代码,

matrix mat1 = set(set(1,2,3,4),set(5,6,7,8),set(9,10,11,12),set(13,14,15,16));
matrix mat2 = set(set(16,15,14,13),set(12,11,10,9),set(8,7,6,5),set(4,3,2,1));matrix mat3 = mat1 * mat2;       //相乘顺序不同 结果不同
matrix mat4 = mat2 * mat1;   4@mat3 = mat3;
4@mat4 = mat4;

 结果为: //相乘顺序不同 结果不同,

8、与单位矩阵相乘

矩阵与单位矩阵相乘,得到的还是原矩阵,相乘顺序不影响结果。

9、与转置矩阵相乘

矩阵与转置矩阵相乘:

eg.可在类型为detail的AttributeWrangle节点输入以下代码,

matrix mat1 = set(set(1,2,3,4),set(5,6,7,8),set(9,10,11,12),set(13,14,15,16));
matrix mat2 = set(set(16,15,14,13),set(12,11,10,9),set(8,7,6,5),set(4,3,2,1));matrix mat3 = transpose(mat1) * transpose(mat2);   
matrix mat4 = transpose(mat2 * mat1);   4@mat3 = mat3;
4@mat4 = mat4;

 结果为:两结果相同

10、与向量相乘

①行向量与矩阵相乘,得到一个新向量/值。一般来说,使用向量时需要注意顺序,但是在Houdini中,向量与矩阵相乘,顺序不影响结果,

或者:

eg.可在类型为detail的AttributeWrangle节点输入以下代码,

//向量与矩阵相乘 顺序不影响结果
vector v = set(1, 2, 3);matrix mat = set(set(1,2,3,4),set(5,6,7,8),set(9,10,11,12),set(13,14,15,16));vector v1 = v * mat;
vector v2 = mat * v;v@v1 = v1;
v@v2 = v2;    //    v1 与 v2 结果相等

 ②矩阵与列向量相乘,得到一个新矩阵(2行1列)

③4X4矩阵与向量相乘
与上面的【①行向量与矩阵相乘】相似,只不过在与4X4矩阵相乘的过程中,向量 (x, y, z) 被转换成  (x, y, z, 1) 后再与4X4矩阵相乘。
(x, y, z, 1) :参数1表示位置/移动、参数为0时表示方向,不写参数默认为1/位置。

11、矩阵与位移

在4x4矩阵中,最后一行表示位移Translation值。
比如,某物体从点 (x, y, z) 移动到某点,即移动向量 (a, b, c), 把向量带入到4X4矩阵,移动后的新位置为:

当然,单一移动使用矩阵意义不大。需要复杂变换时可使用矩阵进行变换。
eg.一个polygon的Sphere小球节点、一个类型为Points的AttributeWrangle节点输入以下代码:

vector move = chv('move');    //向量通道matrix mat = ident();        //单位向量
translate(mat, move);        //移动@P = @P * mat;        //小球移动// @P = @P + move;        用这也可以达到同样的移动效果

结果:滑动通道向量move值,小球根据向量值移动。

12、矩阵与缩放

物体的每个坐标的x、y、z值具有特定大小或特定比例值,假设物体某个点位置为 (x, y, z) ,按比例 (a, b, c) 进行缩放,那么该点的新位置为 (ax, by, cz),在矩阵中可表示为:

 当然,单一缩放使用矩阵意义不大。需要复杂变换时可使用矩阵进行变换。
eg.一个polygon的Sphere小球节点、一个类型为Points的AttributeWrangle节点输入以下代码:

vector scale = chv('scale');    //向量通道matrix mat = ident();           //单位矩阵
scale(mat, scale);          //使用矩阵进行缩放@P = @P * mat;              //小球缩放 // @P = @P * scale;      用这也可以达到同样的缩放效果

结果:滑动向量通道的x、y、z值,可以控制小球的x、y、z方向的缩放。

13、矩阵与旋转

以2D旋转为例,在二维空间中的任意向量,可以通过二维的基向量来表示向量(可以理解为用坐标系来表示)。可以联想一下物理中的静止参考系和动参考系。动静参考系在这里对应于动静坐标系。向量旋转的同时,动坐标系是相对于这个向量不动的,相对于静止坐标系则旋转同样的角度。只要知道旋转后动坐标系中的标准正交基在静止坐标系中的表达,就能知道旋转后的向量在静止坐标系中的表达。(本段文字来自@太阳与风博士)

向量绕二维直角坐标系原点逆时针旋转θ,对应的旋转矩阵R(θ)为:

 关于2X2旋转矩阵的推导过程,及3x3旋转矩阵可以看这里的推导过程,点击直达。

eg.按下图添加节点,

在类型为points的rotate中写入以下代码:

vector axis = normalize(point(1, 'P', 1));  //直线作为旋转轴
float ang = radians(chf('angle'));          //范围设为0~360matrix mat = ident();       //单位矩阵rotate(mat, ang, axis);     //旋转,直线为旋转轴,旋转角度为ang@P *= mat;                  //更新box位置          

 结果:改变直线转向及滑动角度ang值,box仍以直线为旋转轴进行旋转。

14、组合变换矩阵

移动、旋转、缩放组合变换,看看不同顺序的组合变化。

(理论可以大概参考这篇文章——>点击直达)

eg.一个box盒子节点、一个类型为Points的AttributeWrangle节点输入以下代码:

matrix mat = ident();float ang = radians(chf('angle'));      //设置范围0~360
vector axis = set(0, 1, 0);             //旋转轴
vector move = set(chf('move'), 1, 1);   //暂设置为1~3
vector scale = set(chf('scale'), 1, 1); //暂设置为0-3//改变下面三行代码的顺序,变换结果不相同
translate(mat, move);
scale(mat, scale);
rotate(mat, ang, axis);@P *= mat;

结果:略

如何确定变换顺序?

使用 maletransform()函数, 可在Houdini自带的外部脚本处查看具体用法(安装路径\houdini\vex\include\math.h),下面使用记事本打开math.h脚本

 如图所示,可以使用【XFORM_SRT】或【XFORM_0】等确定组合变换顺序。(截图后半部分是四元数相关的欧拉角转换顺序)。

eg.对上面的【eg】代码进行修改,

float ang = chf('angle');      //旋转角度 设置范围0~360
vector rot = set(0, ang, 0);             //沿Y轴旋转角度
vector move = set(chf('move'), 0, 0);   //暂设置为1~3
vector scale = set(chf('scale'), 1, 1); //暂设置为0-3int trs = chi('trs');       //位移、旋转、缩放变换顺序,范围0~5
int xyz = chi('xyz');       //旋转顺序,范围0~5matrix mat = maketransform(trs, xyz, move, rot, scale);@P *= mat;

结果:略

15、矩阵转换函数

cracktransform() 函数把4x4矩阵分解为位移、旋转、缩放。

使用该函数,必须知道/指定变换顺序、转换顺序

eg.使用【14、Combine Transformation Matrix组合变换矩阵】的节点及代码,
①在代码节点最后一行加上一行代码,

4@mat = mat;

添加并在下方连接类型为points的attributeWrangle节点,并写入以下代码,

int trs = chi('trs');    //与上一节点的 trs 复制粘贴相对引用
int xyz = chi('xyz');    //与上一节点的 xyz 复制粘贴相对引用vector t, r, s;
cracktransform(trs, xyz, set(0, 0, 0), 4@mat, t, r, s);v@t = t;
v@r = r;
v@s = s;

结果:略

16、Exercise1——基础变换

eg.一个box盒子节点、一个类型为Points的AttributeWrangle节点输入以下代码:

float rad = chf('rad');                 //后面用作在X轴上的位移距离
float ang = radians(chf('ang'));        //旋转角度  范围设置0~360float size = cos(ang * 4);              //一个360°周期内,进行了4次变换
size = fit(size, -1, 1, 1.0, 3.0);      //映射函数,把size值从-1到1重新映射为1.0到3.0matrix mat = ident();
translate(mat, set(0.5, 0, 0));         //设置中心点,因为box默认中心点为(0,0,0)且默认大小为1
scale(mat, set(size, 1, 1));            //X方向进行缩放
translate(mat, set(rad, 0, 0));         //在X轴上的位移
rotate(mat, ang, set(0, 0, 1));         //旋转角度,即转1圈@P *= mat;

结果:①滑动rad值;②在生成的通道中,把ang通道值改为:$FF / $FEND * 360,

17、Exercise2——扭曲和弯曲

eg.①一个box盒子节点、一个类型为Points的AttributeWrangle节点输入以下代码:

float boxheight = chf('boxheight');         //box高度,与box节点的Y轴高度【复制-粘贴引用】
float twistang = chf('twist_ang') * $PI;    //扭曲角度
float bendrad = chf('bend_rad');
float bendang = chf('bend_ang') * $PI;matrix mat = ident();//根据box的高度/Y轴坐标值映射为角度范围 (0.0, twistang)。因为box中心点在 (0,0,0),所以需要乘以0.5
float tang = fit(@P.y, -boxheight * 0.5, boxheight * 0.5, 0.0, twistang);   
rotate(mat, tang, set(0, 1, 0));        // 得到一个旋转矩阵,以Y轴为轴心旋转,旋转角度 0~tang°
//scale值为0.01~1.0~0.01   根据box的高度/Y轴坐标值映射为0.0~180°,角度范围对应sin函数值范围为0~1~0float scale = max(sin(fit(@P.y, -boxheight * 0.5, boxheight * 0.5, 0.0, $PI)), 0.01);    
scale(mat, set(scale, 1, scale));     //位于X、Y轴的点进行缩放,缩放为 0.01 ~ 1 ~ 0.01@P *= mat;

其中一些设置:box节点的细分设置为(5,50,5),高度设置为5(大小随意);AttributeWrangle节点的boxheight通道设置为:与box的高度绑定,即【复制-粘贴引用】,

结果为:基于上述条件,及twist_ang=1.5的情况下,(为了排版美观,下图旋转90°为正确结果)

 ②对代码进行添加弯曲代码,完整代码如下,

float boxheight = chf('boxheight');         
float twistang = chf('twist_ang') * $PI;   //添加代码
float bendrad = chf('bend_rad');            //弯曲半径/移动距离
float bendang = chf('bend_ang') * $PI;      //弯曲角度  范围暂设为0~2,即最大值为2π,即一个圆matrix mat = ident();float tang = fit(@P.y, -boxheight * 0.5, boxheight * 0.5, 0.0, twistang);   
rotate(mat, tang, set(0, 1, 0));      float scale = max(sin(fit(@P.y, -boxheight * 0.5, boxheight * 0.5, 0.0, $PI)), 0.01);    
scale(mat, set(scale, 1, scale));    //添加代码
translate(mat, set(-bendrad, 0, 0));  //box沿-X轴移动
//box的高度进行映射,映射的角度范围为 (0.0,bendang),当bendang = 2π时,即弯曲360°
float bang = fit(@P.y, -boxheight * 0.5, boxheight * 0.5, 0.0, bendang);    
rotate(mat, -bang, set(0, 0, 1));     //沿Z轴旋转@P *= mat;

 结果为:基于上述条件,及弯曲半径bendrad = 2,弯曲角度bendang=1(即弯曲π),

 ③扩展,对扭曲、弯曲的角度可添加帧相关代码,使其转起来

//看着位置添加,不懂就白学了
tang += $PI * 2 * @Frame / $FEND * chi('tangmult');     //引入帧相关,实现动态扭曲bang += $PI / 2 * @Frame / $FEND;     //动态旋转/弯曲

结果为:基于上述条件,及扭曲速率tangmult = 3的情况下,(图片不够直观,可以看B站视频第十五节的2h48m17s)

18、Exercise3——逆变换

 eg.①继续使用【17、Exercise2——Twisting and Bending扭曲和弯曲】的案例,在代码节点最后一行添加代码,

//对变换后的矩阵进行存储,方便调用
4@mat = mat;

 ②在下方添加及连接类型为points的attributeWrangle节点并写入代码,

matrix mat = 4@mat;matrix imat = invert(mat);@P *= imat;

 结果为:box打回原形,

19、二面体函数

将方向从一个向量更改为另一个向量方向。

eg.①如下图所示创建节点

②对节点进行设置:
line2、line3随便旋转下,color1、color2颜色随意;
polywire1节点是从线生成管道,细分设置8或者任意值;
dihedral节点类型为Points。
③在dihedral节点中写入以下代码,

vector cdir = normalize(point(1, 'P', 1));    //直线方向进行归一化
vector tdir = normalize(point(2, 'P', 1));    //直线方向进行归一化matrix mat = dihedral(cdir, tdir);            //创建矩阵。不懂看官方文档@P *= mat;

结果为:polywire对象由直线lin2(红色)的转向,变为直线line3(蓝色)的转向,

20、朝向函数

lookat() 函数用法官方文档截图如下,

该函数计算两个向量之间的差值向量(To-From),然后物体以-Z轴为起点旋转,旋转大小为两个向量的差值向量。
有点绕,点击可以看这个老哥的文章解释。

eg.理论:创建一个顶点指向-Z轴的圆锥,再创建一个随机点,使用 lookat() 函数使圆锥指向点。
实操:
如下图创建节点,并进行一些必要设置
circle1细分设置4;
transform1节点的Translate的Z轴改成-2;
polyloft1节点的U wrap选择On;
add1节点添加一个点,随便移动下;
lookat节点类型为Points,

 ②在lookat节点写入如下代码,

vector from = set(0, 0, 0);
vector to = point(1, 'P', 0);float roll = chf('roll');                //范围0~360
matrix3 mat = lookat(from, to, roll);@P *= mat;

 结果:圆锥指向随机点,滑动Roll值,变化如下图,

21、实例化函数

instance() 函数,创建一个给定参数的变换矩阵。类似复制。

eg.先上节点图及结果:

操作:可以拿【 20、Lookat VEX Function朝向函数】的案例进行修改,
①设置
circle1细分设置4;
transform1节点的Translate的Z值改成2;
polyloft1节点的U wrap选择On;
sphere3节点类型为Polygon;
normal1节点的Add Normals to选择Points,即点法线;
foreach节点类型为for-Each Point;
instance节点类型为Points,
②instance节点写入代码,

vector pos = point(1, 'P', 0);              //点位置
vector norm = point(1, 'N', 0);               //点法线
vector scale= set(1, 1, abs(pos.y) + 0.25);     //缩放matrix mat = instance(pos, norm, scale);@P *= mat;

22、Exercise4——矩阵插值

就是在两个矩阵变换中之间平滑过渡。

最好使用四元数进行旋转,而不是向量。

eg.直接使用【21、Instance VEX Function实例化函数】的案例,在instance节点添加代码,
完整代码如下:

vector pos = point(1, 'P', 0);              //点位置
vector norm = point(1, 'N', 0);               //点法线
vector scale= set(1, 1, abs(pos.y) + 0.25);     //缩放matrix mat = instance(pos, norm, scale);        //矩阵mat为最终状态vector t, r, s;
cracktransform(0, 0, set(0,0,0), mat, t, r, s);     //逆矩阵函数,把矩阵分解float lerpval = chf('lerpval');                     //插值系数 / 平滑过渡系数vector sl = lerp(set(1,1,1), s, lerpval);           //缩放插值,即缩放平滑过渡
vector tl = lerp(set(0,0,0), t, lerpval);           //位移插值,即位移旋转过渡
vector4 q = eulertoquaternion(radians(r), 0);       //旋转的欧拉角转为四元数,0表示旋转顺序
vector4 ql = slerp(quaternion(set(0,0,0)), q, lerpval);     //对旋转四元数进行插值
vector rl = quaterniontoeuler(ql, 0);                       //旋转插值,即旋转平滑过渡,0表示旋转顺序matrix lmat = ident();
scale(lmat, sl);
rotate(lmat, rl, 0);
translate(lmat, tl);@P *= lmat;

 结果:lerpval值0~1变化过程,
 

这篇关于HoudiniVex笔记_P15_MatrixBasics矩阵基础的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/321459

相关文章

从基础到进阶详解Pandas时间数据处理指南

《从基础到进阶详解Pandas时间数据处理指南》Pandas构建了完整的时间数据处理生态,核心由四个基础类构成,Timestamp,DatetimeIndex,Period和Timedelta,下面我... 目录1. 时间数据类型与基础操作1.1 核心时间对象体系1.2 时间数据生成技巧2. 时间索引与数据

安装centos8设置基础软件仓库时出错的解决方案

《安装centos8设置基础软件仓库时出错的解决方案》:本文主要介绍安装centos8设置基础软件仓库时出错的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录安装Centos8设置基础软件仓库时出错版本 8版本 8.2.200android4版本 javas

Linux基础命令@grep、wc、管道符的使用详解

《Linux基础命令@grep、wc、管道符的使用详解》:本文主要介绍Linux基础命令@grep、wc、管道符的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录grep概念语法作用演示一演示二演示三,带选项 -nwc概念语法作用wc,不带选项-c,统计字节数-

C/C++中OpenCV 矩阵运算的实现

《C/C++中OpenCV矩阵运算的实现》本文主要介绍了C/C++中OpenCV矩阵运算的实现,包括基本算术运算(标量与矩阵)、矩阵乘法、转置、逆矩阵、行列式、迹、范数等操作,感兴趣的可以了解一下... 目录矩阵的创建与初始化创建矩阵访问矩阵元素基本的算术运算 ➕➖✖️➗矩阵与标量运算矩阵与矩阵运算 (逐元

python操作redis基础

《python操作redis基础》Redis(RemoteDictionaryServer)是一个开源的、基于内存的键值对(Key-Value)存储系统,它通常用作数据库、缓存和消息代理,这篇文章... 目录1. Redis 简介2. 前提条件3. 安装 python Redis 客户端库4. 连接到 Re

SpringBoot基础框架详解

《SpringBoot基础框架详解》SpringBoot开发目的是为了简化Spring应用的创建、运行、调试和部署等,使用SpringBoot可以不用或者只需要很少的Spring配置就可以让企业项目快... 目录SpringBoot基础 – 框架介绍1.SpringBoot介绍1.1 概述1.2 核心功能2

Spring Boot集成SLF4j从基础到高级实践(最新推荐)

《SpringBoot集成SLF4j从基础到高级实践(最新推荐)》SLF4j(SimpleLoggingFacadeforJava)是一个日志门面(Facade),不是具体的日志实现,这篇文章主要介... 目录一、日志框架概述与SLF4j简介1.1 为什么需要日志框架1.2 主流日志框架对比1.3 SLF4

Spring Boot集成Logback终极指南之从基础到高级配置实战指南

《SpringBoot集成Logback终极指南之从基础到高级配置实战指南》Logback是一个可靠、通用且快速的Java日志框架,作为Log4j的继承者,由Log4j创始人设计,:本文主要介绍... 目录一、Logback简介与Spring Boot集成基础1.1 Logback是什么?1.2 Sprin

MySQL复合查询从基础到多表关联与高级技巧全解析

《MySQL复合查询从基础到多表关联与高级技巧全解析》本文主要讲解了在MySQL中的复合查询,下面是关于本文章所需要数据的建表语句,感兴趣的朋友跟随小编一起看看吧... 目录前言:1.基本查询回顾:1.1.查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J1.2.按照部门

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键