phong_shade — 3D render op

• Data kinds: normalmap → image2d

• Call: import fullseye as fs; fs.ledger.phong_shade(normals, view=(0.0, 0.0, 1.0), light=(0.0, 0.0, 1.0), ambient: 'float' = 0.1, diffuse: 'float' = 0.8, specular: 'float' = 0.5, shininess: 'float' = 32.0, clip: 'bool' = True) -> 'np.ndarray' (to call the implementation directly, import render_shade; render_shade.phong_shade(normals, view=(0.0, 0.0, 1.0), light=(0.0, 0.0, 1.0), ambient: 'float' = 0.1, diffuse: 'float' = 0.8, specular: 'float' = 0.5, shininess: 'float' = 32.0, clip: 'bool' = True) -> 'np.ndarray'; from the registry, ops3d.get("phong_shade"))

Usage

Shade a normal map with the Phong reflection model (ambient + diffuse + specular). → (H, W).

At each foreground pixel, for the unit normal N, computes the ideal reflection direction obtained by mirroring the light direction L about N, R = 2(N·L)N − L, and builds the specular term from its agreement with the view direction V::

I = ambient + diffuse * max(N·L, 0) + specular * max(R·V, 0)^shininess

The specular term applies only to faces hit by light (N·L > 0). It is render_lambertian with a specular lobe added, and the highlight peak sits not at the diffuse maximum (N=L) but in the half-angle direction N = normalize(L+V) (where R=V and R·V=1).

The background (normals of length 0 = empty pixels of render3d.render_mesh) is 0. clip=True clips the output to [0, 1] (for display), clip=False returns the raw summed intensity (use this when you want the highlight position exactly with argmax — saturation does not make the peak a tie).

Args:

• normals : float (H, W, 3) normal map (view space, facing the viewer; length 0 = background).

• view : direction surface→viewer (default (0,0,1) = the viewpoint of render3d).

• light : direction surface→light source.

• ambient/diffuse/specular : coefficient of each term (finite, non-negative).

• shininess: sharpness of the specular lobe (> 0; larger is sharper / smaller is broader).

• clip : whether to clip to [0,1].

fail-closed: invalid shapes, non-finite values, zero-length directions, non-positive shininess and negative coefficients raise ValueError.

Background guides (the physics and conventions behind this op)

• blender_interop — Using Blender alongside — build the shape, measure it with fullseye (pitfalls: axes, units, ground truth)

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

Runnable examples (verified samples that actually call this op)

• render_beauty — py -3.11 examples_3d/render_beauty.py

• render_shade — py -3.11 examples_3d/render_shade.py

Ops the type connects to (they accept image2d as input)

fuse_to_voxel · fit_poly_surface · eval_poly_surface · surface_form_error · background_flatten · polar_unwrap · fit_zernike · matcap_shade

Same category (render)

project_points · render_point_depth · render_volume_projection · render_shaded · ambient_occlusion · cast_shadow · matcap_shade · supersample_mesh


*Provenance: render_shade.py — 3D operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.