Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul of volume Raymarching #206

Merged

Conversation

shadielhajj
Copy link
Collaborator

@shadielhajj shadielhajj commented Aug 16, 2024

The current volume raymarching sub-system is quite limited: It is not physically accurate, it's not very flexible (it doesn't support specifying volume density and works with directional light only), and most importantly, it cannot co-exist with other raymarching renderers (such as PBR). The entire scene is rendered either as a solid or a volume, not both.

The proposed rewrite:

  1. First steps towards a physically accurate system
  2. Per-fragment volume density
  3. Supports directional lights and point lights.
  4. Support for opaque objects shadowing (see below)
  5. Integrated with other raymarching renderers, such as PBR

Here's a scene rendered with PBR materials and and an fbm volume
Screenshot 2024-08-20 154410
fbm3

The code for the volume

#define RAYMARCH_VOLUME

Medium raymarchVolumeMap( in vec3 pos ) {
    float sdf = fbm(pos);
    vec3 scattering = vec3(1.0, 1.0, 1.0);
    vec3 absorption = vec3(0.0, 0.0, 0.0);

    return mediumNew(scattering, absorption, sdf);
}

Note how the volume renderer is enabled with RAYMARCH_VOLUME. The map function is implemented in raymarchVolumeMap (by default) which must return a Medium. Easy peasy.

Note also that the volume is not simply overlay on the scene, but properly integrated in space. In the example below, the volume sphere correctly sits in between the cone and the doughnut.
Screenshot 2024-08-19 232036

Another cool feature is volumetric shadowing by opaque objects. Very handy to create atmospheric effects. Enable it with RAYMARCH_VOLUMETRIC_SHADOWS
shadowing

Bonus Track:
Rework of the AOV interface in raymarch.*. Just pass the desired output parameters. If full AOV output (Material) is desired, #define RAYMARCH_AOV should be explicitly specified. This is for optimization purposes.

#define RAYMARCH_AOV
vec4 raymarch(mat4 viewMatrix, vec2 st, out float eyeDepth, out Material mat);

vec4 raymarch(mat4 viewMatrix, vec2 st, out float eyeDepth);

vec4 raymarch(mat4 viewMatrix, vec2 st);

Including the legacy view (lookAt) signatures

#define RAYMARCH_AOV
vec4 raymarch(vec3 cameraPosition, vec3 cameraLookAt, vec2 st, out float eyeDepth, out Material mat);

vec4 raymarch(vec3 cameraPosition, vec3 cameraLookAt, vec2 st, out float eyeDepth);

vec4 raymarch(vec3 cameraPosition, vec3 cameraLookAt, vec2 st);

@patriciogonzalezvivo
Copy link
Owner

Omg! this is so impressive! I'm really excited for it

Tiny suggestion:

  • renaming VolumeMaterial class be Volume
  • adding a separate folder call volume/ where new.*lsl lives together with other volume-related functions instead of usingVolumeNew.*lsl

@patriciogonzalezvivo
Copy link
Owner

This is fantastic work @shadielhajj! Do you mind providing a use example?

