Our webinar Getting your point cloud ready for your application partly covers Normals.


This article explains surface normals, how to compute them from a point cloud in Zivid SDK, and why normals are a valuable feature. Lastly, it provides a tutorial with code examples.

A surface normal is an important property of a geometric surface. It represents a unit vector perpendicular to the tangent plane of the surface at a specific point in the point cloud. Since it is a vector, the surface normal comprises three vector elements (x, y, z) describing the 3D coordinates. All surface normals of a plane are parallel, while the surface normals of a sphere are pointing in all possible directions.

The normal describes the surface orientation, and the rate of change in the normal describes the curvature of a surface.

Normals API

std::cout << "Computing normals and copying them to CPU memory" << std::endl;
const auto normals = pointCloud.copyData<Zivid::NormalXYZ>();

The Normals API computes the normal at each point in the point cloud and copies normals from the GPU memory to the CPU memory. The result is a matrix of normal vectors, one for each point in the input point cloud. The size of normals is equal to the size of the input point cloud.


The Normals API in Zivid SDK is fast because the computation is done in parallel on the GPU while the point cloud data is still in GPU memory. Calculating normals with third-party libraries is likely more time-consuming: CPU computations are much slower in general, and GPU computations require another copy. See Point Cloud Capture Process for more info.

If you already have a buffer, then you can copy data directly from GPU memory to your destination buffer using copyData with the template parameter NormalXYZ.


  • Use Normals, Downsample, and Transform APIs through the Zivid SDK for performance reasons because the GPU performs all these computations while the point cloud data is still in GPU memory.

  • For the highest performance, first downsample the point cloud, then compute normals.

  • The normals are computed on the point cloud when it is requested to copy them. Compute normals after transforming the point cloud to have the normals in the same coordinate system as the point cloud.

  • Transforming normals requires transforming the point cloud first, then computing the normals from the transformed point cloud. It is not possible to only transform the normals.

Normals in applications

Different applications for various reasons require computing normals from the point cloud. A normal describes the surface orientation, and the rate of change in the normal describes the curvature of a surface.

Some examples of applications that utilize normals are CAD-based and surface-based matching, edge detection, and segmentation algorithms based on the difference of normals. Normals are also utilized by meshing algorithms and filtering algorithms to remove point cloud artifacts or outliers. For instance, normals that are almost perpendicular to the line from the camera origin to a point can indicate an artifact and be filtered out.

Compute Normals and Visualize them using PCL

This tutorial demonstrates how to compute normals from Zivid point cloud, convert point cloud and normals to PCL format, and visualize them in 3D as normal map.

First, we connect to the camera and capture a point cloud.

std::cout << "Connecting to camera" << std::endl;
auto camera = zivid.connectCamera();

std::cout << "Configuring settings" << std::endl;
Zivid::Settings settings;
for(const auto aperture : { 9.57, 4.76, 2.59 })
    std::cout << "Adding acquisition with aperture = " << aperture << std::endl;
    const auto acquisitionSettings = Zivid::Settings::Acquisition{
        Zivid::Settings::Acquisition::Aperture{ aperture },

std::cout << "Capturing frame (HDR)" << std::endl;
const auto frame = camera.capture(settings);
const auto pointCloud = frame.pointCloud();

Then we compute the normals for the point cloud.

std::cout << "Computing point cloud normals" << std::endl;
const auto normals = pointCloud.copyData<Zivid::NormalXYZ>();

We then convert point cloud and normals to PCL format.

std::cout << "Creating PCL point cloud structure" << std::endl;
const auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA>();
const auto pointCloudPCL = convertToPCLPointCloud(data);
std::cout << "Creating PCL normals structure suited for visualization" << std::endl;
const auto pointCloudWithNormalsPCL = convertToPCLVisualizationNormals(data, normals);

Finally, we visualize the point cloud and normals in 3D.

std::cout << "Visualizing normals" << std::endl;
visualizePointCloudAndNormalsPCL(pointCloudPCL.makeShared(), pointCloudWithNormalsPCL.makeShared());

Version History




Normals API is added.