An environment gives a scene a distant sky and diffuse image-based lighting. It is optional: scenes without one keep their solid background and existing lighting.

sky := nt3d.GradientEnvironment(
    color.RGBA{72, 130, 220, 255},   // zenith
    color.RGBA{218, 231, 245, 255},  // horizon
    color.RGBA{115, 100, 77, 255},   // ground
)
scene.SetEnvironment(sky)

The widget picks it up through the scene’s frames. Call view.Refresh() after changing it when the application is not already scheduling frames. Remove it with scene.SetEnvironment(nil). Environment objects are immutable and can be shared between scenes; frames retain the environment they were made with.

Image skies#

Pass a decoded PNG, JPEG, or other Go image to nt3d.NewEnvironment(img). It snapshots the pixels, so callers may reuse or modify the source afterwards. The image is an equirectangular panorama, normally twice as wide as it is tall. The top is +Z, the center faces +X, and increasing U turns toward +Y. Horizontal sampling wraps at the seam, while the poles clamp. Alpha is ignored. The same 8192-pixel side limit as NewTexture applies. Input colors are sRGB, not HDR.

environment, err := nt3d.NewEnvironment(img)
if err != nil {
    return err
}
scene.SetEnvironment(environment)

The sky follows camera orientation but stays fixed under translation and zoom. Perspective views use the camera’s authored field of view and roll. Orthographic views use a fixed 45-degree field of view, so models have a useful sky instead of a single constant direction. For hand-built frames, set Frame.Forward to the camera’s viewing direction. Scene.Frame does this automatically.

Light from the environment#

Both renderers add the same precomputed diffuse light to Frame.Lighting. To use the environment alone, clear the usual lights:

frame, err := scene.Frame(camera, 960, 640)
if err != nil {
    return err
}
frame.Lighting = nt3d.Lighting{}
frame.EnvironmentStrength = 0.6

EnvironmentStrength scales only the lighting; zero means 1, as with the other lighting intensity fields. EnvironmentBackgroundOnly = true keeps the sky while disabling its light. Frame.Environment can also override the scene’s choice for one frame. In a widget, set these fields in a frame decorator; use SetLighting(nt3d.Lighting{}) to remove its default lights.

Environment lighting uses nine degree-two RGB spherical-harmonic coefficients, with cosine convolution, for broad directional diffuse illumination. It is a cheap, low-frequency approximation: it does not reproduce small bright light sources, cast environment shadows, or capture sharp reflections. Existing directional and point lights still provide highlights and mapped shadows. Unlit materials, points, lines, and overlays remain unlit. Prefer the default linear-light frames for physically meaningful color blending.

Use nt3d.NewSHEnvironment(radiance) to build an environment directly from SH radiance coefficients, including a generated sky panorama. Environment.DiffuseSH() returns its nine convolved diffuse coefficients. The shared nt3dsh package provides projection, evaluation, and rotation; see spherical-harmonic lighting for an example and coefficient conventions. Gaussian splats use SH for their own view-dependent color and remain unlit by the environment.

The GPU uploads the panorama when it changes and draws one fullscreen triangle. Software samples the same sky and uses the same diffuse coefficients. The sky writes no depth, so transparency composites over it and it never changes bounds, culling, picking, or shadow-map fitting. Wireframe uses sky-colored terminal cell backgrounds and contrasting default strokes. Fog keeps its existing behavior: set an explicit Fog.Color near the horizon color if you want it to meet the sky.

Try task lights or the Lights web gallery: it starts with sky and key, then l cycles through the environment and light presets, including SH studio. Press h to select the SH studio environment and rotate it in 30-degree steps. The image preset is an original generated warm/cool panorama, with no downloaded assets. 1, 2, and 3 compare GPU, software, and wireframe; drag to orbit.

Reflections#

Material.Reflectivity samples the panorama along the reflected view direction. It is the normal-incidence reflectance F0, in [0,1]: zero explicitly disables reflection, .02 suits water, and 1 is a mirror. Schlick Fresnel increases reflection toward grazing angles, blending it with the lit base color. Direct specular highlights are added afterward. Normal maps affect both contributions.

GPU and software share panorama orientation, seam wrapping, pole clamping, and color-space behavior. EnvironmentStrength scales reflected light too; EnvironmentBackgroundOnly disables it along with diffuse environment light. Without an environment, reflectivity has no effect.

This is a sharp distant-sky reflection. It does not reflect nearby geometry or prefilter rough surfaces. Shininess controls direct highlights only.