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

FogVolumes, FogShaders, FogMaterial, and overhaul of VolumetricFog #53353

Merged
merged 1 commit into from
Oct 29, 2021

Conversation

clayjohn
Copy link
Member

@clayjohn clayjohn commented Oct 3, 2021

Implements: godotengine/godot-proposals#2261

FogVolumes

FogVolumes are entities that can be rendered into the VolumetricFog's Froxel buffer. They can have 3 different shapes: ellipsoid, box, or world. As the name suggests world shape FogVolumes are not culled and are rendered for every froxel in the froxel buffer. They are very useful for world-space effects like global height fog. However, because they write to every froxel, they have a more significant impact on performance than the other shapes, use them sparingly.

FogVolumes using ellipsoid or box shapes can set their extents with the extents parameter.

Screenshot showing ellipsoid, box, and world volumes, as well as a subtractive volume
Screenshot (224)

When temporal reprojection is enabled, moving volumes can result in "ghosting" artifacts. It is best to keep movement or animation of fog quite slow.

FogMaterial

The FogMaterial contains a short FogShader that sets density, albedo, and emission and allows scaling the density by the SDF of the shape and a 3D density texture.

FogShaders

FogShaders are attached to Fog Volumes. Internally they are compute shaders that run for each froxel within the Froxel-Aligned-Bounding-Box of the FogVolume (calculated just before rendering). The real beauty in FogShaders is that they use Atomic operations so that multiple volumes can be rendered at the same time.

Radeon GPU profiler showing the GPU barely working while simultaneously rendering 96 simple FogVolumes
Screenshot (222)

FogShaders can output DENSITY, ALBEDO, and EMISSION. They must write to DENSITY and may write to ALBEDO, and EMISSION. If you do not write to either ALBEDO or EMISSION the shader will skip adding them and save on a few instructions.

By default. all parameters are additive. However, you can set DENSITY to a negative value to reduce density locally, when DENSITY is negative, neither ALBEDO nor EMISSION are written. ALBEDO is converted to scattering internally (which is additive)

VolumetricFog

Volumetric fog has been modified to use a PBR material model (internally, scattering, absorption, emission) exposed to the user as Density, Albedo, Emission. It also takes material anisotropy into account. Anisotropy refers to the direction that light is scattered in through a volume. Fog tends to scatter light forward, while smoke tends to scatter light equally in all directions. The default 0.2 makes the fog slightly brighter when facing towards a light source.

I have also changed the integration to integrate lighting over the depth of the froxel (as described here: https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite) which results in significantly more plausible lighting and a smoother integration.

Future work:

  • I have not added icons for FogMaterial or FogVolume, I would appreciate a hand with those if someone is interested in making some icons
  • FogParticles (will need to do a proposal)
  • Consider storing per-volume anisotropy
  • consider adding Sky as a light source
  • NoiseTexture3D
  • Generate SDFs from Mesh
  • Consider reprojecting material buffers and scattering buffer instead of just scattering

@clayjohn clayjohn added this to the 4.0 milestone Oct 3, 2021
@clayjohn clayjohn changed the title Addition of FogVolumes, FogShaders, FogMaterial, and overhaul of VolumetricFog FogVolumes, FogShaders, FogMaterial, and overhaul of VolumetricFog Oct 3, 2021
@briansemrau
Copy link
Contributor

briansemrau commented Oct 3, 2021

Hope I'm not providing feedback a little too early, given this is still a draft. I'm quite excited to see this PR.

A prominent issue I've noticed is a yellowing/blackening effect when I reduce DENSITY close to zero. This occurs when looking along the edge of a volume as well (I assume due to sampling). I've messed with all sorts of environment settings and volumetric fog options, and it seems to remain. At no point am I setting DENSITY outside the range [0, 1].

Edit: I was testing on this branch merged with the latest master. Seems it's not an issue on your branch, unmodified. Silly me, but also somewhat suspicious.

Here are some screenshots/examples:

`DENSITY = 0.005` on the left, `0.05` on the right:

image

The effect is enhanced with EMISSION:

image

Here's a very large volume with `DENSITY = 0.003`:

image

