Nodes/ComfyUI libigl/Add Normals to PointCloud
ComfyUI Node

Add Normals to PointCloud

Point clouds without normals are half a cloud

By PozzettiAndrea·Created 10 months ago·Updated 4 days ago· 218
Add Normals to PointCloud
  • pointcloud
  • pointcloud_with_normals
  • info
methodopen3d_knn
k_neighbors30
search_radius0.050
mls_smoothing5
orient_normalstrue
add_as_attributestrue

A raw point cloud is just positions - a pile of coordinates with no idea which way the surface faces. A lot of downstream work needs those normals: Poisson surface reconstruction, curvature estimation, normal-based filtering, shading. This node estimates surface normals for a point cloud, which is the difference between "a cloud" and "a cloud that knows which way is out."

One important note up front: the input is called pointcloud and the tooltip is explicit - it will reject meshes with faces. This node is for true point clouds, and it's the natural follow-on to the pack's Mesh to Point Cloud (which outputs a face-less TRIMESH). Give it that, and you get back pointcloud_with_normals plus an info string.

The method dropdown picks the estimation strategy, each with its own parameters:

  • open3d_knn (default) - fits a local plane via PCA to each point's k_neighbors nearest neighbors (default 30) and takes the plane normal. The workhorse: fast, robust, good on evenly-sampled clouds.
  • open3d_radius - same PCA idea but using all points within search_radius instead of a fixed count. Better on clouds with uneven density, where a fixed neighbor count misbehaves.
  • pymeshlab_mls - moving least squares, driven by mls_smoothing iterations. Smoothes the local surface estimate as it goes, which helps on noisy capture data.

Two toggles finish it off:

  • orient_normals - try to orient all normals consistently (all pointing "out"). On its own, PCA can only estimate the normal line, not the direction - this is what disambiguates. Keep it on.
  • add_as_attributes - also store the normals as per-vertex attributes (normal_x/y/z) so VTK-based visualization tools can read them directly.

Where it fits

The classic chain: Mesh to Point Cloud → this node → reconstruction or analysis. If your cloud came from a scan, run this before Poisson reconstruction - Poisson assumes oriented normals and will silently produce garbage without them. That's the failure mode to know: not an error, just a lumpy, inside-out surface.

Install

Part of the pack; install once, get all nodes:

cd ComfyUI/custom_nodes
git clone https://github.com/PozzettiAndrea/ComfyUI-GeometryPack.git
cd ComfyUI-GeometryPack
pip install -r requirements.txt --upgrade
python install.py

Or ComfyUI Manager → GeometryPack. The method dropdown depends on the bundled environments (Open3D and PyMeshLab both arrive via the pack's experimental comfy-env/pixi install, which can be a big first download). Fast-moving repo, reports to Discussions, all the usual.

The honest advice

Start with open3d_knn and only switch if the result looks wrong - the neighbor-count method is the default for a reason. If your cloud has wildly uneven density (dense clusters and sparse gaps), open3d_radius is the more principled pick. And don't skip orient_normals thinking you'll fix direction later - you usually can't, cheaply. Normals are the one thing everyone assumes you already did.

Categorygeompack/repair

Inputs (7)

NameTypeDefaultDescription
pointcloudTRIMESHInput point cloud (will reject meshes with faces)
methodCOMBOopen3d_knnNormal estimation method
k_neighborsoptINT303–100[open3d_knn] Number of nearest neighbors for PCA
search_radiusoptFLOAT0.0500.001–1[open3d_radius] Search radius for neighborhood (in normalized space)
mls_smoothingoptINT51–20[pymeshlab_mls] MLS smoothing iterations
orient_normalsoptBOOLEANtrueOrient normals consistently across surface
add_as_attributesoptBOOLEANtrueAlso store normals as vertex_attributes (normal_x/y/z) for VTK visualization

Outputs (2)

NameTypeDescription
pointcloud_with_normalsTRIMESH
infoSTRING