Progress update: September 2018


#1

Hi everyone! Time for another progress update with a rundown of what has been added to the framework in the last month.

During September multiple major features have been added to the particle system, which is now very close to being complete and I’ll be wrapping it up slowly as I work on remaining v1.1 features. I’ve also started to work on other (non particle related) features, first of which is the Bloom effect that’s available and ready to use right now.

In particular the new features are:

Depth buffer collisions

While the system already supports a couple of collision modes for CPU simulated particles, it didn’t support collision for GPU particles. This feature fixes that shortcoming by allowing GPU particles to use depth buffer based method for resolving collisions. This is an extremely fast collision approach and can easily work with tens of thousands of particles that might be present in a GPU simulated system.

It is controlled through ParticleGpuSimulationSettings::depthCollision, and can be modified in the same way as the other GPU simulation particle settings. Check out documentation for ParticleDepthCollisionSettings for more information on what the parameters do.

ParticleGpuSimulationSettings gpuSimSettings;
gpuSimSettings.depthCollision.enabled = true;
gpuSimSettings.depthCollision.restitution = 0.5f; // Controls bounciness
gpuSimSettings.depthCollision.dampening = 0.5; // Controls energy loss on collision
gpuSimSettings.depthCollision.radiusScale = 1.0f; // Scale of the collision radius (relative to particle size)

particleSystem->setGpuSimulationSettings(gpuSimSettings);

3D particles

Particles no longer need to be 2D billboards! Now you can use 3D meshes in place of billboards. This can be useful for simulating debris (e.g. chunks of wall flying off when a bullet hits them) among other things.

To enable this feature change ParticleSystemSettings::renderMode to ParticleRenderMode::Mesh, and assign the mesh you wish to use to ParticleSystemSettings::mesh.

HMesh mesh = ...;

ParticleSystemSettings psSettings;
psSettings.renderMode = ParticleRenderMode::Mesh;
psSettings.mesh = mesh;

particleSystem->setSettings(psSettings);

Particle lighting

Particles now support lighting along both forward and deferred rendering paths! Two new built-in materials were added to support this functionality:

  • BuiltinShader::ParticlesLit - Renders lit particles using the forward rendering path. Supports transparency.
  • BuiltinShader::ParticlesLitOpaque - Renders lit particles using the deferred rendering path. Doesn’t support transparency but supports a wider range of renderer features.

You can retrieve those from BuiltinResources same as with the currently available BuiltinShader::ParticlesUnlit.

HShader shader = gBuiltinResources().getBuiltinShader(BuiltinShader::ParticlesLitOpaque);
HMaterial material = Material::create(particleShader);

HTexture albedoTex, normalsTex, roughnessTex, metalnessTex;
// ... load the textures

// Uses the same inputs as the Standard PBR material
material->setTexture("gAlbedoTex", albedoTex);
material->setTexture("gNormalTex", normalsTex);
material->setTexture("gRoughnessTex", roughnessTex);
material->setTexture("gMetalnessTex", metalnessTex);

ParticleSystemSettings psSettings;
psSettings.material = material;

particleSystem->setSettings(psSettings);

Custom particle shaders

You can now also create your own custom particle shaders, similarly as you would create custom surface shaders for normal renderable objects. The rules are essentially the same as for surface shaders described here. The main difference is that instead of including BasePass.bslinc, you include ParticleVertex.bslinc.

#include "$ENGINE$\ParticleVertex.bslinc"
#include "$ENGINE$\GBufferOutput.bslinc"

shader Surface
{
    mixin ParticleVertex;
    mixin GBufferOutput;

    code
    {
        void fsmain(
            in VStoFS input, 
            out float4 OutGBufferA : SV_Target0,
            out float4 OutGBufferB : SV_Target1,
            out float2 OutGBufferC : SV_Target2)
        {
            // ...
        }   
    };
};

By default ParticleVertex mixin will not include information required for lighting, but if you need it make sure to add LIGHTING_DATA define before including it. This will provide some additional vertex attributes, such as world normal/tangent and world position.

#define LIGHTING_DATA
#include "$ENGINE$\ParticleVertex.bslinc"
#include "$ENGINE$\GBufferOutput.bslinc"

shader Surface
{
    mixin ParticleVertex;
    mixin GBufferOutput;

    code
    {
        void fsmain(
            in VStoFS input, 
            out float4 OutGBufferA : SV_Target0,
            out float4 OutGBufferB : SV_Target1,
            out float2 OutGBufferC : SV_Target2)
        {
            // ...
        }   
    };
};

You can check out existing particle shader implementations for complete examples, see ParticlesUnlit.bsl, ParticlesLit.bsl and ParticlesLitOpaque.bsl in Data/Raw/Shaders folder.

Bloom

After a few months focusing mostly on the particle system I’m finally moving away to some other features. First of which is the Bloom effect. It should be familiar to most of you - it highlights can bright areas of the scene, simulating a real world camera effect where the light overwhelms the camera lens. It should look especially nice when used together with emissive materials (coming soon), especially for sci-fi or nighttime modern city scenes.

The effect is controlled through the CCamera component, more specifically through RenderSettings::bloom.

HCamera camera = ...;

auto rs = camera->getRenderSettings();
rs->bloom.enabled = true; // Enables/disables the effect
rs->bloom.threshold = 1.0f; // Determines how bright something needs to be to be effected
rs->bloom.intensity = 1.0f; // As the name implies
rs->bloom.quality = 2; // In range [0, 3]. Higher number means nicer bloom but higher performance cost
rs->bloom.tint = Color(0.9f, 0.8f, 0.1f); // Color tint to apply to the effected areas

camera->setRenderSettings(rs);

And more…

A few other minor things:

  • GPU simulation now supports generic particle emitter properties such as size, rotation, color and texture animation
  • GPU simulation now supports curve distributions that change particle properties over time (either particle or emitter lifetime)
  • GPU simulation can now apply generic acceleration & damping values for simulating forces (e.g. gravity)
  • GPU simulated particles can now be sorted by distance using the general purpose GPU sort shader. You can access this shader for any massively parallel sorting purposes through GpuSort.
  • New template helpers that significantly reduce the amount of boilerplate when writing dual core objects, as well as a new approach to core object syncing that cuts down on boilerplate 3x.

On the editor side of things I’m still focusing on adding various GUI required for the particle system and have started exposing bits of the system to the editor. In particular:

  • Distribution types used by particle system are now exposed to scripting
  • Animation curve drawing moved into the C++ core, instead of being in C#
  • Added general purpose animation curve and particle distribution GUI fields

What’s next?

I’ve yet to wrap up the particle system, some of the more important remaining features are:

  • Soft particle rendering
  • Emission bursts and more emission modes
  • Some very basic evolver types like color-over-time or size-over-time are still missing

And the two remaining v1.1 non-particle system features are emissive materials and decals, both of which I will probably start working on soon.

On the editor side I’m looking forward to hooking up asynchronous scene loading, but that might take a bit until I integrate all the particle system features. There’s a bunch of editor enhancements planned but the particle system integration is taking more time than expected.

Finally there are some refactors I’d really like to get done, but we’ll see if and when I get around to them:

  • BSL 2.0 - I want to refactor the backend for the shading language. Currently it’s built as two separate parsers handling different parts of it, and there is some hacky code as well as excessive amount of redundant parsing when it comes to different variations. In short I’m not happy with it.
    I’m hoping ideally to move all the BSL syntax into the XShaderCompiler library. This will add some immediate benefits like proper preprocessor support, but long-term it will allow me to better extend and modify the language and add things like templates, less boilerplate and better variation support (for start), along with much faster shader parsing times.
    This would also open up the way for adding a simple BSL IDE, likely implemented as a VS Code extension.
  • Built-in asset import - Importing built-in assets, especially shaders, should be handled by a separate executable during build-time. Currently this is done by bsf when it first starts up, which usually results in long loading times and (seemingly random) modified assets in the Data directory.

That’s it for now. As always, huge thanks to all the patrons supporting the project!

Until next time!