欢迎光临
我们一直在努力

鸿蒙开发-3D数学基础:向量、四元数和矩阵怎么用

向量、四元数、矩阵——搞懂这些数据类型才能玩转 3D

如果你之前只做过 2D 开发,突然跳到 3D 的世界,可能会被一堆数学名词搞懵:向量、四元数、矩阵、AABB 包围盒…这些到底是什么?为什么不能像 2D 里一样直接用 x、y 就完事了?

别慌,这篇文章就是来帮你理清这些概念的。ArkGraphics 3D 的 SceneType 模块定义了所有 3D 开发需要的基础数据类型,我们一个一个来看。

Vec2:二维向量

这是最简单的,你在 2D 开发里肯定见过。它就是两个数:x 和 y。

import { Vec2 } from '@kit.ArkGraphics3D';

let point: Vec2 = { x: 0.5, y: 0.3 };

在 3D 场景里,Vec2 常用于:

  • 屏幕坐标(归一化后的 [0,1] 范围)
  • UV 纹理坐标
  • 平面尺寸

比如射线检测的 viewPosition 参数就是 Vec2 类型,表示屏幕上的归一化坐标。

Vec3:三维向量

这是 3D 开发里最常用的类型。三个分量:x、y、z。

import { Vec3 } from '@kit.ArkGraphics3D';

let position: Vec3 = { x: 1.0, y: 2.0, z: 3.0 };

Vec3 在 3D 场景里无处不在:

  • 表示位置(Position3 就是 Vec3 的类型别名)
  • 表示方向(比如灯光方向、法线方向)
  • 表示缩放(Scale3 也是 Vec3 的类型别名)
  • 表示旋转(Rotation3 也是 Vec3,单位是弧度)
  • 表示颜色(RGB 三个分量)
  • 表示立方体的尺寸(宽、高、深)

你可以把 Vec3 想象成空间中的一个箭头,它既有方向也有长度。当它表示位置时,就是从原点到那个点的箭头;当它表示方向时,就是指向某个方向的单位箭头。

Vec4:四维向量

比 Vec3 多一个 w 分量。

import { Vec4 } from '@kit.ArkGraphics3D();

let color: Vec4 = { x: 1.0, y: 0.0, z: 0.0, w: 1.0 }; // 红色,完全不透明

Vec4 常用于:

  • RGBA 颜色(x=r, y=g, z=b, w=a)
  • 齐次坐标(3D 图形学中用于矩阵变换)
  • PBR 材质的属性因子

为什么需要第四个分量?打个比方:在做矩阵变换时,3×3 的矩阵只能表示旋转和缩放,加上第四维变成 4×4 矩阵后,就能统一表示平移、旋转、缩放和透视变换了。这就是齐次坐标的妙处。

Quaternion:四元数

这是 3D 开发中最让人头疼但又最重要的概念。简单说,四元数是一种表示旋转的方式,它比欧拉角(就是分别绕 x、y、z 轴旋转)更好用。

下面的流程图展示了 3D 开发中选择旋转表示方式的决策过程:

#mermaid-svg-uZ38uMc7WeuNhxQR{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uZ38uMc7WeuNhxQR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uZ38uMc7WeuNhxQR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uZ38uMc7WeuNhxQR .error-icon{fill:#552222;}#mermaid-svg-uZ38uMc7WeuNhxQR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uZ38uMc7WeuNhxQR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uZ38uMc7WeuNhxQR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uZ38uMc7WeuNhxQR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uZ38uMc7WeuNhxQR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uZ38uMc7WeuNhxQR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uZ38uMc7WeuNhxQR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uZ38uMc7WeuNhxQR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uZ38uMc7WeuNhxQR .marker.cross{stroke:#333333;}#mermaid-svg-uZ38uMc7WeuNhxQR svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uZ38uMc7WeuNhxQR p{margin:0;}#mermaid-svg-uZ38uMc7WeuNhxQR .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-uZ38uMc7WeuNhxQR .cluster-label text{fill:#333;}#mermaid-svg-uZ38uMc7WeuNhxQR .cluster-label span{color:#333;}#mermaid-svg-uZ38uMc7WeuNhxQR .cluster-label span p{background-color:transparent;}#mermaid-svg-uZ38uMc7WeuNhxQR .label text,#mermaid-svg-uZ38uMc7WeuNhxQR span{fill:#333;color:#333;}#mermaid-svg-uZ38uMc7WeuNhxQR .node rect,#mermaid-svg-uZ38uMc7WeuNhxQR .node circle,#mermaid-svg-uZ38uMc7WeuNhxQR .node ellipse,#mermaid-svg-uZ38uMc7WeuNhxQR .node polygon,#mermaid-svg-uZ38uMc7WeuNhxQR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uZ38uMc7WeuNhxQR .rough-node .label text,#mermaid-svg-uZ38uMc7WeuNhxQR .node .label text,#mermaid-svg-uZ38uMc7WeuNhxQR .image-shape .label,#mermaid-svg-uZ38uMc7WeuNhxQR .icon-shape .label{text-anchor:middle;}#mermaid-svg-uZ38uMc7WeuNhxQR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uZ38uMc7WeuNhxQR .rough-node .label,#mermaid-svg-uZ38uMc7WeuNhxQR .node .label,#mermaid-svg-uZ38uMc7WeuNhxQR .image-shape .label,#mermaid-svg-uZ38uMc7WeuNhxQR .icon-shape .label{text-align:center;}#mermaid-svg-uZ38uMc7WeuNhxQR .node.clickable{cursor:pointer;}#mermaid-svg-uZ38uMc7WeuNhxQR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uZ38uMc7WeuNhxQR .arrowheadPath{fill:#333333;}#mermaid-svg-uZ38uMc7WeuNhxQR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uZ38uMc7WeuNhxQR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uZ38uMc7WeuNhxQR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uZ38uMc7WeuNhxQR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uZ38uMc7WeuNhxQR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uZ38uMc7WeuNhxQR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uZ38uMc7WeuNhxQR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uZ38uMc7WeuNhxQR .cluster text{fill:#333;}#mermaid-svg-uZ38uMc7WeuNhxQR .cluster span{color:#333;}#mermaid-svg-uZ38uMc7WeuNhxQR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uZ38uMc7WeuNhxQR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uZ38uMc7WeuNhxQR rect.text{fill:none;stroke-width:0;}#mermaid-svg-uZ38uMc7WeuNhxQR .icon-shape,#mermaid-svg-uZ38uMc7WeuNhxQR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uZ38uMc7WeuNhxQR .icon-shape p,#mermaid-svg-uZ38uMc7WeuNhxQR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uZ38uMc7WeuNhxQR .icon-shape .label rect,#mermaid-svg-uZ38uMc7WeuNhxQR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uZ38uMc7WeuNhxQR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uZ38uMc7WeuNhxQR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uZ38uMc7WeuNhxQR :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

简单静态旋转

需要平滑插值动画

从欧拉角转换

需要组合多个旋转

需要表示旋转

旋转类型

使用四元数 Quaternion

使用四元数 Slerp插值

欧拉角转四元数公式

设置到节点rotation属性

四元数乘法

避免万向节锁问题


import { Quaternion } from '@kit.ArkGraphics3D';

let rotation: Quaternion = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }; // 不旋转(单位四元数)

四元数有四个分量:x、y、z、w。但你千万别把它们想象成"绕 x 轴转多少度、绕 y 轴转多少度"——不是这样的。这四个分量是一个整体,共同描述了一个旋转。

为什么不用欧拉角?因为欧拉角有一个致命缺陷叫"万向节锁"(Gimbal Lock)。简单说就是当两个旋转轴重合时,你会突然失去一个自由度,导致旋转行为异常。四元数就不存在这个问题,而且在做旋转插值(比如动画中的平滑过渡)时效果也更好。

四元数的基本操作

四元数虽然抽象,但你不需要手动去算它。ArkGraphics 3D 会帮你处理大部分运算。你只需要知道几个基本概念:

单位四元数:{ x: 0, y: 0, z: 0, w: 1 } 表示不旋转。这是四元数的"初始状态"。

绕轴旋转:要表示绕某个轴旋转一定角度,四元数的计算公式是:

  • x = axis.x * sin(angle/2)
  • y = axis.y * sin(angle/2)
  • z = axis.z * sin(angle/2)
  • w = cos(angle/2)

其中 axis 是旋转轴的单位向量,angle 是旋转角度(弧度)。

比如要绕 Y 轴旋转 90 度:

import { Quaternion } from '@kit.ArkGraphics3D';

let angle = Math.PI / 2; // 90度
let rotation: Quaternion = {
x: 0,
y: Math.sin(angle / 2),
z: 0,
w: Math.cos(angle / 2)
};

在节点上使用四元数

节点的 rotation 属性就是 Quaternion 类型。你可以直接设置它来旋转物体:

import { Scene, Node, Quaternion } from '@kit.ArkGraphics3D';

function rotateNode(): void {
let scene: Promise<Scene> = Scene.load($rawfile("gltf/CubeWithFloor/glTF/AnimatedCube.glb"));
scene.then(async (result: Scene) => {
let node: Node | null = result.getNodeByPath("rootNode_/Unnamed Node 1/AnimatedCube");
if (node) {
// 绕Y轴旋转45度
let angle = Math.PI / 4;
node.rotation = {
x: 0,
y: Math.sin(angle / 2),
z: 0,
w: Math.cos(angle / 2)
};
}
});
}

Color:颜色

3D 场景中的颜色用 RGBA 四个分量表示,每个分量的范围是 [0, 1]。

import { Color } from '@kit.ArkGraphics3D';

let red: Color = { r: 1.0, g: 0.0, b: 0.0, a: 1.0 };
let semiTransparentBlue: Color = { r: 0.0, g: 0.0, b: 1.0, a: 0.5 };

注意 Color 用的是 r、g、b、a 字母,而 Vec4 用的是 x、y、z、w。它们在数学上是等价的,但语义不同。

Color 用于灯光颜色、材质颜色、清屏颜色等场景。

Aabb:轴对齐包围盒

AABB(Axis-Aligned Bounding Box)是一个长方体的盒子,它的边永远和坐标轴平行。它主要用于碰撞检测和空间查询。

import { Aabb } from '@kit.ArkGraphics3D';

let box: Aabb = {
aabbMin: { x: 1, y: 1, z: 1 },
aabbMax: { x: 1, y: 1, z: 1 }
};

aabbMin 是盒子的最小角(x、y、z 都是最小值),aabbMax 是最大角。两个点就能确定一个长方体。

为什么叫"轴对齐"?因为它的边永远平行于 x、y、z 轴,不能旋转。这样做的好处是计算碰撞检测时非常快——只需要比较坐标值的大小就行。

在 ArkGraphics 3D 里,Mesh 和 SubMesh 都有 aabb 属性,表示这个网格在局部坐标系中的包围盒。射线检测时也会用到包围盒来快速排除不可能命中的物体。

Rect:矩形

表示平面上的一个矩形区域。

import { Rect } from '@kit.ArkGraphics3D';

let rect: Rect = {
x: 0,
y: 0,
width: 100,
height: 50
};

x 和 y 是左下角的坐标,width 和 height 是宽高。单位是世界坐标系下的场景单位。

Mat4x4:4×4 矩阵

下面的流程图展示了 3D 坐标变换的完整流程,体现了各数据类型的协作关系:

#mermaid-svg-o1F1hPLTgi903jY8{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-o1F1hPLTgi903jY8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-o1F1hPLTgi903jY8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-o1F1hPLTgi903jY8 .error-icon{fill:#552222;}#mermaid-svg-o1F1hPLTgi903jY8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o1F1hPLTgi903jY8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-o1F1hPLTgi903jY8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o1F1hPLTgi903jY8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o1F1hPLTgi903jY8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-o1F1hPLTgi903jY8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o1F1hPLTgi903jY8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o1F1hPLTgi903jY8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o1F1hPLTgi903jY8 .marker.cross{stroke:#333333;}#mermaid-svg-o1F1hPLTgi903jY8 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o1F1hPLTgi903jY8 p{margin:0;}#mermaid-svg-o1F1hPLTgi903jY8 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-o1F1hPLTgi903jY8 .cluster-label text{fill:#333;}#mermaid-svg-o1F1hPLTgi903jY8 .cluster-label span{color:#333;}#mermaid-svg-o1F1hPLTgi903jY8 .cluster-label span p{background-color:transparent;}#mermaid-svg-o1F1hPLTgi903jY8 .label text,#mermaid-svg-o1F1hPLTgi903jY8 span{fill:#333;color:#333;}#mermaid-svg-o1F1hPLTgi903jY8 .node rect,#mermaid-svg-o1F1hPLTgi903jY8 .node circle,#mermaid-svg-o1F1hPLTgi903jY8 .node ellipse,#mermaid-svg-o1F1hPLTgi903jY8 .node polygon,#mermaid-svg-o1F1hPLTgi903jY8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-o1F1hPLTgi903jY8 .rough-node .label text,#mermaid-svg-o1F1hPLTgi903jY8 .node .label text,#mermaid-svg-o1F1hPLTgi903jY8 .image-shape .label,#mermaid-svg-o1F1hPLTgi903jY8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-o1F1hPLTgi903jY8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-o1F1hPLTgi903jY8 .rough-node .label,#mermaid-svg-o1F1hPLTgi903jY8 .node .label,#mermaid-svg-o1F1hPLTgi903jY8 .image-shape .label,#mermaid-svg-o1F1hPLTgi903jY8 .icon-shape .label{text-align:center;}#mermaid-svg-o1F1hPLTgi903jY8 .node.clickable{cursor:pointer;}#mermaid-svg-o1F1hPLTgi903jY8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-o1F1hPLTgi903jY8 .arrowheadPath{fill:#333333;}#mermaid-svg-o1F1hPLTgi903jY8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-o1F1hPLTgi903jY8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-o1F1hPLTgi903jY8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o1F1hPLTgi903jY8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-o1F1hPLTgi903jY8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o1F1hPLTgi903jY8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-o1F1hPLTgi903jY8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-o1F1hPLTgi903jY8 .cluster text{fill:#333;}#mermaid-svg-o1F1hPLTgi903jY8 .cluster span{color:#333;}#mermaid-svg-o1F1hPLTgi903jY8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-o1F1hPLTgi903jY8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-o1F1hPLTgi903jY8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-o1F1hPLTgi903jY8 .icon-shape,#mermaid-svg-o1F1hPLTgi903jY8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o1F1hPLTgi903jY8 .icon-shape p,#mermaid-svg-o1F1hPLTgi903jY8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-o1F1hPLTgi903jY8 .icon-shape .label rect,#mermaid-svg-o1F1hPLTgi903jY8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o1F1hPLTgi903jY8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-o1F1hPLTgi903jY8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-o1F1hPLTgi903jY8 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

模型顶点: Vec3

模型矩阵: 平移+旋转+缩放

世界坐标: Vec3

视图矩阵: 相机变换

相机坐标: Vec3

投影矩阵: 透视变换

裁剪坐标: Vec4齐次坐标

透视除法

NDC坐标: Vec3

视口变换

屏幕坐标: Vec2

4×4 矩阵是 3D 图形学的数学基础。相机的视图矩阵和投影矩阵都是这个类型。

import { Mat4x4, Vec4 } from '@kit.ArkGraphics3D';

let identity: Mat4x4 = {
x: { x: 1, y: 0, z: 0, w: 0 }, // 第一列
y: { x: 0, y: 1, z: 0, w: 0 }, // 第二列
z: { x: 0, y: 0, z: 1, w: 0 }, // 第三列
w: { x: 0, y: 0, z: 0, w: 1 } // 第四列
};

Mat4x4 有四个属性 x、y、z、w,每个都是 Vec4 类型,分别代表矩阵的四列(注意是列主序)。

上面这个是单位矩阵,对角线全是 1,表示不做任何变换。实际使用中,你通常不需要手动构造矩阵,而是通过 camera.getViewMatrix() 和 camera.getProjectionMatrix() 来获取。

矩阵能表示的变换包括:

  • 平移(移动位置)
  • 旋转
  • 缩放
  • 透视投影

视图矩阵描述了"相机在哪里、朝哪个方向看",投影矩阵描述了"透视效果是什么样的"。两者结合就能把 3D 世界坐标转换为 2D 屏幕坐标。

几何类型

除了基本的数据类型,SceneType 还定义了几何相关的类型,用于创建自定义形状。

GeometryType 枚举

  • CUSTOM(0):自定义几何体
  • CUBE(1):立方体
  • PLANE(2):平面
  • SPHERE(3):球体
  • CYLINDER(4):圆柱体(API 23+)

PrimitiveTopology 枚举

控制顶点如何组成三角形:

  • TRIANGLE_LIST(0):每 3 个顶点构成一个独立的三角形
  • TRIANGLE_STRIP(1):每个新顶点和前一个三角形的一条边构成新三角形

内置几何体

CubeGeometry:立方体,只需指定 size(Vec3 类型,表示宽高深)

import { CubeGeometry } from '@kit.ArkGraphics3D';

let cube = new CubeGeometry();
cube.size = { x: 1, y: 1, z: 1 };

PlaneGeometry:平面,只需指定 size(Vec2 类型,表示宽高)

import { PlaneGeometry } from '@kit.ArkGraphics3D';

let plane = new PlaneGeometry();
plane.size = { x: 2, y: 3 };

SphereGeometry:球体,需要 radius(半径)和 segmentCount(分段数,越大越光滑,最少 3)

import { SphereGeometry } from '@kit.ArkGraphics3D();

let sphere = new SphereGeometry();
sphere.radius = 0.5;
sphere.segmentCount = 32;

CylinderGeometry(API 23+):圆柱体,需要 radius、height 和 segmentCount

import { CylinderGeometry } from '@kit.ArkGraphics3D';

let cylinder = new CylinderGeometry();
cylinder.radius = 0.5;
cylinder.height = 2;
cylinder.segmentCount = 20;

注意 segmentCount 不能太小(至少 3),太大会影响性能。设为浮点数会自动向下取整。

CustomGeometry:自定义几何体

如果你需要创建内置几何体没有的形状,可以用 CustomGeometry:

import { CustomGeometry, PrimitiveTopology, Vec2, Vec3, Color } from '@kit.ArkGraphics3D';

let geometry = new CustomGeometry();
geometry.vertices = [
{ x: 0, y: 0, z: 0 },
{ x: 1, y: 0, z: 0 },
{ x: 0.5, y: 1, z: 0 }
];
geometry.indices = [0, 1, 2];
geometry.topology = PrimitiveTopology.TRIANGLE_LIST;
geometry.normals = [
{ x: 0, y: 0, z: 1 },
{ x: 0, y: 0, z: 1 },
{ x: 0, y: 0, z: 1 }
];
geometry.uvs = [
{ x: 0, y: 0 },
{ x: 1, y: 0 },
{ x: 0.5, y: 1 }
];
geometry.colors = [
{ r: 1, g: 0, b: 0, a: 1 },
{ r: 0, g: 1, b: 0, a: 1 },
{ r: 0, g: 0, b: 1, a: 1 }
];

这里创建了一个三角形。vertices、indices、topology 是必须的,normals、uvs、colors 是可选的。

位置、旋转、缩放的类型别名

为了语义更清晰,ArkGraphics 3D 定义了三个类型别名:

  • Position3 = Vec3:位置,单位是世界坐标系下的场景单位
  • Rotation3 = Vec3:旋转,单位是弧度(注意这不是四元数,是欧拉角的表示方式)
  • Scale3 = Vec3:缩放比例

节点的 position 属性是 Position3 类型,scale 属性是 Scale3 类型,但 rotation 属性是 Quaternion 类型(不是 Rotation3)。所以你在设置旋转时需要用四元数,而不是欧拉角。

RenderingPipelineType:渲染管线

这个枚举决定了相机使用哪种渲染管线:

  • FORWARD_LIGHTWEIGHT(0):轻量级前向渲染管线,直接渲染到后缓冲区,只能在着色器中实现逐像素效果,不支持复杂效果(如光晕)
  • FORWARD(1):高质量前向渲染管线,支持复杂视觉效果

如果你的场景需要光晕(Bloom)等后处理效果,就必须用 FORWARD 管线。如果不需要,用 FORWARD_LIGHTWEIGHT 性能更好。

小结

这些数据类型是 3D 开发的"字母表":

  • Vec2/Vec3/Vec4:基础向量类型,无处不在
  • Quaternion:四元数,表示旋转的最佳方式,避免万向节锁
  • Color:RGBA 颜色
  • Aabb:轴对齐包围盒,用于碰撞检测
  • Mat4x4:4×4 矩阵,相机变换的基础
  • 几何类型:定义物体的形状

你不需要成为数学专家才能用好这些类型。大多数时候,你只需要知道"Vec3 表示位置/方向,Quaternion 表示旋转"就够了。只有在做自定义相机控制、坐标变换等高级操作时,才需要深入理解矩阵和四元数的数学原理。

下一篇文章我们来看看后处理效果——怎么给 3D 场景加上色调映射、泛光、暗角这些"滤镜"。

赞(0)
未经允许不得转载:171主机测评 » 鸿蒙开发-3D数学基础:向量、四元数和矩阵怎么用
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址