Why this update exists

On the official VaM Discord, @AlphaCentauri reported a noticeable framerate drop from a baked EnvRay shaft in an apartment scene with no Person atoms in it at all, and asked whether he had set it up wrong.

He had not. Our documentation was wrong.

v1.0 described the baked engine as having "zero running cost". What is actually free is the light slot and the re-bake. The shaft itself is a volumetric raymarch and it is re-integrated every frame, from wherever your camera is, whether or not anyone is standing in it. Baking freezes the bake, not the beam.

That report is the reason for everything below. The wording is corrected, and then the thing the wording was wrong about got measured properly and rebuilt around what the measurement said.



⚡ What it costs now

Framingv1.0v1.1Frame timeThe shaft's own cost
Shaft crossing the frame (typical)13.51 ms6.51 ms-51.8%10.28 → 3.28 ms (3.1x)
Standing inside the shaft (worst case)23.13 ms13.71 ms-40.7%19.90 → 10.48 ms (1.9x)
Shaft entirely behind a wallnearly full price0.00 msfree19.6 → 0.00 ms

The last row is the one that matters most in a real interior. A shaft in the next room used to cost almost as much as one filling your screen, because the volume was still being integrated behind the wall. It now exits before doing any work at all.

What that means for your framerate

The shaft got cheaper by roughly three times. Your framerate gain depends on how much of your frame the shaft was, which is something only you can see:

If you were at 60 fps and the shaft was...expect roughly
30% of your frame time~75 fps
50% of your frame time~91 fps
70% of your frame time~114 fps

⚠ Reference only, and here is what that qualifier actually covers. Every number on this page was measured on a single RTX 5090 at 7680x4320, in a purpose-built empty test room where the shaft is nearly the whole cost. Your GPU, your resolution and your composition all move these figures, and the largest of the three optimisations trades arithmetic for texture lookups, which is a bargain whose value depends on GPU architecture. We have no data from any card other than that one. If your result differs, that is information we want.



🔧 What actually changed

1. The noise is now a lookup table (most of the win)
Haze and Dust turned out to be 88.9% of the shaft's entire cost. Both were procedural value noise, evaluated three times per raymarch step, each evaluation eight hashes and seven blends. They are now one texture fetch each from a small tiled 3D table built at load.

v1.0 proceduralv1.1 lookup
Haze11.40 ms2.61 ms4.4x
Dust6.16 ms2.22 ms2.8x

Side effect worth having: the old code fed world position plus elapsed time into the noise, and after a few hours at dust frequencies that coordinate ran out of floating point precision and the grain slowly degraded. The table tiles, so the coordinate can be wrapped exactly. That failure mode is now structurally gone.

If you prefer the original look, Noise quality on the EnvRay panel has a Legacy hash option. It costs about twice as much and, in a still frame, the two differ by 1.3% in average brightness.

2. The march no longer walks the empty parts of the ray
The shader stepped from the camera to the far side of the beam volume in a fixed number of steps, so a large share of them landed in empty air outside the shaft, doing nothing. It now solves where the ray enters and leaves the volume and visits only the steps in between. Same sample positions, same image : the difference is that the empty ones are skipped.

Worth 8-14% depending on framing, and worth nothing at all when you are standing inside the shaft, because then there is no empty part to skip.

3. A hidden shaft exits immediately
Falls out of the same solve. If the ray meets solid geometry before it reaches the volume, the pixel returns black without entering the loop.

4. StageLightGroup : flare offset fixed
With several fixtures in one group and Flare enabled, the flare billboards collapsed onto a single position. Unity's two merging paths both require the merged renderers to share a material, so each fixture now carries its own flare material, which rules the merge out by construction rather than by hoping.



🚫 What we tried and did not ship

  • Drawing only the back faces of the beam volume. On paper this halves the work. Measured: no gain at all : the front-face pixels were already exiting almost immediately. It would have carried a small risk to the shaft's silhouette for nothing, so it is not in the build.
  • A smaller lookup table for the haze. Faster when Haze runs alone, but its repeat period in metres would then be short enough to see inside a long shaft. Rejected on looks, not on speed.

Both are listed because "we measured this and decided against it" is a more useful thing to know than a list of wins.



🎚️ If it is still too expensive

In the order that actually helps:

  1. Haze off, then Dust off. Even after this update they are most of the shaft's cost. Together they are worth more than everything else on this list combined.
  2. Raymarch steps 48 → 24. Cost is exactly linear in steps, so this is exactly half.
  3. Make the shaft smaller in frame. Beam radius, throw depth, or simply not composing it across the whole view. Screen coverage is a straight multiplier.
  4. Fog softening off. Smallest of the four.

Changing STATIC / SMART / LIVE will not help. Measured, the three differ by 0.05 ms, which is 0.2% of the shaft. They decide how often the bake re-runs, and the bake was never the expensive part. This is the same misunderstanding the v1.0 wording encouraged, so it is worth stating twice.



🔬 How it was measured

  • A purpose-built test room, generated by a plugin rather than hand-assembled, so the fixture is identical every run and on anyone's machine. The camera is snapped to numbered positions by code, because "same seat as last time" is not a measurement.
  • 8 passes of 10 seconds per configuration, every setting written by the harness rather than by hand. Scatter per row 0.01 to 0.08 ms; drift across an hour under 0.2%.
  • 7680x4320, forced with DSR. An earlier attempt at 4K produced a completely flat table : the shaft was cheaper than the CPU floor, so every row read the same and Dust and Fog looked free when they were not.
  • Every configuration was checked both ways. If a change should make one row move and another row stay still, both halves were required before it was believed. Two conclusions were thrown away during this work because the row that was supposed to stay still had never been able to move in the first place.

Nothing here was estimated. Where a number could not be measured honestly it is not on this page.



Thanks to @AlphaCentauri, who reported the issue after wondering if he'd set something up wrong.
He had not, and the question was worth more than a bug report.
Back
Top Bottom