Renderer · interface
RenderQuality
Explained in Hello world.
interface RenderQualityimport type { RenderQuality } from '@driftengine/core';Properties
| Name | Type | Description |
|---|---|---|
outputTransformreadonly | OutputTransform | The transfer curve applied at the end of every shaded pass.MoreConstruction-time, like every other quality control here, and The symptom, written down because nobody searches for a transfer curve they do not know
exists. With The default stays |
outputExposurereadonly | number | How far the scene is scaled into the tone curve before it is applied. 1 is neutral.MoreA tone curve is only meaningful relative to an exposure, and the reference ACES
transform is graded for a dark cinema. Renderers that ship it therefore scale their
input to suit the room the image will actually be seen in, and the factor each one
chooses is a look rather than a standard — three.js uses 1/0.6 and says so. This
engine holds no opinion and takes the number from whoever is authoring the world, so
that matching another renderer, or simply preferring a different grade, is a setting
rather than a patch. Scales into |
pointLightFalloffreadonly | 'smooth' | 'inverseSquare' | How a point light fades with distance.More
|
maxDevicePixelRatioreadonly | number | Drawing-buffer pixel density cap. |
maxDrawingBufferPixelsreadonly | number | Drawing-buffer area cap, in pixels. Zero is uncapped.MoreThe companion to |
materialChangesPerFramereadonly | number | How many times a frame may change material, on a backend that keeps a slot for each.MoreWebGPU spends a slot of a uniform ring per material change and skips every draw past it, because a buffer rewritten between two draws would hand both of them its last contents. So this is a ceiling on what a frame can show, not a performance hint: a scene that needs more loses whatever it asks for last — measured on a bought city of 1,139 materials, whose street spends some nine hundred and whose river's planar mirror spends the rest twice over. What it costs is memory, paid at construction: a slot is the fragment block twice over, staging and buffer, so 7 to 13 KB depending on the permutation built — 1,024 is 7 to 13 MB and 2,048 twice that. Hence a default that suits most scenes and an option for the one that measures past it, rather than a number every consumer pays for. WebGL2 writes each uniform into the command stream and keeps no ring, so there it is read and does nothing, and its budget line reports no ceiling. What would make it wrong is a scene that switches material every draw, which no ring answers: many copies of one mesh want one instanced draw. |
drawsPerFramereadonly | number | How many draws a frame may make, on a backend that keeps a uniform slot for each — the main
pass and the shadow passes each hold this many.MoreThe same shape as |
directionalShadowsreadonly | boolean | |
pointShadowsreadonly | boolean | |
maxLightsreadonly | number | The most point lights the lit shader will be built to shade at once. A ceiling, not a promise.MoreWhat it is for is fitting a device, not tuning a picture. Ten of the lit shader's uniform arrays are sized by this with point shadows off and twenty with them on, and GLSL ES spends a whole row of the fragment uniform grid per array element whatever its base type — so the full budget declares 440 rows where an Adreno 740 offers 256 and WebGL2 guarantees 224. A shader that does not link is not a slower frame, it is no frame at all. The renderer already lowers this on its own when the part is short, counting the shader it is about to compile against what the device reports and saying so in the console. So a consumer never has to set it to boot — this is here for the one who wants the arrays smaller than the device would force, to spend the rows on something else or to hold a scene to a budget they have measured. Lowering it costs lights. WebGL2 only. The generated WGSL is built and committed at the full budget, and WebGPU has no per-stage uniform-vector ceiling to be short of. A consumer that lowers this and lands on WebGPU gets all sixteen, which is both the better picture and the one that device can afford. |
maxAreaLightsreadonly | number | The most rectangular emitters the lit shader will be built to shade at once. See maxLights. |
directionalShadowMapSizereadonly | number | |
pointShadowFaceSizereadonly | number | Side length of each of the six point-shadow cubemap faces. |
shadowFilterTapsreadonly | ShadowFilterTaps | Shared PCF budget for directional and point-light shadows.MoreA closed set written as one, so a value outside it is a red squiggle rather than a blank
page. Reported from outside: |
lightVolumeSamplesreadonly | number | How many samples a volume of light takes along each view ray. See drawLightVolume.MoreThe pass integrates the air rather than drawing sheets through it, so this is the dial between banding and cost, and it is the only quality control the volume has. Thirty-two is clean at the size a shaft usually occupies because the march is dithered per pixel, which turns what would be concentric shells into grain the effect wants anyway. Below about eight that grain becomes the picture; above thirty-two nothing here could see a change. It costs nothing when no volume is drawn, which is most frames in most worlds. |
directionalShadowDepthLayersreadonly | DirectionalShadowDepthLayers | Independently composited static sun depths; two preserves common overlaps. |
glassShadowsreadonly | GlassShadows | Whether light through glass takes the glass's colour, and at what size.More
Nothing is allocated until a glass caster first reaches a shadow, so a scene with no glass pays
no memory and no pass whatever this says. Nor any shader, since 4.8.2: the lit shader is
built without the glass lookup and rebuilt with it when the first glass caster reaches a shadow.
A lookup that finds no glass still cost the lit pass its registers — measured on the courtyard at
night, 0.27 ms at 720p on WebGPU, 0.42 ms on WebGL2 and 1.2 ms at 4K — and from 4.5.0 to 4.8.1
every scene paid it, glass or none: about 30% of the lit shader's instructions, which on a phone
was the lit pass. |
pipelineCompilereadonly | PipelineCompile | What a lit draw does when the pipeline it needs has not compiled: one first needed inside a
frame, by a material's own state — two-sided, cut out, a surface model — or a draw state a
mesh's creation did not warm.More
The way to have neither is to draw what a scene will use once, behind a loading screen, and
await Measured on a desktop GPU ( |
skinScatteringreadonly | SkinScattering | How light that scatters beneath skin is drawn, for a material whose model is skinModel.More
Construction-time: three half-float targets the size of the frame, and two multisampled ones
under multisampling, made at the first skin draw. It needs the composite ( |
directionalShadowMaxDistancereadonly | number | Horizontal world-space reach over which a directional shadow dissolves. |
directionalShadowMaxSlopereadonly | number | Maximum horizontal projection per vertical metre before low-angle shadows vanish. |
pointShadowRebakeDistancereadonly | number | Metres a point light may drift from where its shadow was baked before re-baking.MoreZero is the old behaviour and it was the wrong default. See
It was 0.4 until 4.1.5, chosen to clear a flame's whole wander with room to spare — so a flame never re-baked at all, and its shadow of the static world stood still while the live map under the same light, which is re-rendered every frame because it holds the movers, swung with the flame. One light, two shadows, visibly disagreeing about where it was. Reported as the runner's shadow following the fire and the brazier's not. A millimetre instead, which is as near to "whenever it moved" as makes sense, so a shadow travels with its light rather than freezing between re-bakes. A centimetre was tried first and left a visible vibration: a light wandering on a curve moves less than that in a frame near the turning points of its travel, so the shadow ran smoothly through the fast part and stepped through the slow part. What the tolerance is still for is not re-baking on arithmetic noise. A light that does not move never goes stale at any positive value, so the whole cost falls on the lights that need it, and it is bounded rather than proportional: one such light takes a whole cube in a frame and the rest dribble under the ordinary face budget. What makes that affordable is not this number. The 174-of-296 draw calls this paragraph
used to cite was measured before What would make it wrong is a caster set heavy enough that six passes over it does not
fit the frame, where a stepped shadow is the better trade and this should go back up.
|
pointShadowFacesPerFramereadonly | number | Cubemap faces of point-shadow bake the renderer may spend in one frame.MoreA face is a full pass over the static casters, so six of them is one light. Baking every stale light in the frame it goes stale is fine in the steady state and ruinous on the frame that stops being true: walking into a courtyard makes eight lights stale at once, and 48 passes measured as two consecutive frames of 89 ms and 99 ms against an 8.3 ms budget. That is the hitch a second after a page load. Two faces is about 6 ms of the same work, and the backlog drains over the next handful of frames while shadows resolve progressively. Raising it trades that smoothness for lamps that light sooner. |
liveShadowFacesPerFramereadonly | number | Cubemap faces of live point-shadow bake the renderer may spend in one frame.MoreThe companion to It defaults to a whole cubemap because that is what the renderer did before this existed,
and that default is wrong for a phone. The live loop had no budget at all: it asked for
all six faces per live map per frame, outside the static budget. Measured as
Lowering it spreads a mover's shadow across two or three frames instead of finishing it in one. The shadow then trails a fast character slightly, which is a real cost and a smaller one than the frame it was taking. Six keeps the old behaviour exactly. |
waterreadonly | boolean | |
waterResolutionreadonly | number | Cells per side; the vertex count is this value plus one, squared. |
waterReflectionsreadonly | boolean | Full planar scene reflection sampled by water; false keeps the sky fallback. |
waterReflectionScalereadonly | number | Reflection-target dimensions relative to the drawing buffer. |
waterReflectionFilterTapsreadonly | WaterReflectionFilterTaps | Water-material samples used to broaden the otherwise sharp planar image. |
planarReflectionsreadonly | boolean | Allocate the planar reflection target even where there is no water to sample it.MoreThe reflection was a water feature by accident of its wiring rather than by design. The
target existed only when Off by default, because the target is a second colour buffer sized from the drawing one and
a scene that does not sample it should not pay for it. |
hdrScenereadonly | boolean | Keep the scene's real brightness through the composite, and grade at the end instead of in
the mesh pass.MoreOff by default and it has to be, because it is not neutral. With it on, the scene target holds half floats, so a value above 1 survives to the composite instead of being clamped on the way in, and every effect there works on what the scene actually emitted. Measured on the gilded chamber, held: pixels the scene had already driven near white changed 19.4% of the time with a mean delta of 3.21, while every other band moved by 0.02 to 0.04, which is rounding between eight bits and a half float. That is the change being correct rather than the change being small, and it is still a change to a shipped picture. What it is for. Nothing downstream can tell a star at five times white from a sheet of white paper once the buffer has clamped both to 1, so a brightness threshold cannot exist. This is the prerequisite for one. It also moves the tone curve to the resolve, which fixes an inconsistency that predates it: the mesh pass was the only pass that graded, so the sky, the particles, the wet film and the water were composited ungraded beside a world that was. Needs |
highDynamicRangereadonly | boolean | Ask for a high dynamic range display: a canvas whose highlights run past paper white to as
far as the display allows, where it reports one. Off by default.MoreAsked for, not assumed. It needs WebGPU, a display reporting a high dynamic range, and a
composite that holds the light to give it ( |
underwaterAtmospherereadonly | boolean | Camera-depth water tint and scattering across every visible pass. |
plumeNoiseOctavesreadonly | number | Procedural noise octaves evaluated by plume shaders. |
capabilityClampreadonly | boolean | Whether a GPU family known to struggle may have its pixel terms clamped at construction.MoreTrue by default, because the overwhelming majority of players never open a settings
screen and a phone opening at a desktop profile is how A caller passes false to mean "this person chose these numbers themselves." A player who has deliberately asked for the good profile on a part that cannot hold it is entitled to have it, and to the frame rate that comes with it. Guessing over the top of an explicit choice is worse than a slow game, because it is a setting that silently does not apply. |
gpuTimingreadonly | boolean | Measure GPU time with timestamp queries. Off by default, and that is the lesson.MoreA frame timer is a diagnostic, and a diagnostic must never be able to break the thing it
observes. This one could: enabling it attaches a It shipped on by default and had only ever run against one implementation. Turning it on costs two queries a pass and is worth it while measuring; paying that risk on every consumer's frame for a number nobody is reading is not. Requires the adapter's |
discardResolvedAttachmentsreadonly | boolean | |
deferFramePassreadonly | boolean | Open the frame's render pass on the first draw that wants it, rather than in beginFrame.MoreTrue avoids closing and reloading the frame's attachment around every planar reflection, measured at 155 MB a frame on a scene with one mirror. False restores the eager open. A switch for the same reason as the one above: it moves when a pass is opened relative to everything else a consumer does in a frame, and the failure it could cause is a frame that does not present rather than a pixel that is wrong. |
frameGraphreadonly | boolean | Route the verbs that have been migrated through the frame graph rather than issuing them
directly.MoreOn by default as of 2026-08-25. The graph records a draw instead of executing it and
schedules what accumulated at each boundary. What kept it off was two defects that only recording could expose, both fixed the same day:
a draw took the pipeline of whichever pass happened to be open rather than the one it would
land in, and a probe bake never replayed its records, so it baked an empty cube and leaked six
draws into the next pass to open. It remains a switch on the same reasoning as the two above: it changes when work reaches the
GPU relative to everything else a consumer does, so a regression wants to be bisectable to this
rather than to a release. |
identifierGraphreadonly | boolean | Schedule each flush through the identifier graph rather than through masks. WebGPU only.MoreOff, and nothing a consumer can see changes with it on. The graph Ignored where |
clusteredLightsreadonly | boolean | Light the world from a froxel table rather than from a fixed set of uniform-array slots.MoreOff, and off is what every published scene is gated at. With it off the clustered lines
are not in the compiled shader at all — On, the point-light budget stops being What it gives up. One texture unit on WebGL2, permanently, for the table. A per-frame
binning cost that is on the main thread on WebGL2 and on the GPU on WebGPU — measured in
What would make it wrong. A scene whose lights are few and enormous: binning cost scales with a light's screen area rather than with the light count, so sixteen lights each covering the frame is the shape that pays most and gains least. The fixed path is still there and is still the right answer for it. |
cullDrawsreadonly | boolean | Skip a mesh draw whose bounds are outside the frame.MoreOff, and it is a behaviour change rather than a switch between two right answers. A correct cull is invisible: the pixels are identical and the work is not done. A cull with wrong bounds is a missing object, and bounds come from vertex positions the renderer was handed — so a mesh whose geometry is displaced in its vertex shader, as foliage under wind is, occupies more space than its positions describe. A consumer that knows its own world is better placed to cull than this is: |
screenEffectsreadonly | boolean | Resolve the scene through an off-screen target so screen-space effects can run.MoreOff is not "no blur" — it is no post-process stage at all: the scene draws straight to the canvas as it always did, and the extra colour target and full-viewport pass are never allocated. That is the honest low-end setting, because the cost is one write and one read of every pixel and it does not shrink with a smaller effect. |
sceneSamplesreadonly | number | Multisample the off-screen scene target: 1 is off, 4 is the usual asked-for figure.MoreThis is the missing half of a decision that was only half made. The renderer asks
the canvas for True multisampling rather than a post filter, because it works on geometry edges and does not touch texture detail: FXAA is cheaper and softens a 4K base colour map, which is a real loss on exactly the assets this matters for. It costs memory bandwidth proportional to the sample count and nothing else. Defaults to 1, so nothing changes for anyone who does not ask. Clamped to what the
driver reports through |
cameraMotionBlurreadonly | number | Motion blur, 0 to 1. Off by default.MoreBy the camera's motion everywhere, and by each drawn object's own motion where a
reconstruction draws one (DriftTR, WebGPU): a mover's pixels smear along its path while a
still stage stays sharp. Elsewhere a velocity buffer would be every mesh writing its motion into
a second target — a structural change to a forward renderer — so those frames blur by the
camera alone, which needs only the previous view-projection, and say so once when a draw names
where it was. Runs inside the composite pass, so it costs a depth sample and eight taps on the pixels
that are moving and nothing on a still frame. Needs |
temporalAareadonly | boolean | Temporal antialiasing: an edge stops crawling as the camera moves. Off by default.MoreAntialiasing across frames instead of inside one. The projection is jittered a fraction of a pixel each frame and the result is blended into where the last frame was, so an edge that fell one side of a pixel centre falls the other side next frame. What MSAA buys per frame this buys over eight of them, for one texture and one fullscreen pass instead of a multiplied fill rate — which is the trade a browser target wants, where MSAA on a float target is not free. Off by default, and that is not timidity. Jittering the projection moves every pixel of every frame by construction, so turning it on by default would change every capture in the repository and every consumer's reference images with them. The motion is the camera's: reprojection is exact for a static world under a moving camera and wrong for a moving object under a still one, which the neighbourhood clip rejects rather than corrects. A velocity buffer is what fixes that, and the README carries it as its own gap. Needs |
orderIndependentreadonly | boolean | Order-independent transparency: two panes of glass stop depending on submission order. Off by
default.MoreSorted alpha blending is order-dependent by construction — Weighted blending replaces the ordering with a sum and a product, both of which commute. It is exact for a single layer and an approximation of the ordering for several, where near layers count for more than far ones — which is what a pane of glass needs and what a stained-glass window of eight overlapping leaves does not. The cost is two extra targets and a second submission of the translucent geometry: the
accumulation and the revealage are separate blend states, and this renderer draws immediately
rather than into a G-buffer. That was chosen over writing both from one pass, which would need
a second fragment output and therefore another Needs |
screenSpaceReflectionsreadonly | boolean | FrameReflections | Every opaque lit surface reflects the frame by its own material: a ray marched against the
frame's depth swaps the probes' share of each pixel for what it finds, at the pixel's own
roughness and reflectance, and a surface past maxRoughness keeps the probes alone. Off by
default; true for the defaults, or the numbers. See frameReflections.ts.MoreNeeds |
depthOfFieldreadonly | number | Depth of field: how wide the blur may reach, as a fraction of the frame's height. Off by
default.MoreA ceiling rather than the effect, the same split Runs inside the composite pass, so it costs a depth sample and eight taps on the pixels that
are out of focus and one comparison on a frame that has not asked for it. Needs
0.02 is a strong, visible defocus — two per cent of the frame's height is about twenty pixels at 1080 — and 1 is the top of the range rather than a value anybody wants. |
occlusionCullingreadonly | number | Occlusion culling: how many texels wide the occlusion buffer is. 0 is off and is the
default.MoreA width rather than a boolean, because the only dial this has is its resolution and a second option for it would be a second thing to keep in step. 256 is a reasonable frame's worth: at 16:9 that is a buffer of 256 by 144, which rasterises a handful of occluder boxes in well under a millisecond and answers a rectangle test in a few reads. It culls nothing until a consumer declares an occluder, which is what Costs nothing on the GPU at all — the whole thing is arithmetic on the CPU, which is what lets it be identical on both backends and asserted without one. |
reconstructionreadonly | number | Reconstruction: how many output pixels the renderer draws for each render pixel, each axis.
0 is off and is the default.MoreOn, the scene and everything that reads it before the resolve are drawn at
Off is byte-for-byte what the renderer does today, which is what the published scenes are gated at. WebGPU only: WebGL2 has no compute stage, and it keeps the temporal resolve at native size, which is what it does now. Zero is off rather than one, because one is a thing somebody might mean — the resolve at
the output size with no upscaling, which is a temporal antialiaser with a better neighbourhood
rule than The range is 1.3 to 2 and a positive value outside it is clamped to the end it is nearest. Below 1.3 the render saves less than a third of the fragment work and the resolve's own cost eats it; above 2 the render is a quarter of the output and no reconstruction holds an edge through that. A negative number is off, not a clamp to the bottom: it cannot mean "a little". What it needs from you. A What it cannot give back is the resolution it draws at: an edge that never moves is softer
than a native frame's at any ratio, because two thirds of the pixels at 1.5 are a guess made
sharp. Measured on
|
indirectLightreadonly | boolean | Light a scene from what bounces, rather than from what was baked: DriftRay, this engine's
ray tracing, rays marched through the declared distance fields in GPU compute rather than on
the dedicated ray-tracing units WebGPU does not expose. Off by default.MoreOn, the probe grid the shading already samples is filled by tracing A switch rather than a strength. Half an indirect bounce is not a thing to ask for: a probe either holds what the chain traced or it holds what the cube held, and mixing them is two solutions averaged rather than one at a lower quality. What is adjustable is how many probes refresh a frame, and that belongs to the grid. WebGPU only, and refused in words elsewhere —
|
ambientOcclusionreadonly | number | Ambient occlusion, 0 to 1. Off by default.MoreContact shading where geometry meets: a wheel arch against a tyre, a plinth against a floor, the inside of a panel gap. It is most of what makes an imported model sit in a room rather than float in it, and its absence does not read as a missing feature — it reads as the model being pasted over the picture. Depth compared against its neighbours, in the pass that already holds the depth, so it
costs no extra target and no second submission of the scene. Needs 1 is not the intended look. It is the top of the range, and a value that dark is useful mostly for seeing where the effect is landing while tuning it. |
nightEmissivereadonly | boolean | Compile the night-side emissive term into the surface shader. Off by default.MoreThe amount is It is two options rather than one because the term is not free when it is switched off,
and that was measured rather than assumed. It names So a consumer that wants city lights on a rotating planet asks for it and accepts that its
pictures move by that much, and every consumer that does not is charged nothing and sees
nothing. See |
bloomreadonly | number | Bloom, 0 to 1. Off by default.MoreHow far a bright thing spreads past its own pixels. Without it an emissive surface is exactly as bright as the pixels it covers and not one wider, so anything meant to overwhelm the eye — a lamp, a star, a neon strip, a shaft of daylight — has to be built out of geometry that imitates a halo. A consumer asked for this after building one star from a core sphere, a second sphere at 1.22 times the radius and fifty-four additive particles, which is a hundred lines standing in for one pass. Composed with It wants Deliberately absent, because they were asked against by name: lens dirt, anamorphic streaks and ghosting. One threshold, one radius, one strength. |
bloomThresholdreadonly | number | How bright a pixel has to be before it blooms, in scene units.More1.0 means "brighter than white", which is the useful place to start with a tone curve and an exposure: everything a surface reflects sits below it and only sources of light climb past. Lower it to bloom bright paint as well as lamps. Subtracted rather than cut, so there is no second knee knob and no visible edge where a gradient crosses the threshold: a pixel just over it contributes almost nothing and one far over contributes nearly all of itself. Only consulted while |
ambientOcclusionRadiusreadonly | number | How far the occlusion sampling reaches, in metres.MoreA world-space radius rather than a pixel one, because contact shading is a fact about the geometry: a gap of two centimetres should darken by the same amount from across the room as from a metre away, and a radius in pixels does the opposite. It is projected to a screen radius per pixel from the depth there. The scale to match is the size of the gaps that matter. Half a metre suits a room and a vehicle; a landscape wants more, a model of a watch wants very much less. |
globalMediumStepsreadonly | number | How many steps a global-medium march takes per pixel. 0 is off, and is the default.MoreA ceiling rather than the effect, in the shape It is the only thing here that costs linearly. Each step is a shadow lookup and an exponential; the march is otherwise a fixed setup. 24 is a room, 48 is a landscape where the sun is low enough to make the steps visible as banding at fewer, and above about 48 the picture stops changing. Clamped to 64, which is the shader's own loop bound. What the medium is — a body of light inside a hull, or the air itself — is the distinction
|
globalMediumHalfResolutionreadonly | boolean | Whether the march runs at half the frame each way. On by default, and it should stay on.MoreHalf is a quarter of the march, and what pays for it is that a homogeneous medium is smooth everywhere except at a silhouette — which is the one thing the depth-aware upsample is built to handle. Full resolution is here for a capture that has to rule the upsample out as the cause of something, and for a part with pixels to spare. |
reflectionProbeSizereadonly | number | Cubemap face size for a baked reflection probe. Zero is off, and is the default.MoreA probe is how a reflective surface shows the room rather than a sky-and-ground gradient:
a car body carrying the light panels above it and the walls around it. It is baked by the
caller through It needs a seventeenth texture unit, and WebGL2 guarantees sixteen. On a device at the
guarantee the probe is declined with a warning and reflective surfaces keep the
approximation, rather than point shadows being dropped to make room. See
|
environmentReflectionsreadonly | boolean | Whether a surface may reflect its surroundings at all. On by default.MoreThe switch beside the size, in the shape What it costs to leave on, since that is the question this answers: one cubemap fetch per pixel of every surface that reflects anything, plus six submissions of the scene and a blur chain each time a caller bakes. Nothing per frame beyond the fetch. What it costs to turn off is the room: a metal has no diffuse term, so what it shows is its reflection. Off is a defined picture rather than an absence, which is the half worth stating. With no
probe the mesh shader keeps the sky-and-ground gradient it had before probes existed —
What would make this wrong: image-based lighting. With a real irradiance term and a prefiltered environment the gradient stops being the honest fallback, because there would be a cheaper correct answer to fall back to. |
environmentPrefilterSamplesreadonly | number | Samples per texel in the environment prefilter's GGX convolution.MoreA quality option because a phone and a workstation should not agree on it, and it is
affordable at all only because a bake happens once per scene rather than once per frame. The
named tiers live in What it costs: the convolution is this many cube fetches per texel per level, against the single fetch the box-filtered chain it replaces cost. What would make a low count wrong is a small very bright source in an otherwise dark environment, which is the worst case for variance and is where a rough level shows the samples separately rather than as a lobe. Consulted only when a probe exists, so at the default |
environmentPrefilterreadonly | boolean | Whether the lit pass reads the GGX-prefiltered cube rather than the capture's box chain.MoreOff by default, and that default is a correction rather than a preference. The prefilter shipped in 3.2.0 wired straight into the lit pass, so it changed the picture of every consumer that had a probe — which this file's own rule forbids: every feature's default reproduces today's picture, and a scene that changes because a feature was added is a regression however good it looks. It was reported as metals reading opaque, twice, before anybody connected it to a feature nobody had asked for. What the two chains are. A box level is a smaller picture of the room; a prefiltered level is the room convolved with the lobe for the roughness that level stands for. At the same material roughness the prefiltered one is far blurrier — which is the physically correct answer and is why it exists — so a surface authored against the box chain reads as a wash. On is the better construction and costs a cube of memory plus the bake. Turn it on when a
scene's materials were authored for it, and note that |