#endif
) {

vec4 raymarch(mat4 viewMatrix, vec2 st, out float eyeDepth, out Material mat) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic here is that if eyeDepth or mat are not used the compiler will optimize it?

Copy link
Collaborator Author

@shadielhajj shadielhajj Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a Material struct anyway. Whether the user passes it to us, or we have to create it for them.

// user-provided struct
vec4 raymarch(mat4 viewMatrix, vec2 st, out float eyeDepth, out Material mat) {
...
}

// lygia-provided struct
vec4 raymarch(mat4 viewMatrix, vec2 st, out float eyeDepth) {
    Material mat;
    materialZero(mat);
    return raymarch(viewMatrix, st, eyeDepth, mat);
}

In the single-sample version. There's no extra cost associated with returning AOVs.
However, in the multi-sample version, there is indeed an extra cost associated with averaging all the material properties. Which is why we require the user to enable AOVs using RAYMARCH_AOV

#if RAYMARCH_AOV
    add(matAcc, mat, matAcc);
#endif

#if RAYMARCH_AOV  
    multiply(mat, RAYMARCH_MULTISAMPLE_FACTOR, mat);
#endif

@shadielhajj
Copy link
Collaborator Author

shadielhajj commented Aug 22, 2024

This is fantastic work @shadielhajj! Do you mind providing a use example?

Thanks, @patriciogonzalezvivo!

Here's how to add a spherical fog volume to the raymarching example scene and enable volumetric shadows

#define RAYMARCH_VOLUME
#define RAYMARCH_VOLUMETRIC_SHADOWS
Medium raymarchVolumeMap( in vec3 pos ) {
    vec3 scattering = 0.05*vec3(1.0, 1.0, 1.0);
    vec3 absorption = vec3(0.0);
    return mediumNew(scattering, absorption, sphereSDF(pos-vec3( 0.0, 0.50, 0.0), 3.0 ));
}

And here's how to add noisy volumetric fog

#define RAYMARCH_VOLUME
Medium raymarchVolumeMap( in vec3 pos ) {
    vec3 scattering = vec3(1.0, 1.0, 1.0);
    vec3 absorption = vec3(0.0, 0.0, 0.0);
    return mediumNew(scattering, absorption, fbm(pos));
}

Volumetrics are quite heavy computationally, so you might want to tweak the quality params:

#define RAYMARCH_VOLUME_SAMPLES 64
#define RAYMARCH_VOLUME_SAMPLES_LIGHT 32
#define FBM_OCTAVES 3

lighting/light/attenuation.glsl Outdated Show resolved Hide resolved
lighting/light/attenuation.glsl Outdated Show resolved Hide resolved
lighting/light/attenuation.glsl Outdated Show resolved Hide resolved
lighting/light/attenuation.glsl Outdated Show resolved Hide resolved
@patriciogonzalezvivo
Copy link
Owner

Sorry for all the single comments!
I'm adding some flags to keep parity with all raymarch examples. I'm assuming for your examples that Volumetric is only desired when defining #define RAYMARCH_VOLUME

Hope you don't mind the additions

Repository owner deleted a comment from patriciogonzalezvivo Aug 23, 2024
@shadielhajj
Copy link
Collaborator Author

@patriciogonzalezvivo. Sorry didn't mean to delete your comment, was actually trying to delete mine.

this one makes total sense

#if defined(RAYMARCH_VOLUME)
Medium RAYMARCH_VOLUME_MAP_FNC( in vec3 pos );
#endif

Regarding this one

#if defined(RAYMARCH_VOLUME)
Medium res = RAYMARCH_VOLUME_MAP_FNC(position);
float density = -res.sdf;
vec3 scattering = res.scattering;
vec3 extinction = res.absorption + scattering;
#else 
float density = 0.0;
vec3 scattering = vec3(0.0);
vec3 extinction = vec3(0.0);
#endif

I don't really like the idea of breaking the algo because a #define is not present, apart from the code clutter. How about something like this?

#if defined(RAYMARCH_VOLUME)
#include "raymarch/volume.glsl"
#endif

@patriciogonzalezvivo
Copy link
Owner

patriciogonzalezvivo commented Aug 23, 2024

The issue with that approach is that the includes inside raymarch/volume.glsl could be flag "trapped" inside the define flag.
Most of the #include parsers for GLSL, or at least all of those I have implemented, tried to do their best to prevent duplications so they get checked against a map using their absolute path.

So all this files could be consider as "already added" while in reality they are not accessible because they are behind a flat

#include "map.glsl"
#include "../light/attenuation.glsl"
#include "../../generative/random.glsl"
#include "../../math/const.glsl"
#include "../medium/new.glsl"

To solve that all #include parsers for python, JS and C++ need to resolve all #defines first or directly not try to prevent duplications.

What about doing this?

#if !defined(FNC_RAYMARCH_VOLUMERENDER) && defined(RAYMARCH_VOLUME)
#define FNC_RAYMARCH_VOLUMERENDER

...

@patriciogonzalezvivo
Copy link
Owner

Brillant PR @shadielhajj!

@patriciogonzalezvivo patriciogonzalezvivo merged commit 6ac82b8 into patriciogonzalezvivo:main Aug 23, 2024
@shadielhajj shadielhajj mentioned this pull request Aug 23, 2024
@shadielhajj shadielhajj deleted the refactor/volume branch August 23, 2024 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants