---
kind: concept
slug: point-to-grid-interpolation
title: Point-to-grid interpolation (trilinear gridding)
summary: >-
  Scatters each point of a cloud onto the eight corners of its grid cell with trilinear weights, so a regular 3D grid can feed ordinary 3D convolutions. Dividing each vertex sum by its neighbour count, as the paper writes it, keeps values from growing with local point density; the released code sums without the count. Only one paper card covers this concept.
tags: [point-cloud, 3d-grid, interpolation, shape-completion]
papers: ["2006.03761"]
related: [explicit-feature-grid, chamfer-distance]
status: draft
updated_at: 2026-10-24
---

## Overview

A point cloud has no order and no fixed grid, so convolutions cannot read it
directly. Point-to-grid interpolation places each point on a regular grid of
vertices. The point adds a weight to the eight vertices of the cell that
contains it, and the weight falls off with distance along each axis
[arxiv:2006.03761 §3.2] [arxiv:2006.03761 eq. 3]. The result is a dense array
that a 3D CNN can process [arxiv:2006.03761 §3.3].

The GRNet authors argue against the simpler alternative. Voxelising a cloud
into binary cells loses geometric information, and the paper says the result
is not a differentiable reconstruction target [arxiv:2006.03761 §1]
[arxiv:2006.03761 §3.2]. Trilinear weights keep each point's contribution
continuous in its position.

Only one paper card lists this concept, so the variants below are the forms
found in that paper. They are not a survey of point-to-grid methods. The
reverse step, grid to points, is a different operation (Gridding Reverse, then
Cubic Feature Sampling) and is not covered here [arxiv:2006.03761 §3.4]
[arxiv:2006.03761 §3.5].

## Variants

### Trilinear splat, averaged over neighbours (as written)

Each vertex value is the sum of the trilinear weights of the points that
neighbour it, divided by the number of those neighbours [arxiv:2006.03761 §3.2]
[arxiv:2006.03761 eq. 2]. A vertex with no neighbour gets 0 [arxiv:2006.03761
§3.2]. Card: [GRNet](/papers/2006.03761).

The division makes the vertex value an average, so it does not depend on how
many points fall near that vertex. The paper's stated grid resolution is 64
vertices per axis [arxiv:2006.03761 §4.7] [arxiv:2006.03761 tab. 5].

### Trilinear splat, summed (as released)

The released CUDA kernel adds each trilinear product to its vertex and does
not divide by the neighbour count [code: extensions/gridding/gridding.cu#L137-L175].
The vertex value therefore grows with local point density. The card records
this as a difference between the paper and the code, and does not say which
form produced the reported numbers [arxiv:2006.03761 §3.2] [arxiv:2006.03761 eq. 2].

## When to use which

| Situation | Use | Why |
|---|---|---|
| Point density varies across inputs, and grid magnitudes should not depend on it | Averaged form (eq. 2) | Dividing by the neighbour count makes each vertex an average, so the value does not scale with how many points cluster there [arxiv:2006.03761 eq. 2] |
| You need to match the released code's behaviour, for example to compare runs | Summed form | The released kernel does not divide by the count [code: extensions/gridding/gridding.cu#L137-L175]. The card does not show that the paper's numbers came from it |
| The grid must be differentiable with respect to the point positions and feed a CNN | Trilinear splat, either form | The paper rejects binary voxelisation for this purpose [arxiv:2006.03761 §1] [arxiv:2006.03761 §3.2]. Weights are continuous in the position |
| Finer detail is needed and the budget allows it | 64 vertices per axis | Its ablation gives Chamfer 2.723 against 5.943 at 32 per axis (x10^-4, complete output) [arxiv:2006.03761 tab. 5]. The paper gives no cost of the gridding step alone; the whole network at 64 per axis has about 76.70 M parameters [arxiv:2006.03761 tab. 5] |
| Inputs are not in the unit cube | Normalise the cloud first | The paper states no normalisation. The kernel computes cell indices with no range check, so points outside [-1, 1]^3 index outside the grid [code: extensions/gridding/gridding.cu#L49-L76] |

## Implementation sketch

Pseudocode for the forward splat, with the averaging step as a flag. The
paper does not state the map from coordinates to vertex units, so that step is
left to the implementer.

```pseudocode
# Trilinear splat of a point cloud onto an n^3 grid  [§3.2] [eq. 2] [eq. 3]
# Input:  points (P, 3) in [-1, 1]^3, n vertices per axis
# Output: grid (n, n, n)

def point_to_grid(points, n, average=True):
    grid = zeros(n, n, n)
    count = zeros(n, n, n)
    for p in points:
        # Position in vertex units; the paper does not state this map  [§3.2]
        x = to_vertex_units(p, n)
        # The point's cell has eight corner vertices  [§3.2]
        for v in cell_vertices(x):
            # Product over the three axes of 1 - |v - x|  [eq. 3]
            w = prod(1 - abs(v - x))
            grid[v] += w
            count[v] += 1
    if average:
        # Eq. 2: divide each sum by its neighbour count; empty vertices stay 0  [§3.2]
        grid = where(count > 0, grid / count, 0)
    return grid
```

## Contradictions

- **Averaging.** The paper divides each vertex sum by the number of neighbours
  [arxiv:2006.03761 §3.2] [arxiv:2006.03761 eq. 2]. The released kernel does not
  [code: extensions/gridding/gridding.cu#L137-L175]. Both are stated; this page
  does not choose between them.

## Papers

- [GRNet: Gridding Residual Network for Dense Point Cloud Completion](/papers/2006.03761):
  defines the trilinear gridding step and the grid-to-point step that follows it.
