What this is
An NNTC material is two small latent textures and a handful of weights. This page samples each latent once per
pixel with the GPU's own sampler - the same filtering, mips and anisotropy a game would use - and runs
the decoder in the fragment shader, so what you see is what a real renderer would get rather than an offline
decode. The shader is a transliteration of viewer/bin/nntc_view.hlsl, the file the Direct3D 11 and
Direct3D 12 viewers both compile, and this page's keys are those viewers' keys.
Where the GPU has no texture-compression-bc, the page unpacks BC4 and BC5 on the CPU in plain
JavaScript and binds uncompressed planes instead; the shader does not change. The Decode block in the
panel says which path drew level 0.
The workflow
- Open a bundled material. The pulldown at the top of the panel lists the materials that ship with this
page; one is already open. Picking another resets the view, because a new material is a new thing to
look at.
- Or give the page a folder. Drop a folder on the page, or press "Select working
directory..." where your browser has that button (Chrome and Edge do, and the panel says so when yours does
not). The page treats the two the same way. Every material in the folder joins the same pulldown. You can
open one of them by picking or dropping its
.json on its own, because the page reads the
.dds files it names out of that folder. Reload re-reads those files from disk and lists
the folder again, so a material you encode while the page is open shows up too. The page only reads your
files. It never writes. A folder you pick with the button is remembered for your next visit, though the
browser may ask you once more before the page may read it. A folder you drop in a browser that has no such
button cannot be stored at all, so you drop it again next time. The panel says which of the two you
have.
- Or bring your own files. Select a material's
PREFIX_nntc.json and its .dds files
together (open the folder, Ctrl+A, Open), or drag the group - or the folder itself - onto the
page. No browser hands a page a
file's siblings, so the descriptor alone cannot load on its own - but the page keeps every file you offer
it, so picking the .json and then picking its .dds files works, in as many goes
as it takes and from as many folders. Until the set is complete the top of the panel says what is still
wanted, by name, the rows under "Your files" list what is in hand, and the material on screen does not
move.
- Look. Drag the view to yaw and pitch, the wheel zooms, and the keys below do the rest. The
Loaded files block is the asset's own facts: both latents side by side, the four bitrates and the
decoder's weight count. Channel(s) to view, in the Show group, draws one channel at a time - R, G, B
or A - in all three views, and draws it as grey rather than in its own colour, so what you read is a
magnitude and not a hue. It is the only way to see a four-channel latent's fourth channel. In Decode, alpha
is 1.0 by construction and A therefore draws white.
- Re-encode and press
R. Reload re-reads the same files and keeps the camera and
every mode, which is the whole point of it: the same angle and the same settings, the new encode. It is
the opposite of Reset.
Reset, and what it covers
Space, the Reset view button at the foot of the Show group and the button below all do the same
thing: the camera, the filter, anisotropy, mips, the level-1 LOD shift, the geometry, the raw views, the
renormalise flag, the channels shown and the shown texture all go back to where a fresh load leaves them. It
is there for when a few keystrokes have left the picture looking wrong and you cannot remember which one did
it.
Four things it deliberately leaves alone. Three are about the device or the display rather than
the view: the CPU unpack checkbox (which path level 0 is bound through), the canvas colour space, and "Stop on
a WebGPU error" beside the message log. The fourth is the drawn frame-rate and camera strip, which is a thing
over the picture and not part of it.
Keys
Click the view first - the keys go to the canvas, so typing in the panel never moves the camera.
Every key here is also a control in the left panel, and the two always agree.
| Camera (hold) |
arrows | move the quad or cube left / right / up / down |
W / S | zoom in / out |
A / D | yaw |
Q / E | pitch |
Shift | with any of the above: one third the speed |
Space | reset: the camera and every mode below, back to a fresh load |
| Display |
C | quad, cube, sphere |
G | one object per output texture, in a row; N then slides which
texture is on which |
O | turn the view by itself, 30 degrees a second |
N | next output texture of a material (cycles) |
1 | show level 0's texture as stored (toggle) |
2 | show level 1's texture as stored (toggle) |
V | renormalise the shown texture as a tangent-space normal (toggle) |
| Sampling |
P / B / T | point / bilinear / trilinear filter |
X | anisotropic filtering on / off (trilinear filtering only) |
M | mips on / off (off: the sampler is held at mip 0, nothing is reloaded) |
L | level 1's LOD shift on / off (its gradients x 2^lod_bias_level1) |
| Other |
R | reload the asset, keeping the camera and every mode |
Shift+R | reload the shader (nntc_view.wgsl) |
F1 | this page |
Esc | close this page |
| drag, wheel | yaw and pitch; zoom |
Three differences from the native viewers, and no others. R reloads the asset here
and Shift+R the shader, because the asset is the thing that changes ten times an hour
in an encode-look-encode loop; Ctrl+R stays the browser's. Esc closes
this page rather than quitting, since a page cannot close itself. And the native 4 (level 0 from a
BC pack made at load) and 5-8 (the shader's spare debug constants) are not bound: the
pack applies only to an uncompressed level 0 and is not implemented here, and nothing in the shipped shader
reads the spare constants.
URL fields
They mirror the native viewers' flag names: ?asset=, ?tex=, ?cube,
?z= ?yaw= ?pitch= ?x= ?y=,
?nomips ?nobias ?noaniso ?renorm ?raw0
?raw1, and beside them ?unpack=0|1, ?overlay=0,
?geom=quad|cube|sphere, ?row=1, ?orbit=1,
?filter=point|bilinear|trilinear and ?chan=rgb|r|g|b|a. A URL that sets a mode
is honoured on the first load and is not undone by it.
Part of the NNTC tree; see webgpu/README.md beside this page, and
docs/FORMAT.md for the format itself.