Godot PointMesh 完整指南:如何把单点图元变成低成本粒子与点云
【免费下载链接】godot-docsGodot Engine official documentation项目地址: https://gitcode.com/GitHub_Trending/go/godot-docs
Godot 引擎的PointMesh是一种只含单个顶点的图元网格(Primitive Mesh):它不生成任何三角形,渲染时该顶点会被画成屏幕上大小恒定的一个矩形“点”,并必须配合设置了点大小(point size)的材质才能被看见。本文以官方类参考 class_pointmesh.rst(L22–L26)为主线,结合 Mesh 枚举(L132–L134)、BaseMaterial3D 属性 与 空间着色器参考(L316),讲清点渲染的运行机制、两种点大小配置方式、粒子系统与点云的实战做法,以及一组高频避坑清单。
核心概念
结论先行:PointMesh 是PrimitiveMesh家族中几何体“最小”的一员——单点、零三角形、恒定像素大小,官方定位是粒子系统载体与廉价广告牌精灵(billboarded sprites),典型如点云(point cloud)可视化(见 class_pointmesh.rst L22)。
继承链(L13)如下:
PointMesh < PrimitiveMesh < Mesh < Resource < RefCounted < Object- PrimitiveMesh:所有图元网格基类,负责给网格挂
Material,并提供custom_aabb、flip_faces、add_uv2等通用属性(class_primitivemesh.rst L24); - Mesh:网格资源基类,定义表面数据与
PrimitiveType枚举,其中PRIMITIVE_POINTS = 0的官方解释是 “Render array as points (one vertex equals one point)”(class_mesh.rst L132–L134)。
与同族图元网格对比(完整子类清单见 class_primitivemesh.rst L15):
| 图元网格 | 几何构成 | 三角形数量 | 典型用途 |
|---|---|---|---|
BoxMesh/SphereMesh/CylinderMesh等 | 实体表面 | 数十至数千 | 常规 3D 物体 |
PlaneMesh/QuadMesh | 单个平面 | 2 | 地板、贴片、粒子贴图载体 |
RibbonTrailMesh/TubeTrailMesh | 沿曲线拉伸的带/管 | 可变 | 粒子拖尾(trail) |
PointMesh | 单个点 | 0 | 粒子本体、点云、廉价广告牌 |
正因为“零三角形”,PointMesh 的顶点处理、光栅化与内存开销都是图元家族中最低的一档——这也是它被选作海量粒子渲染载体的根本原因。
工作原理
本质上,PointMesh 是一张只含 1 个顶点的“点图元”表面。GPU 绘制它时不走三角形光栅化流程:顶点经投影落到屏幕后,直接展开为一个大小恒定的矩形(相当于隐式的广告牌朝向),像素尺寸由材质或着色器显式指定。
关键在于三个机制:
- 大小恒定,与距离无关。点尺寸以像素为单位,
point_size默认值就是1.0像素(class_basematerial3d.rst L3055–L3066)。这与三角形网格的透视缩放截然不同:拉远相机,点不会变小,只会“看起来”离得远。这正是粒子、地图标记类需求的理想行为,却是普通 3D 物体的禁忌——需要透视缩放时必须自己在着色器里算。 - 必须显式给点大小。官方描述明确:点网格只有配上一个“带 point size 的材质”才能被显示(class_pointmesh.rst L24)。否则点以极小的默认尺寸渲染,肉眼看就是“没画出来”。
- 点内坐标可读取。片段阶段可用只读变量
POINT_COORD(in vec2,0.0–1.0 范围)拿到当前像素在点矩形内的归一化坐标(spatial_shader.rst L388),据此做圆形裁剪、径向渐变,无需任何额外几何。
代价方面:单点没有表面与法线概念,所有依赖“面”的材质行为(背面剔除、沿法线膨胀、面向相机的旋转)对它就失效了,详见后文「避坑与限制」。
配置方式
结论:点大小有两条互斥的实现路径——BaseMaterial3D属性(无代码、编辑器可调)与着色器内置变量POINT_SIZE(可编程、按点动态控制)。
| 维度 | 方式一:BaseMaterial3D 属性 | 方式二:着色器 POINT_SIZE |
|---|---|---|
| 入口 | use_point_size(默认false)+point_size(默认1.0像素) | ShaderMaterial顶点着色器写POINT_SIZE |
| 粒度 | 整个网格统一值 | 每个顶点/每帧可动态计算 |
| 适用 | 静态点云、简单粒子 | 随距离/数据变化的粒子、点云 |
| 定位 | class_basematerial3d.rst L3706–L3719、L3055–L3066 | spatial_shader.rst L316(inout float) |
值得注意的是,use_point_size在BaseMaterial3D.Flags枚举中编号为FLAG_USE_POINT_SIZE = 3(L1046),也可用set_flag()/get_flag()以标志位方式读写;官方注释强调该选项只对基于点而非三角形的几何体生效(L3719)。
方式一:BaseMaterial3D 属性(GDScript)
var mesh_instance := MeshInstance3D.new() add_child(mesh_instance) var point_mesh := PointMesh.new() var mat := StandardMaterial3D.new() mat.use_point_size = true # 关键:开启点大小支持 mat.point_size = 8.0 # 点直径,单位像素(默认仅 1 像素) mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED # 纯点绘制常配无光照 point_mesh.material = mat mesh_instance.mesh = point_meshC# 等价写法:
var pointMesh = new PointMesh(); var mat = new StandardMaterial3D(); mat.UsePointSize = true; mat.PointSize = 8.0f; pointMesh.Material = mat;方式二:着色器内置变量
shader_type spatial; void vertex() { POINT_SIZE = 16.0; // 单位像素;可改为按距离/属性动态计算 } void fragment() { // POINT_COORD 为点内归一化坐标,把方形点裁剪为圆形 vec2 d = POINT_COORD - vec2(0.5); if (dot(d, d) > 0.25) { discard; } ALBEDO = vec3(0.8, 0.4, 0.9); }若你从 GLSL 迁移而来,官方对照表可直接将gl_PointSize换成POINT_SIZE、gl_PointCoord换成POINT_COORD(converting_glsl_to_godot_shaders.rst L99–L101)。2D 场景下CanvasItem着色器同样提供POINT_SIZE(canvas_item_shader.rst L145),写法完全一致。
继承的通用能力
PointMesh 作为PrimitiveMesh子类,自动获得以下属性与方法(class_primitivemesh.rst L31–L60):
| 成员 | 类型 / 默认值 | 说明 |
|---|---|---|
material | Material | 当前图元网格的材质——点大小就在这里配置 |
custom_aabb | AABB(0,0,0, 0,0,0) | 用户自定义包围盒,供视锥剔除使用;着色器偏移顶点时尤其有用 |
flip_faces | false | 反转三角形顶点顺序(等价cull_mode = CULL_FRONT)——点无面朝向,实际无意义 |
add_uv2 | false | 生成光照贴图用 UV2 坐标,配合uv2_padding留白 |
uv2_padding | 2.0 | UV2 接缝处留白像素;值越小贴图利用率越高,但可能渗色 |
get_mesh_arrays() | Array | 返回构成表面的网格数组,可传给ArrayMesh.add_surface_from_arrays()重组新表面(官方示例 L188–L200) |
_create_mesh_array() | Array(virtual) | 虚方法,可覆写以自定义图元生成方式;PointMesh 的实现即产出仅含单顶点的点表面 |
request_update() | void | 依据属性变化请求重建图元网格 |
对 PointMesh 而言最有实用价值的是custom_aabb:由于点本身几乎不占空间,当你在着色器里把点“推开”时,默认包围盒可能触发意外剔除,手动指定包围盒即可规避。
实战场景
场景一:3D 粒子系统(官方首要用途)
GPUParticles3D与CPUParticles3D都用网格定义“每个粒子长什么样”,官方对后者的mesh属性说明为:“The Mesh used for each particle. If null, particles will be spheres.”(class_cpuparticles3d.rst L1398)。把该网格换成 PointMesh,每个粒子就是一个恒定大小的点,是最廉价的粒子渲染方案之一。
- 创建
GPUParticles3D(或CPUParticles3D)节点; - 新建
PointMesh资源,为其指定一个开启use_point_size的StandardMaterial3D(或用写了POINT_SIZE的ShaderMaterial); - 将 PointMesh 赋给粒子的绘制通道网格(编辑器:Process Material → Draw Passes;代码:
set_draw_pass_mesh(0, point_mesh),见 class_gpuparticles3d.rst L990–L994); - 在 Process Material 中调发射率、寿命与
scale_*曲线。
与
RibbonTrailMesh形成互补:粒子本体用 PointMesh,拖尾用 Ribbon/Tube 网格(需同时开启材质的use_particle_trails,见 class_gpuparticles3d.rst L785–L787)。
场景二:点云(Point Cloud)可视化
用MultiMesh(MultiMeshInstance3D)批量实例化同一个 PointMesh:所有实例共享一份几何数据,GPU 以单次 draw call完成上千实例,API 开销远低于逐个MeshInstance3D(class_multimesh.rst L25–L27)。
- 创建
MultiMeshInstance3D,其multimesh.mesh设为共享的 PointMesh 资源; - 设置
instance_count,按采样数据逐实例写入transform; - 手动提供
aabb——MultiMesh 作为整体做可见性判断,包围盒必须覆盖全部实例(L31),否则远处实例可能被整体剔除; - 需要每点不同颜色时,可配合 MultiMesh 的
colors数组。
场景三:低开销广告牌精灵
需要大量“始终面向相机”的小标记(地图钉、远处特效、标注)时,PointMesh 的单点渲染天然满足广告牌需求,且无需配置billboard_mode——点渲染本身就恒定正对屏幕。
- 为每个标记放置一个
MeshInstance3D,或统一走MultiMeshInstance3D; - 材质用
SHADING_MODE_UNSHADED避免光照影响标记辨识度; - 若希望“远处的点更小”,在顶点着色器中基于深度换算像素后写入
POINT_SIZE(例如借助PROJECTION_MATRIX投影到 NDC 再换算),不能依赖默认透视缩放。
避坑与限制
以下每条均按「现象 → 原因 → 对策」给出,全部来自官方类参考的显式声明或可直接验证的默认行为。
现象:PointMesh 挂上后“看不见”,或只剩 1 像素的微小亮点。原因:未开启点大小支持,
point_size默认仅1.0像素(class_basematerial3d.rst L3059),多数屏幕上难以察觉。对策:设use_point_size = true并把point_size调到可辨值(如8.0),或着色器中写入POINT_SIZE。现象:设置
billboard_mode、grow、cull_mode后毫无变化。原因:官方明确声明点网格会忽略这些“影响顶点的属性”(class_pointmesh.rst L26)。点已恒定正对屏幕,且没有表面/法线,grow的沿法线膨胀与cull_mode的背面判定都无从谈起。对策:不要依赖这三项做点渲染增强;“面向相机”是点渲染的免费特性。现象:
flip_faces = true无效。原因:该属性反转的是三角形顶点顺序,点图元没有面,不存在正反面。对策:忽略即可,无需替代方案。现象:点被画成矩形,想要圆形或其他形状。原因:GPU 把点图元光栅化为屏幕轴对齐矩形,这是硬件层面的固定行为。对策:在片段着色器用
POINT_COORD做形状裁剪(discard)或径向渐变,见上文方式二的示例代码。现象:把 PointMesh 赋给
CSGMesh3D不可行。原因:CSGMesh3D的mesh属性类型提示明确排除了PointMesh、PlaneMesh、QuadMesh、RibbonTrailMesh,理由是这些图元非流形(non-manifold),不满足 CSG 布尔运算的拓扑要求(class_csgmesh3d.rst L86)。对策:CSG 场景改用BoxMesh、SphereMesh、CapsuleMesh等实体图元。现象:相机拉近拉远,点大小纹丝不动。原因:点尺寸以像素恒定,不受透视影响——这是设计特性而非 Bug。对策:确需透视缩放时,在顶点着色器按深度手动计算
POINT_SIZE。
常见问题 FAQ
Q1:为什么我的点完全不可见?九成是没配点大小。默认point_size = 1.0且use_point_size = false,等于按最小尺寸渲染。开启use_point_size或着色器写POINT_SIZE后立即可见。
Q2:PointMesh 能用在 2D 里吗?能。2D 的点渲染不依赖 PointMesh 资源,而是走CanvasItem着色器(如Point2D/粒子 2D 的自定义着色器),其中同样提供POINT_SIZE(canvas_item_shader.rst L145)与POINT_COORD(L236)。
Q3:点云里每个点想用不同颜色,怎么做?用MultiMeshInstance3D的colors属性为每个实例着色;材质侧开启vertex_color_use_as_albedo让颜色参与混色,或着色器中读取实例颜色。
Q4:粒子数上万时用 PointMesh 一定比 Quad 网格快吗?通常是的:单顶点图元的顶点数据、索引与光栅化开销都远低于四边形(2 三角形)。但要留意 GPU 点尺寸上限——部分驱动对超大point_size有截断,超大规模时可适当调小点数、用更亮的颜色补偿观感。
Q5:custom_aabb对 PointMesh 重要吗?重要。点几何几乎零体积,默认包围盒极小;若着色器把点位置大幅偏移(如模拟扩散),实体会跑出剔除范围。把custom_aabb设为覆盖最终点云范围的 AABB 可避免“点凭空消失”。
总结
三条核心要点:
- PointMesh = 单顶点零三角形图元,点以恒定像素尺寸渲染,天然正对屏幕,是粒子、点云与廉价广告牌的最优几何载体;
- 可见的前提是显式点大小:
BaseMaterial3D.use_point_size + point_size(静态值)或着色器POINT_SIZE(可编程),二选一; - 不要依赖顶点级材质属性:
billboard_mode、grow、cull_mode、flip_faces对点无效;形状裁剪交给片段阶段的POINT_COORD。
延伸阅读(均位于本仓库):
- 类参考:PointMesh、PrimitiveMesh、Mesh、BaseMaterial3D、MultiMesh、GPUParticles3D、CPUParticles3D、CSGMesh3D
- 着色器:空间着色器参考、CanvasItem 着色器参考、GLSL 迁移对照
- 教程:创建 3D 粒子系统、粒子拖尾
【免费下载链接】godot-docsGodot Engine official documentation项目地址: https://gitcode.com/GitHub_Trending/go/godot-docs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考