Overall, this is quite problematic when calculating density using worley noise:

image

@clayjohn
Copy link
Member Author

clayjohn commented Oct 3, 2021

@briansemrau That is very odd. I am not sure what would have caused that.

At any rate, testing is very welcome, I will be pushing docs and some QOL changes and taking this out of draft within the hour! :)

@clayjohn clayjohn force-pushed the VULKAN-fog-volumes branch 2 times, most recently from 73e00be to 1f1f8b3 Compare October 3, 2021 22:06
@clayjohn clayjohn marked this pull request as ready for review October 3, 2021 22:08
@clayjohn clayjohn requested review from a team as code owners October 3, 2021 22:08
Copy link
Contributor

@briansemrau briansemrau left a comment

Choose a reason for hiding this comment

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

Just some minor things I noticed while testing.

scene/resources/fog_material.cpp Outdated Show resolved Hide resolved
scene/resources/fog_material.cpp Outdated Show resolved Hide resolved
scene/resources/fog_material.cpp Outdated Show resolved Hide resolved
doc/classes/Environment.xml Outdated Show resolved Hide resolved
doc/classes/Environment.xml Outdated Show resolved Hide resolved
@clayjohn clayjohn force-pushed the VULKAN-fog-volumes branch 4 times, most recently from 5a24a21 to e7cacfc Compare October 4, 2021 05:59
@clayjohn clayjohn requested a review from a team as a code owner October 4, 2021 06:50
@clayjohn clayjohn force-pushed the VULKAN-fog-volumes branch 2 times, most recently from b5bafe0 to 56b235f Compare October 4, 2021 15:56
@Calinou
Copy link
Member

Calinou commented Oct 4, 2021

I've done some testing and it seems that when shadows are enabled for an OmniLight3D or SpotLight3D, its volumetric fog will always appear to have the same size and will be very faint. When you adjust the light's range, the volumetric fog will keep the same size. This problem goes away if you disable shadows for the light and reload the current scene.

Also, would it be possible to add a "albedo multiplies density" option to WorldEnvironment's volumetric fog? This would allow for purely additive volumetric fog without a constant emission component (since you can't adjust the emission's alpha value).

…metricFog

Co-authored-by: Brian Semrau <brian.semrau@gmail.com>
@clayjohn
Copy link
Member Author

@akien-mga done! :)

@akien-mga akien-mga merged commit 75baf16 into godotengine:master Oct 29, 2021
@akien-mga
Copy link
Member

Thanks!

@bruvzg
Copy link
Member

bruvzg commented Nov 8, 2021

The shader compilation fails on macOS, IIRC Metal (and MoltenVK) do not support atomic operations on most types of images.

ERROR:  - Message Id Number: 0 | Message Id Name:
	VK_ERROR_INITIALIZATION_FAILED: Shader library compile failed (Error code 3):
program_source:198:96: error: array subscript is not an integer
            uint _491 = atomic_fetch_add_explicit((device atomic_uint*)&density_only_map_atomic[pos], uint(final_density), memory_order_relaxed);
                                                                                               ^~~~
program_source:203:94: error: array subscript is not an integer
            uint _534 = atomic_fetch_add_explicit((device atomic_uint*)&light_only_map_atomic[pos], final_scattering, memory_order_relaxed);
                                                                                             ^~~~
program_source:213:97: error: array subscript is not an integer
                uint _590 = atomic_fetch_or_explicit((device atomic_uint*)&light_only_map_atomic[pos], force_max, memory_order_relaxed);
                                                                                                ^~~~
.
	Objects - 1
		Object[0] - VK_OBJECT_TYPE_SHADER_MODULE, Handle 5686092208
ERROR:  - Message Id Number: 0 | Message Id Name:
	VK_ERROR_INVALID_SHADER_NV: Compute shader function could not be compiled into pipeline. See previous logged error.
	Objects - 1
		Object[0] - VK_OBJECT_TYPE_PIPELINE, Handle 5686095968
   at: _debug_messenger_callback (drivers/vulkan/vulkan_context.cpp:157)
