Paper · 2021-06-20 · cs.CV, cs.GR
NeuS: Learning Neural Implicit Surfaces by Volume Rendering for Multi-view Reconstruction
NeuS reconstructs surfaces from posed images by training a neural SDF with volume rendering. It converts the SDF into an opaque density whose rendering weights peak exactly at the zero-level set (unbiased to first order) while still handling occlusion. On 15 DTU scenes it reports mean Chamfer 0.77 with masks (IDR 0.90) and 0.84 without (UNISURF 1.02).
Neural implicit surfacesNeural radiance field (NeRF) and variantsMulti-view reconstruction3d-reconstructionsdfvolume-renderingnerfmulti-view
arXivPDFCodeMarkdown13 min read
AI-compiled by claude-opus-5-5 on 2026-10-18, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Given a few dozen photos of an object taken from known camera positions, we want a clean 3D surface mesh of it. One family of neural methods models the surface directly and renders only the point where each camera ray hits it. Those give crisp surfaces but need a cut-out mask of the object in every photo and tend to get stuck on objects with holes, thin parts or sudden depth jumps. Another family (NeRF and its relatives) models a fuzzy "fog" of density and blends colors along the whole ray. That trains robustly, but the fog has no precise surface, so meshes pulled out of it are noisy.
NeuS keeps the robust fog-style rendering but makes the fog come from a signed distance function: a network that tells, for any point, how far it is from the surface, positive outside and negative inside. The surface is where that distance is zero. The trick is in how distance is turned into fog. The obvious conversion makes the rendered color come mostly from a point slightly in front of the true surface, so the learned surface ends up in the wrong place. The authors derive a different conversion whose rendering weight peaks right on the surface (for locally flat surfaces) and still lets the nearest surface hide the ones behind it.
A single learned "sharpness" number controls how thick the fog layer around the surface is; it starts blurry and tightens during training. The result is a mesh you can extract directly from the zero level of the network, trained from photos alone, with or without masks.
TL;DR
NeuS represents geometry as an MLP signed distance function f plus a view-dependent color MLP, and trains both by volume rendering against input images. Instead of plugging an SDF-derived density straight into NeRF's w = T\sigma (which peaks before the surface), it defines an opaque density \rho from the logistic CDF \Phi_s of the SDF so that, discretised, \alpha_i = \max((\Phi_s(f_i) - \Phi_s(f_{i+1}))/\Phi_s(f_i), 0). The weight is occlusion-aware and its maximum lies on the zero-level set to first order. A single trainable scalar s (inverse standard deviation) sharpens the surface over training. Author-reported mean Chamfer distance on 15 DTU scans: 0.77 with masks, 0.84 without, best among IDR, NeRF, COLMAP and UNISURF in both settings.
Problem
Surface-rendering methods such as DVR and IDR need foreground masks, get trapped in local minima and struggle with severe self-occlusion, thin structures and abrupt depth changes, because gradients only flow through the single ray-surface intersection §1 fig. 1. Volume-rendering methods such as NeRF optimise robustly but their density field lacks surface constraints, so extracted level-set meshes are noisy §1 §4.2. Naively using an SDF-derived density in standard volume rendering introduces a systematic depth bias: the rendering weight is maximal before the ray reaches the surface §3.1 fig. 2 app. B. Assumptions: posed images, region of interest inside a unit sphere §4.1.
Method
Representation §3.1. Two MLPs: f:\mathbb{R}^3\to\mathbb{R} (SDF) and c:\mathbb{R}^3\times\mathbb{S}^2\to\mathbb{R}^3 (color from position and view direction). The surface is the zero-level set:
\mathcal{S} = \{ \mathbf{x} \in \mathbb{R}^3 \mid f(\mathbf{x}) = 0 \} eq. 1
The S-density is \phi_s(f(\mathbf{x})) with the logistic density \phi_s(x) = s e^{-sx}/(1+e^{-sx})^2, the derivative of the sigmoid \Phi_s(x) = (1+e^{-sx})^{-1}. Its standard deviation is 1/s (as written in the paper), and s is trainable; 1/s is expected to approach 0 as training converges §3.1.
Rendering §3.1. For a ray \mathbf{p}(t) = \mathbf{o} + t\mathbf{v}:
C(\mathbf{o}, \mathbf{v}) = \int_0^{+\infty} w(t)\, c(\mathbf{p}(t), \mathbf{v})\, dt eq. 2
Two requirements on w §3.1:
- Unbiased: w has a local maximum at the surface intersection t^* with f(\mathbf{p}(t^*))=0.
- Occlusion-aware: for two points with equal SDF, the nearer one gets the larger weight.
Three candidate weights:
- Naive solution eq. 3: w(t) = T(t)\sigma(t) with \sigma(t) = \phi_s(f(\mathbf{p}(t))) and T(t) = \exp(-\int_0^t \sigma(u)du). Occlusion-aware but biased: dw/dt(t^*) = -T(t^*)\phi_s(0)^2 < 0, so the peak is before the surface §3.1 app. B.
- Straightforward construction eq. 4: normalised S-density w(t) = \phi_s(f(\mathbf{p}(t))) / \int_0^{\infty}\phi_s(f(\mathbf{p}(u)))du. Unbiased but not occlusion-aware: two surfaces on a ray get blended equally §3.1.
- NeuS eq. 5: w(t) = T(t)\rho(t), T(t) = \exp(-\int_0^t \rho(u)du) with the opaque density below.
Deriving \rho §3.1 eq. 6–10. For a single plane infinitely far from the camera, f(\mathbf{p}(t)) = -|\cos\theta|(t - t^*), and eq. 4 reduces to w(t) = |\cos\theta|\,\phi_s(f(\mathbf{p}(t))) eq. 6. Setting this equal to T(t)\rho(t) = -dT/dt and integrating gives T(t) = \Phi_s(f(\mathbf{p}(t))) eq. 7 eq. 8, hence
\rho(t) = \frac{-\frac{d\Phi_s}{dt}(f(\mathbf{p}(t)))}{\Phi_s(f(\mathbf{p}(t)))} eq. 9
For multiple intersections the numerator becomes negative where the SDF increases along the ray (exiting a surface), so it is clipped:
\rho(t) = \max\left(\frac{-\frac{d\Phi_s}{dt}(f(\mathbf{p}(t)))}{\Phi_s(f(\mathbf{p}(t)))}, 0\right) eq. 10
Expanded with the chain rule: \rho(t) = \max\left(\frac{-(\nabla f(\mathbf{p}(t))\cdot\mathbf{v})\,\phi_s(f(\mathbf{p}(t)))}{\Phi_s(f(\mathbf{p}(t)))}, 0\right) app. A.
Theorem 1 §3.1 app. B: if a ray enters a smooth surface from outside, f decreases monotonically on [t_l, t_r] \ni t^*, and the surface is locally planar (\nabla f fixed), then w from eq. 5 and eq. 10 is maximal at t^* on that interval. The proof makes no assumption about surfaces before t_l, so it covers multiple intersections. A second-order analysis (curved surface) gives a peak offset \Delta_t = O(s^{-2}) for NeuS vs O(s^{-1}) for the naive solution, obtained by numerically solving the stationarity conditions app. C fig. 9.
Discretisation §3.1. With n samples t_1 < \dots < t_n:
\hat{C} = \sum_{i=1}^{n} T_i \alpha_i c_i, \qquad T_i = \prod_{j=1}^{i-1}(1-\alpha_j) eq. 11
\alpha_i = 1 - \exp\left(-\int_{t_i}^{t_{i+1}} \rho(t)\,dt\right) eq. 12
\alpha_i = \max\left(\frac{\Phi_s(f(\mathbf{p}(t_i))) - \Phi_s(f(\mathbf{p}(t_{i+1})))}{\Phi_s(f(\mathbf{p}(t_i)))}, 0\right) eq. 13
The closed form is exact (no quadrature) because the integrand of eq. 12 is the derivative of -\ln\Phi_s(f(\mathbf{p}(t))) on entering segments and 0 on exiting ones app. A. In the implementation, \alpha_i uses SDF at section points \mathbf{q}_i = \mathbf{o} + t_i\mathbf{v} and color c_i is evaluated at mid-points \mathbf{p}_i = \mathbf{o} + \frac{t_i+t_{i+1}}{2}\mathbf{v} app. D.1 fig. 10. (The released code differs; see Paper vs code.)
Training §3.2. Each iteration samples a batch of m pixels/rays from one image, with color C_k and optional mask M_k\in\{0,1\}; n points per ray:
\mathcal{L} = \mathcal{L}_{color} + \lambda\mathcal{L}_{reg} + \beta\mathcal{L}_{mask} eq. 14
\mathcal{L}_{color} = \frac{1}{m}\sum_k \mathcal{R}(\hat{C}_k, C_k), \quad \mathcal{R} = \text{L1} eq. 15
\mathcal{L}_{reg} = \frac{1}{nm}\sum_{k,i}\left(\|\nabla f(\hat{\mathbf{p}}_{k,i})\|_2 - 1\right)^2 eq. 16
\mathcal{L}_{mask} = \text{BCE}(M_k, \hat{O}_k), \quad \hat{O}_k = \sum_{i=1}^{n} T_{k,i}\alpha_{k,i} eq. 17
\mathcal{L}_{reg} is the Eikonal term §3.2. The trainable s is optimised jointly with the networks §3.2.
Hierarchical sampling §3.2 app. D.1. One network only (no NeRF-style coarse/fine pair). Sample 64 points uniformly, then 4 rounds of importance sampling, 16 new points each (128 total). Round i computes its sampling PDF from NeuS weights with a fixed s = 32 \times 2^i rather than the learned s; the final rendering uses the learned s. In the w/o-mask setting, 32 extra points outside the unit sphere feed a NeRF++ background model.
# NeuS forward pass for a batch of rays [§3.1] [§3.2] [app. D.1]
# Input: rays_origin (m, 3), rays_dir (m, 3), unit-length directions
# Output: rendered colors (m, 3), opacity sums (m,), eikonal loss
def render_rays(rays_origin, rays_dir):
# Ray segment inside the unit sphere [§4.1]
near, far = intersect_unit_sphere(rays_origin, rays_dir)
# Coarse samples: 64 uniform depths [app. D.1]
depths = uniform_samples(near, far, 64)
sdf = sdf_net(rays_origin + depths * rays_dir)
# 4 rounds of importance sampling with a fixed s [app. D.1]
for round_index in range(4):
# Paper: s = 32 * 2^i (released code uses 64 * 2^i) [app. D.1]
fixed_s = 32 * 2 ** round_index
weights = neus_weights(sdf, depths, fixed_s)
new_depths = sample_from_pdf(depths, weights, 16)
depths = sort(concat(depths, new_depths))
sdf = sdf_net(rays_origin + depths * rays_dir)
# Final render with the learned s [§3.2]
section_points = rays_origin + depths * rays_dir
mid_points = rays_origin + midpoints(depths) * rays_dir
sdf_section = sdf_net(section_points)
normals = grad_sdf(mid_points)
feature = sdf_net_feature(mid_points)
colors = color_net(mid_points, rays_dir, normals, feature)
# Discrete opacity from section-point SDFs [eq. 13] [app. D.1]
cdf = sigmoid(learned_s * sdf_section)
cdf_prev = cdf[:, :-1]
cdf_next = cdf[:, 1:]
alpha = clamp_min((cdf_prev - cdf_next) / cdf_prev, 0)
# Transmittance and weights as in NeRF [eq. 11]
transmittance = cumprod_exclusive(1 - alpha)
weights = transmittance * alpha
color = sum(weights * colors)
opacity = sum(weights)
# Eikonal regulariser on sampled points [eq. 16]
eikonal = mean((norm(normals) - 1) ** 2)
return color, opacity, eikonal
def training_loss(color, opacity, eikonal, target_rgb, mask):
# L1 color + lambda * Eikonal + beta * BCE mask [eq. 14] [eq. 15] [eq. 17]
color_loss = mean(abs(color - target_rgb))
mask_loss = binary_cross_entropy(opacity, mask)
return color_loss + lam * eikonal + beta * mask_lossMesh extraction: the paper does not describe it beyond taking the zero-level set eq. 1. The code runs marching cubes on -f at threshold 0 over the object bounding box renderer.py renderer.py.
Hyperparameters
| Name | Value | Where | Notes |
|---|---|---|---|
| SDF MLP | 8 hidden layers, width 256 | app. D.1 womask.conf | Softplus, β=100, all hidden layers |
| SDF skip connection | input concatenated at layer 4 | app. D.1 fields.py | code divides the concatenation by √2 |
| SDF output | 1 SDF + 256-d feature | app. D.1 womask.conf | d_out = 257 |
| Color MLP | 4 hidden layers, width 256 | app. D.1 womask.conf | inputs: position, view dir, normal ∇f, 256-d feature; ReLU, sigmoid output in code |
| Positional encoding, position | 6 frequencies | app. D.1 womask.conf | code applies it only in the SDF MLP |
| Positional encoding, view dir | 4 frequencies | app. D.1 womask.conf | |
| Weight normalization | yes | app. D.1 womask.conf | |
| Initialisation | geometric init (SAL), "similar to IDR" | §4.1 §4.3 fields.py | code: initial SDF ≈ sphere of radius 0.5 (bias = 0.5) |
| Optimizer | Adam | app. D.1 | betas, eps not stated (PyTorch defaults in code) |
| Learning rate | linear warm-up 0 → 5e-4 over 5k iters, cosine decay to 2.5e-5 | app. D.1 exp_runner.py | code: learning_rate_alpha = 0.05 |
| Iterations | 300k | §4.1 app. D.1 | |
| Batch size | 512 rays, all from one image | §4.1 §3.2 exp_runner.py | images visited in a random permutation |
| Coarse samples | 64 uniform | app. D.1 | |
| Importance sampling | 4 rounds × 16 points (128 total) | app. D.1 | |
| Fixed s for importance round i | 32 × 2^i | app. D.1 | code uses 64 × 2^i |
| Background samples (w/o mask) | 32 outside the sphere, NeRF++ | app. D.1 womask.conf | |
| Eikonal weight λ | not stated | eq. 14 | code: 0.1 womask.conf |
| Mask weight β | not stated | eq. 14 | code: 0.1 (w/ mask), 0.0 (w/o mask) wmask.conf |
| Initial s | not stated | code: s = exp(10 × 0.3) ≈ 20.1 fields.py womask.conf | |
| Region of interest | unit sphere | §4.1 | requires normalising cameras (IDR scale_mat) |
| Hardware / time | 1 × RTX 2080Ti; 14 h (w/ mask), 16 h (w/o mask) | §4.1 app. D.1 | |
| NeRF baseline mesh threshold | density 25 | §4.1 tab. 2 |
Results
Author-reported. Chamfer distance (lower is better) on 15 DTU scans (49 or 64 images, 1600×1200, masks from IDR), measured as in IDR and UNISURF §4.1 §4.2. IDR (w/ mask) and NeRF, UNISURF (w/o mask) scores are copied from the IDR and UNISURF papers; COLMAP uses trim=0 §4.2 tab. 1.
| Scan | IDR (mask) | NeRF (mask) | NeuS (mask) | COLMAP | NeRF | UNISURF | NeuS |
|---|---|---|---|---|---|---|---|
| 24 | 1.63 | 1.83 | 0.83 | 0.81 | 1.90 | 1.32 | 1.00 |
| 37 | 1.87 | 2.39 | 0.98 | 2.05 | 1.60 | 1.36 | 1.37 |
| 40 | 0.63 | 1.79 | 0.56 | 0.73 | 1.85 | 1.72 | 0.93 |
| 55 | 0.48 | 0.66 | 0.37 | 1.22 | 0.58 | 0.44 | 0.43 |
| 63 | 1.04 | 1.79 | 1.13 | 1.79 | 2.28 | 1.35 | 1.10 |
| 65 | 0.79 | 1.44 | 0.59 | 1.58 | 1.27 | 0.79 | 0.65 |
| 69 | 0.77 | 1.50 | 0.60 | 1.02 | 1.47 | 0.80 | 0.57 |
| 83 | 1.33 | 1.20 | 1.45 | 3.05 | 1.67 | 1.49 | 1.48 |
| 97 | 1.16 | 1.96 | 0.95 | 1.40 | 2.05 | 1.37 | 1.09 |
| 105 | 0.76 | 1.27 | 0.78 | 2.05 | 1.07 | 0.89 | 0.83 |
| 106 | 0.67 | 1.44 | 0.52 | 1.00 | 0.88 | 0.59 | 0.52 |
| 110 | 0.90 | 2.61 | 1.43 | 1.32 | 2.53 | 1.47 | 1.20 |
| 114 | 0.42 | 1.04 | 0.36 | 0.49 | 1.06 | 0.46 | 0.35 |
| 118 | 0.51 | 1.13 | 0.45 | 0.78 | 1.15 | 0.59 | 0.49 |
| 122 | 0.53 | 0.99 | 0.45 | 1.17 | 0.96 | 0.62 | 0.54 |
| mean | 0.90 | 1.54 | 0.77 | 1.36 | 1.49 | 1.02 | 0.84 |
Bold marks the best per setting as in tab. 1. Columns 2–4: w/ mask; columns 5–8: w/o mask.
Ablation on one DTU scene (shown as a skull; scan id not stated), Chamfer distance and MAE between predicted and ground-truth SDF on points sampled uniformly in the bounding sphere §4.3 fig. 6:
| Variant | Chamfer | SDF MAE |
|---|---|---|
| (a) Naive solution eq. 3 | 1.49 | 1.75 |
| (b) Straightforward / direct eq. 4 | 4.45 | 44.34 |
| (c) w/o Eikonal | 0.64 | 88.94 |
| (d) w/o geometric init | 0.62 | 6.19 |
| (e) Full model | 0.59 | 0.93 |
Without Eikonal or geometric init, Chamfer stays close to the full model but the field is no longer a valid SDF; random init also produces axis-aligned artifacts attributed to positional-encoding spectral bias §4.3 app. E fig. 13.
NeRF baseline threshold (Chamfer, DTU) tab. 2:
| Scan | 0 | 25 | 50 | 100 | 500 |
|---|---|---|---|---|---|
| 40 | 2.36 | 1.79 | 1.86 | 2.07 | 4.26 |
| 83 | 1.65 | 1.20 | 1.37 | 2.24 | 29.10 |
| 114 | 1.62 | 1.04 | 1.10 | 1.43 | 8.66 |
Rendering speed app. E tab. 3: one 1600×1200 image takes about 320 s on a 2080Ti with default volume rendering, about 60 s when sphere tracing first locates the surface and only nearby points are sampled. Mean PSNR / SSIM over the 15 scans: 31.97 / 0.840 default vs 30.85 / 0.820 with sphere tracing.
Novel view synthesis (10% of DTU images held out, w/o mask) app. E tab. 4: mean PSNR 28.55 vs NeRF 29.31; mean SSIM 0.820 vs 0.826, i.e. comparable to NeRF, slightly lower.
Per-scan values from tab. 3 and tab. 4:
| Scan | 24 | 37 | 40 | 55 | 63 | 65 | 69 | 83 | 97 | 105 | 106 | 110 | 114 | 118 | 122 | Mean |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| PSNR NeuS (tab. 3) | 28.20 | 27.10 | 28.13 | 28.80 | 32.05 | 33.75 | 30.96 | 34.47 | 29.57 | 32.98 | 35.07 | 32.74 | 31.69 | 36.97 | 37.07 | 31.97 |
| PSNR NeuS-ST (tab. 3) | 27.07 | 26.58 | 27.70 | 28.37 | 31.32 | 31.39 | 30.20 | 31.79 | 28.58 | 30.87 | 33.61 | 32.40 | 31.33 | 35.55 | 35.96 | 30.85 |
| SSIM NeuS (tab. 3) | 0.764 | 0.813 | 0.737 | 0.768 | 0.917 | 0.835 | 0.845 | 0.850 | 0.837 | 0.837 | 0.875 | 0.876 | 0.861 | 0.891 | 0.892 | 0.840 |
| SSIM NeuS-ST (tab. 3) | 0.757 | 0.811 | 0.736 | 0.759 | 0.915 | 0.788 | 0.813 | 0.812 | 0.794 | 0.811 | 0.852 | 0.862 | 0.847 | 0.867 | 0.873 | 0.820 |
| PSNR NeRF, held-out (tab. 4) | 24.83 | 25.35 | 26.87 | 27.64 | 30.24 | 29.65 | 28.03 | 28.94 | 26.76 | 29.61 | 32.85 | 31.00 | 29.94 | 34.28 | 33.69 | 29.31 |
| PSNR NeuS, held-out (tab. 4) | 23.98 | 22.79 | 25.21 | 26.03 | 28.32 | 29.80 | 27.45 | 28.89 | 26.03 | 28.93 | 32.47 | 30.78 | 29.37 | 34.23 | 33.95 | 28.55 |
| SSIM NeRF, held-out (tab. 4) | 0.753 | 0.794 | 0.780 | 0.761 | 0.915 | 0.805 | 0.803 | 0.822 | 0.804 | 0.815 | 0.870 | 0.857 | 0.848 | 0.880 | 0.879 | 0.826 |
| SSIM NeuS, held-out (tab. 4) | 0.732 | 0.778 | 0.722 | 0.739 | 0.915 | 0.809 | 0.818 | 0.831 | 0.812 | 0.815 | 0.866 | 0.863 | 0.847 | 0.878 | 0.878 | 0.820 |
Qualitative only: 7 BlendedMVS scenes (31–143 images, 768×576) and two self-captured thin objects with 32 images, where NeuS recovers thin parts and abrupt depth changes that IDR and NeRF miss §4.1 §4.2 §4.3 fig. 4 fig. 5 fig. 8. A metal rabbit with textureless regions is shown as a failure case app. E fig. 16.
Paper vs code
Code: official repository by the first author (MIT), checked at
6e0a2309. Configs: wmask.conf (w/ mask), womask.conf (w/o mask,
NeRF++ background), thin_structure.conf, womask_dtu_large_roi.conf.
- Importance-sampling s. Paper: fixed s = 32\times2^i in round i
app. D.1. Code:
64 * 2**irenderer.py. - Where SDF is evaluated for α. Paper: SDF at section points
\mathbf{q}_i, \mathbf{q}_{i+1} app. D.1. Code: one SDF and gradient
evaluation at the mid-point, then section-point SDFs are extrapolated
linearly,
sdf ± cos * dist / 2, withcosderived from \nabla f\cdot\mathbf{v} renderer.py. The importance-sampling step uses finite differences of neighbouring SDF samples instead, takes the minimum of the current and previous slope, and clips it to [-10^3, 0] renderer.py. - Cosine annealing (not in the paper). In w/o-mask training the slope used
for α is blended from -\text{relu}(-\cos/2 + 1/2) to -\text{relu}(-\cos)
over the first 50k iterations (
anneal_end = 50000), keeping it nonzero early on renderer.py exp_runner.py womask.conf. The w/ mask config setsanneal_end = 0, so only the second form is used. - α clipping and epsilons. Code computes
((prev_cdf - next_cdf + 1e-5) / (prev_cdf + 1e-5)).clip(0, 1)rather than the baremax(·, 0)of eq. 13 renderer.py. - Parameterisation of s. Paper: "trainable parameter" §3.1. Code: s = \exp(10\cdot v), v initialised to 0.3, clipped to [10^{-6}, 10^6] fields.py renderer.py.
- Eikonal points. Paper averages over all nm samples eq. 16. Code averages only over samples with \|\mathbf{p}\| < 1.2 renderer.py.
- Color loss with masks. Paper averages L1 over all m rays eq. 15.
With
mask_weight > 0the code multiplies the color error by the mask, so background pixels do not contribute exp_runner.py. The BCE input is clipped to [10^{-3}, 1-10^{-3}] exp_runner.py. - Positional encoding of position goes only into the SDF MLP; the color MLP receives raw 3-D position fields.py womask.conf. The paper's wording leaves this open app. D.1.
- Background inside the sphere. With NeRF++ enabled, samples outside the unit sphere take the background model's α and color instead of NeuS's renderer.py.
- Last segment length. The final sample's segment length is the constant
2 / n_samples, not a distance to the next sample renderer.py renderer.py.
Pitfalls
- The sign convention matters. f > 0 outside and < 0 inside app. B; α is nonzero only where the SDF decreases along the ray. Flipping the sign makes the object invisible. The geometric init (positive mean on the last layer, bias −0.5) starts f as roughly a sphere of radius 0.5 with this convention fields.py.
- Unbiasedness is first-order only. It holds for locally planar surfaces; for curved surfaces the offset shrinks as O(s^{-2}), so it relies on s growing during training app. C. A single global s is used for all locations, which the authors list as a limitation §5.
- Don't evaluate α at mid-points. Eq. 13 needs SDF at both ends of each segment. Using one SDF per sample with the naive 1 - \exp(-\sigma\delta) formula reintroduces the bias analysed in app. B.
- Division by \Phi_s(f_i) is near zero deep inside the object; add an epsilon as the code does renderer.py.
- Second-order gradients. The color MLP takes \nabla f and the Eikonal
loss penalises it, so the SDF must be differentiated with
create_graph=Trueat every sample fields.py. This, plus 128 samples per ray, is why training takes 14–16 h for 300k steps on one GPU §4.1. - Cameras must be normalised so the object fits the unit sphere §4.1; near/far come from intersecting each ray with that sphere dataset.py.
- Unstated loss weights. λ and β are not given in the paper; use the code values (0.1, and 0.1 or 0) womask.conf.
- Rendering is slow. About 320 s per 1600×1200 image at default sampling app. E. Sphere tracing speeds it up to about 60 s at a cost of about 1.1 dB mean PSNR tab. 3.
- Textureless regions remain ambiguous and can fail §5 fig. 16.
- Baselines are mixed-source. Several numbers in tab. 1 are copied from the IDR and UNISURF papers, not rerun §4.2.
Lineage
- Builds on: NeRF (arXiv:2003.08934) for volume rendering, discretisation and hierarchical sampling; IDR (Yariv et al., 2020) for the SDF/color network architecture, weight normalisation, L1 color loss and data format; IGR (arXiv:2002.10099) for the Eikonal term; SAL (Atzmon and Lipman, 2020) for geometric initialisation; NeRF++ (arXiv:2010.07492) for the background model.
- Compared against: IDR, NeRF, COLMAP, and the concurrent UNISURF (arXiv:2104.10078), which instead uses an occupancy field.
- Superseded by: no later work with a card in this wiki yet.
Source and license
Adapted from “NeuS: Learning Neural Implicit Surfaces by Volume Rendering for Multi-view Reconstruction” by Peng Wang, Lingjie Liu, Yuan Liu, Christian Theobalt, Taku Komura, Wenping Wang (arXiv:2106.10689), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/Totoro97/NeuS at commit 6e0a2309d3f8 (MIT). Any quoted code is shown under that license.