Given that FXAA is meant to be working in non-linear space and focus on perceived contrasts, it would mean using a reversible curve that would produce colors ideally fitted for the human eye ? (So no log space or anything alike I presume)
Back on Ombre... and I decided to play again with lens-flares (I know 🤪 ).
This time I wanted to try out the little radial projection trick from John Chapman article (https://john-chapman.github.io/2017/11/05/pseudo-lens-flare.html) to create fake streaks. It's a good start, but I will need to think about how to refine that effect. It looks nice already !
Been tweaking my lens-flare again for the past few days and now reaching a point where I want to try some kind of anamorphic bloom.
Right now I went with a hack where I modify one of the downsample texture when it is fed for the upsample pass. It is giving me a rough idea of what to expect, but it's not good enough yet (not sharp enough, and some flickering issue to manage still).
Will likely need to do a proper downsample/upsample process too.
This morning I also quickly tried to add some fake halation effect (light bleeding into darker areas).
It's basically a highpass filter using the bloom downsamples and the current scene color texture, and then isolating the bright parts to make them bleed into the dark areas.
Currently it's an additive blend done with the HDR color, so it adds light. It low enough to no matter too much. Maybe I should use a lerp too to be more energy preserving ?
Turns out the Love framework had a bug for a few months and wasn't loading sRGB texture properly.
Got fixed today after my report, so now colors match properly:
I gave a try at clamping (like @EeroMutka suggested) but as I expected, because I use a non-thresholded and energy preserving bloom method, clamping kills off the HDR range and bloom becomes non-existent.
The current idea I wanna try is doing a copy of the first downsample (full or smaller res) and blend it into the next frame downsample. Just to see if it helps with the spatial/temporal aliasing.
Will figure out ghosting issues afterward if it becomes promising.
This week I continued with my fog stuff and added local volumes of analytical fog.
It's going to be quite useful to make moody effects in scenes.
So far I got Sphere and Box shape working, but I'm thinking about doing cones (for spotlights) and maybe cylinders (for dirty liquids container or holograms).
Instead I decided to finally look into rendering cubemaps.
Currently I'm not writing any code much, I'm trying to evaluate all my needs to properly build the architecture.
So far I only renderer a single point of view: the main camera. Cubemap introduce additional ones, and later I will have Portals too. So there are some rework needed in how I manage my rendering loop.
Quite a few days later and the refactoring is almost done. The engine is rendering again and this time in a more contained way, so I should be able to render cubemaps soon ! :D
With the post-process chain now working again, I thought I could try to add a depth of fieldpass as well, re-using some of the recent bokeh shader I used for my lens-flares.
It didn't go as planned, but it made some nice colors at least ! :D
Here is an example of the bleeding. I used pre-multiplied CoC but it's not enough and any kind of pixel rejection breaks the separable nature of the blur.
Here the bright lights are visible behind the limit of the character silhouette, showing the bleed into the foreground.
I'm currently looking at the Scatter & Gather approach, but I wonder if anybody tried an hybrid method. Like using S&G for small bokeh and sprites for large bokeh ? Or maybe using S&G for far DOF and sprites for near DOF ?
I wonder at which points sprites could help performance, but because large ones cause overdraw. 🤔
A few days have passed and I finally got most of the DOF post-process working !
My hexagonal bokeh works well and is relatively cheap. I even got some nice additional effects like chromatic aberration on the bokeh itself.
The last detail I'm trying to figure out is how to properly fade the center of the bokeh pattern to make "holes" in the shape.
I have already something working, but it's not perfect yet:
Bonus: for fun I'm trying to do an heart shaped bokeh.
First results are quite funny, but not really useful. 😅
(I currently rethinking how I should distributes the sample to fill the shape.)
Like when I used the depth bounds extension at the time, this tricks had almost no impact and I presume the extra cost was coming from the depth buffer copy stuff.
So this is making me think that performance improvement will only come with smarter geometry setup.
I think I need to look in ways to subdivide the geometry but in a less taking way during the compute pass.
@froyok everything your account posts is great. Question about your engine: I see you focus a lot on the rendering side, what are your plans for handling level geometry and PVS? BSP, portals? Are you going to design an ECS?
@GabeMoralesVR I'm thinking of Portals for culling yeah, either in an automated way or hand-authored. It's something I would like to tackle this year.
No plans with ECS system/design for now, will see if I need it when I will start to scale things up.
And if you are curious, for Physics I plan to integrate an engine (likely ODE or Jolt).
@froyok I've been working my way through writing a UT-style engine for the Dreamcast, but all my domain knowledge is in the quake tree. Please keep posting especially when you start writing your portal system, I'd greatly appreciate it!
The optimization that actually worked meanwhile was the fact I was launching threads during my compute dispatch just to discard them afterward in the shader code.
Now instead I launch exactly the number I need and compute a better index for processing my geometry.
So just helping the GPU schedule things better gave me 0.04ms saving on around 140K meshes (went from 0.1ms to 0.065ms). That's on my beefy GPU, I presume on my old laptop this will be even better.
Ha, figured out the issue ! I was actually expanding the alpha radius during my fill pass, which created those gaps.
So it's mostly working okay now, trying to adjust how I tweak the focus range to make it easier to play with (I like the idea of a start/stop positions).
Every time I start a new renderer project I tell myself I'll write it to support multiple views up front, and every single time I end up not doing that.
@ataylor Haha yeah, I can imagine. It adds quite a few constraints. I needed to rethink a lot of things. Doesn't help that this is my first real engine. 🤪
I have been banging my head quite a bit the past two days.
I know I'm in corner case, but I'm once again astonished at the lack of good tooling out there for writing in this kind of format.
I would really like avoid writing my own DDS encoder, because I feel it's one of those rabbit holes it will be difficult to get out of. But it's starting to feel like I won't have a lot of options.
Last silly idea I got: storing my RG11B10 cubemaps in RGBA8 with RGBM compression instead. That will require some on the fly decoding but that's might be tolerable.
@froyok We store R11G11B10 in RGBA8 but without rgbm encoding. Just spread the bits. 32 bits vs 32 bits. Works as PNG files for instance. No extra decoding required.
Went with RG11B10 as expected to store my cubemap. Each mip as an individual file stored as a binary blob into a common zip file per probe.
Not the prettiest but it does the job for now. I don't have to deal with RGBM at least.
I also had to do the pbr balls test !
No that I only take care of radiance here. There is no IBL irradiance. I will see if I do something for it or not via cubemaps.
I need to rethink how I manage my lights (once again) because right now the light casting shadows are rendered as additive light, which means the IBL contributions is applied several times.
Until now I didn't have a notion of "ambient" lighting.
Filament LUT use swapped Red and Green channels in its LUT.
My initial one minus trick was just a lucky fix. I'm glad I took the time to figure out what was happening.
@froyok I can relate to that. The only reason I know about it is because I was following closely when the changes were made. It's also super cheap and easy to integrate when someone ran the math for you :p.
TLDR: rough surfaces tend to be darker than they should be because masking and shadowing terms don't account for rays that escape the micro facets after a few bounces. A precomputed multiple scattering factor is stored in the LUT to compensate for that.
@froyok Just as another reference point, we've written an accurate analytic fit for the integrated FG term in MaterialX, allowing you to render energy-compensated GGX reflections without texture lookups.
@nick It's pretty fresh and requires specific hardware I believe, it just got introduced in DX12 so I don't know about OpenGL and also my framework would need to support it too which isn't the case yet.
Anyway, BC6h is the ideal target here. :)
@froyok fascinating thread. I vote for make your own tooling, as if it doesn't exist, someone else would probably use it too (spoken as someone who has written many tools that I doubt anybody used)
@shram86 It will happen at some point, it's the second time that I end-up facing a wall, but I just don't want to spend the time on it right now unfortunately. Hence my frustration. 😅