OverviewFilesCommitsIssuesDocsReleasesPeers

ifsrenderer-linux commits / 81b7b4b

Add the flame as light, and stop restarting on camera jitter
"its all wrong now, you can barely see the nebula" / "it also doesnt stay in one spot" - both mine, from the compositing change. **Barely visible.** Alpha-over needs an alpha, and the only sensible one - the tonemapped density - is about 0.03 across a nebula's body (log10(1 + 8*0.01)). Multiplying an already-tonemapped colour by that erased it. The blend is now ADDITIVE_PREMULT, which is what a flame actually is: emission. Empty space has rgb 0 and adds nothing, so the viewport still shows through, and the fractal keeps its full brightness instead of being attenuated twice. **Would not sit still.** The view-projection was compared for EXACT equality, so floating-point noise and Blender's smooth-view easing invalidated the histogram almost every frame. The image resampled continuously and never settled, which reads as the fractal refusing to stay put. Comparison now has a tolerance. **Also fixed, found while checking the first one:** sync_from_scene copied the panel's brightness onto the session every redraw, and the property default is 8 - so loading nebula_sample, which asks for 14.55, was silently dimmed to 8 on the first frame. The panel is now seeded from the document on load. Checked the templates before theorising: my first guess was stale depth-of-field smearing the image, but nebula_sample has aperture 0.0, so that was wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
81b7b4b93549029cb2604286f158426f433ad84c
by plankatron <fleeky@gmail.com> · 2026-08-16T23:04:58.000Z

Changed files (2) +32 -9

blender/__init__.py +3 -0
……@@ -213,6 +213,9 @@ class IFS_OT_load(Operator):
213213 return {'CANCELLED'}
214214
215215 _sync_iterators(settings, doc)
216 # Seed from the document, or the live session's sync replaces it with the
217 # property default on the first redraw. nebula_sample asks for 14.55.
218 settings.brightness = doc.brightness * 8.0
216219 settings.status = (f"{doc.title}: {len(doc.iterators)} iterators, "
217220 f"native {doc.width}x{doc.height}")
218221 self.report({'INFO'}, settings.status)
blender/ifs_viewport.py +29 -9
……@@ -65,12 +65,12 @@ void main()
6565 vec4 h = texelFetch(accum_tex, c, 0);
6666 float peak = max(float(texelFetch(max_tex, ivec2(0, 0), 0).x), 1.0);
6767 float d = max(h.w, 1e-12);
68 // Alpha IS the tonemapped density, so empty space is transparent and the
69 // fractal COMPOSITES over the viewport - grid, objects and gizmos stay visible.
70 // An opaque quad here blanks the whole 3D view.
71 float lum = log(1.0 + brightness * h.w / peak) / log(10.0);
72 float s = lum / d;
73 fragColor = vec4(clamp(h.rgb * s, 0.0, 1.0), clamp(lum, 0.0, 1.0));
68 // ADDITIVE, because a flame is EMISSION. Alpha-over needed an alpha, and the
69 // only sensible one - the tonemapped density - is ~0.03 across a nebula's body,
70 // which multiplied an already-tonemapped colour into invisibility. Adding light
71 // leaves empty space untouched (rgb 0 adds nothing) AND keeps full brightness.
72 float s = log(1.0 + brightness * h.w / peak) / log(10.0) / d;
73 fragColor = vec4(clamp(h.rgb * s, 0.0, 1.0), 1.0);
7474}
7575"""
7676
……@@ -130,7 +130,13 @@ class Session:
130130 raise ValueError(f"unknown dirty kind {kind!r}")
131131
132132 def sync_from_scene(self, settings):
133 """Copies the panel's values onto the document, flagging what changed."""
133 """
134 Copies the panel's values onto the document, flagging what changed.
135
136 Brightness included - but the panel must have been seeded from the
137 document first, or this silently replaces a document's 14.55 with the
138 property default of 8 on the first redraw and the fractal renders dim.
139 """
134140 self.brightness = settings.brightness
135141 for item, iterator in zip(settings.iterators, self.doc.iterators):
136142 iterator.opacity = item.opacity
……@@ -251,6 +257,18 @@ class Session:
251257 (eye.x, eye.y, eye.z), (forward.x, forward.y, forward.z),
252258 (up.x, up.y, up.z), fov_degrees=fov, engine_fov_quirk=False)
253259
260 # How much the view-projection may drift before the accumulation is thrown
261 # away. Exact comparison restarts on floating-point noise and on Blender's
262 # smooth-view easing, so the image resamples every frame and never settles -
263 # which looks like the fractal refusing to sit still.
264 VIEW_EPSILON = 1e-5
265
266 def _view_moved(self, view_proj) -> bool:
267 if self.last_view is None:
268 return True
269 return any(abs(a - b) > self.VIEW_EPSILON
270 for a, b in zip(view_proj, self.last_view))
271
254272 # ---------------------------------------------------------------------- draw
255273
256274 def draw(self, context):
……@@ -264,7 +282,7 @@ class Session:
264282
265283 space = context.space_data
266284 view_proj = self._view_projection(space.region_3d, space.lens)
267 if view_proj != self.last_view:
285 if self._view_moved(view_proj):
268286 self.last_view = view_proj
269287 self.invalidate()
270288
……@@ -313,7 +331,9 @@ class Session:
313331 self.tonemap.uniform_sampler('max_tex', self.maxbuf)
314332 self.tonemap.uniform_int('out_size', (self.width, self.height))
315333 self.tonemap.uniform_float('brightness', self.brightness)
316 gpu.state.blend_set('ALPHA')
334 # ADDITIVE_PREMULT is dst = src + dst: the fractal adds light to whatever
335 # the viewport already drew, so the scene stays visible underneath.
336 gpu.state.blend_set('ADDITIVE_PREMULT')
317337 self.batch.draw(self.tonemap)
318338 gpu.state.blend_set('NONE') # leave the viewport's state as we found it
319339