ERROR: vkCreateComputePipelines failed with error -1000012000.
   at: compute_pipeline_create (drivers/vulkan/rendering_device_vulkan.cpp:6629)

@clayjohn
Copy link
Member Author

clayjohn commented Nov 8, 2021

@bruvzg do you know if there are any extensions to enable atomic operations? This technique really relies on atomics in order to be viable.

edit: is the issue that the images are unsigned? If so, we could probably work around it and use signed integers on Apple devices

@bruvzg
Copy link
Member

bruvzg commented Nov 8, 2021

is the issue that the images are unsigned?

No, it's the same if I change it to r32i/iimage3d.

Relevant MoltenVK issues - KhronosGroup/MoltenVK#345, KhronosGroup/SPIRV-Cross#835

@Calinou
Copy link
Member

Calinou commented Nov 8, 2021

I guess we should create a fallback for macOS that disables fog volumes. Ideally, support for global volumetric fog should be kept. It's not very important to support volumetric fog on macOS yet anyway, since I expect only the most powerful Macs to handle volumetric fog smoothly right now (M1 Max with 24 or 32 GPU cores).

@clayjohn
Copy link
Member Author

clayjohn commented Nov 8, 2021

Hmmm, I wonder if it would work if we used a storagebuffers on Mac instead. I imagine performance would take a hit and our code would be slightly more complex, but it could be worthwhile.

edit: it looks like atomic operations can be used on buffers in "device" space (I believe this is equivalent to Vulkan's storage buffers). https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf. The trick is going to be how to get MoltenVK to adequately translate a StorageBuffer containing an array of uints into a device buffer with an array of atomic_uints

@bruvzg
Copy link
Member

bruvzg commented Nov 24, 2021

Hmmm, I wonder if it would work if we used a storagebuffers on Mac instead. I imagine performance would take a hit and our code would be slightly more complex, but it could be worthwhile.

Changing it to the storage buffers seems to work.

I have not compared it to the original, but performance seems to be OK - #55281

@macryc
Copy link

macryc commented Apr 29, 2022

y texture in action, looking great 🙂

Hey, how does one get a fog volume from an image??

@Calinou
Copy link
Member

Calinou commented Apr 29, 2022

Hey, how does one get a fog volume from an image??

  • Import any image format supported by Godot.
  • Select the image in the FileSystem dock.
  • Go to the Import dock, change the import mode to Texture3D.
  • Restart the editor, then select the same image in the FileSystem dock again and go to the Import dock.
  • Change the number of slices that represent the 3D texture. For an extruded 2D image, leave it to 1. Click Reimport.
  • Specify the image file as a texture in the FogVolume's material property (FogMaterial).

@macryc
Copy link

macryc commented Apr 29, 2022

Hey, how does one get a fog volume from an image??

  • Import any image format supported by Godot.
  • Select the image in the FileSystem dock.
  • Go to the Import dock, change the import mode to Texture3D.
  • Restart the editor, then select the same image in the FileSystem dock again and go to the Import dock.
  • Change the number of slices that represent the 3D texture. For an extruded 2D image, leave it to 1. Click Reimport.
  • Specify the image file as a texture in the FogVolume's material property (FogMaterial).

Wow, it does indeed work! Thanks, I really appreciate it. I wonder if it's possible to also animate it. Like convert this fog material to a shader and perhaps use TIME to animate the uvs or something...

@Calinou
Copy link
Member

Calinou commented Apr 29, 2022

Wow, it does indeed work! Thanks, I really appreciate it. I wonder if it's possible to also animate it. Like convert this fog material to a shader and perhaps use TIME to animate the uvs or something...

To animate an arbitrary 3D texture over time, you'd need to have a 4D texture (which is not supported by GPUs to my knowledge). Instead, using a noise function to warp the 3D texture over time is a better idea.

If you only need an extruded 2D texture, then a 3D texture can be used to animate this texture over time using a custom shader.

@macryc
Copy link

macryc commented Apr 29, 2022

If you only need an extruded 2D texture, then a 3D texture can be used to animate this texture over time using a custom shader.

I was thinking noise, but since i'm using a 2d image imported as a 3d texture, I should probably animate using a shader. If only I knew how to do that...:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants