Over the past few months, our engineers have been coding away, and now it’s time to show off what they’ve been up to. This update is the first of several to bring Open-World support to Bright Engine, starting with various performance improvements, as well as the long-awaited Asset Brush Toolkit. Beyond this, shadows have also received some much-needed attention, and another round of bugs have been squished.
A Huge Thank you to our Patrons! Your contributions make Bright Engine Possible!
Jason Bucotte • Daniel Burkhart • Massum Zaidi • Mark Makarainen • Çağlayan Karagözler
New Feature: Continuous Distance-Dependent Terrain LODs
One of the biggest challenges to supporting an open-world environment is rendering terrain. After all, drawing massive terrains is a costly process. And the system that was implemented in the last update was simply incapable of achieving vast landscapes, even with its a chunked LOD system. What’s more, a problem with the older method was the formation of gaps between LOD levels, especially at shorter ranges.
So a new solution was needed. And we turned to Continous Distance-Dependent LOD (CDLOD) as a new rendering technique for terrains in Bright Engine. The process behind this method is pretty complex. But a quick and simple explanation is the smooth morphing between LOD levels of a terrain chunk. That way, the terrain in the far distance will be very low quality, while terrain up close will have maximum detail (Even more than before when layered with tessellation). And best of all, no gaps!
Here you can see the system in action on a 128x128m chunk of terrain (note that the different colours represent different LOD levels).
Users still have complete control over LOD distances and can select the morphing range most suitable to their scenes. Higher morphing distances give a smoother transition at the cost of performance.
The difference in performance is quite substantial. A 500 square meter landscape can still comfortably render at 60 fps with this new system, whereas the older method struggled to remain above 15! That is quite a boost, and we’ve still got a long list of optimisations yet to be implemented.
New Feature: Order Independent Transparency
Transparency is a source of a lot of headaches in rendering pipelines. Due to the way modern GPUs process data, to correctly render transparent objects like glass, these objects have to be sorted so that the most distant one is drawn first and the closest last. The problem is this sorting process is expensive to perform. And when layered with instanced rendering, it adds a whole other layer of complexity.
Existing alpha testers will know that Blended Transparency has been broken in Bright Engine for quite some time precisely because of these problems. But after careful research, we’ve finally implemented a viable solution. Enter Order Independent Transparency.
As the name suggests, this algorithm is order-independent. Meaning that the whole process of sorting transparent objects by distance is no longer required. This helps eliminate an entire process from the render loop and drastically simplifies the instanced rendering systems. Using some very snazzy mathematics and some clever rendering tricks, transparent objects can be rendered with correct blended results at a minimal cost.
New Feature: Asset Brushes
One of the most highly requested features to be implemented into Bright Engine was a model brush toolkit. We actually added one back in version 0.1.4c. But after quite a few updates and a complete overhaul of the material handling system, this feature broke quite badly. Now, after all this time, it’s back, and it got a whole lot more powerful!
One of the biggest challenges any development studio has when creating an open-world environment is the time it takes to populate it. After all, if you needed to place every tree and bush in your world, you’d slowly go insane. This is where asset brushes come into the picture. The simple premise is, you add a collection of assets to a brush, such as models. Assign some specific rules about how and where each asset can be placed. And then paint your environments in seconds instead of hours.
The new Asset Brush tools are significantly more powerful than their original implementation. Thanks to the research documents kindly provided by Guerrilla Games (developers of Horizon: Zero Dawn), we’ve been able to design a completely new approach that is both faster and provides users with even more control.
Beyond basic rules like which terrain surfaces an asset can be placed on (for example, trees are not allowed to grow on rock), users can now specify additional rules regarding the distance between other objects, placement altitude, terrain slope angles, density thresholds, clearing areas, and all sorts of transformational modifiers as well as randomisers.
The applications for this technology are pretty endless. And yet despite it already providing a substantial amount of power and time-saving solutions for environment artists. It’s still far from complete. In future updates, we’ll be extending this system even further by allowing users to place other types of assets such as sound fields, lights, particle etc., as well as introducing a global simulation layer for effects such as wind and weather.
Improvements: Shadow Rendering
The real-time shadow map rendering systems have been pretty crisp and swift until recent updates. While we’ll discuss the performance implications a little bit later, one glaring problem that Bright Engine suffered from was shadow quality, especially when it came to directional shadows. Despite being fully equipped with a cascade shadow map rendering system for long-distance shadow drawing, shadows (especially for foliage) often came out like pixelated garbage even when using a 4k resolution shadow map.
It took a while to figure out exactly what was going on but the core issue revolved around world-space positions of fragments being clamped from 32-bit depth to 16-bit depth. This bug has now been squished, and shadows have returned to their former crisper self.
But we didn’t stop there. We’ve also taken the liberty of giving the cascade shadow mapping system a bit of a makeover. Originally cascade distances were set using view space values. This seemed like the most intuitive solution at the time. Boy, were we wrong in that decision. While this approach had its advantages, it created a real mess when the camera render distance was changed. Visible gaps between shadow maps became a prevalent issue. They resulted in many Alpha testers wondering why their directional shadows looked wrong.
To fix this, all cascade calculations are now performed in world space. Not only did this fix all the main issues in one clean sweep, but it also allowed users to set their distances in meters which is far easier to visualise. On top of this shift, we’ve added a more advanced (and efficient) percentage-closer filtering blur on shadow maps to better hide resolution pixel artefacts. The quality of this blur can also be controlled by the user.
The difference is pretty substantial.
Improvements: Rendering Architecture
Performance is a problem that will continue to be at the top of our development team’s priority list. Bright Engine is still quite young. And there remains some significant room for improvement. In this update, we wanted to start addressing the major problems with the existing rendering pipeline.
We spent a good week deploying GPU profiling tools and collecting a lot of behavioural and performance data. Then after careful analysis, we discovered a long list of items that need to be addressed. We can't deny the list is pretty substantial. And so, we’ve been unable to implement the vast majority of performance fixes. However, we did make a start.
First and foremost, Bright Engine now uses a hybrid rendering pipeline of both Forward and Deferred rendering techniques. Specifically, all opaque objects (like terrain and most models) are handled using a Deferred approach, whereas transparent objects still use the Forward rendering method. But what does this all mean?
These are quite advanced rendering topics that are too complex to explain in detail. But if you want to know the dirty details, Brent Owens has written a good article explaining the difference. A quick and simple explanation is that in a Deferred pipeline, all the expensive lighting calculations are performed later, where lots of fragments (pixels in 3D space) have already been eliminated. This drastically cuts the time required to perform these calculations resulting in faster computation.
The visual results look identical. However, performance is no longer dependent on scene complexity but rather screen resolution. This helps support incredibly dense environments that simply wouldn’t be possible to perform on a Forward rendering pipeline. And at the same time, it gives a notable boost in rendering speeds.
The shift to a deferred pipeline was quite a time-consuming endeavour. But it wasn’t the only performance optimisation we accomplished. As previously mentioned, shadows had been taking their toll on the framerate, and it’s something we wanted to address quickly.
In v0.1.8b, the shadow map passes combined took a total of 6.2ms to calculate per frame. In case you didn’t know, 60 FPS is equal to a frame draw time of 16.67ms. So around 40% of the render time was being used to simply calculate shadows. Needless to say, that’s simply too long. After doing some digging, we discovered that most of the bad performance originated from rendering dynamic foliage - 5.12ms to be precise.
After several gruelling days of going through shader code, we’ve managed to add multiple layers of optimisations that cut the render time down to 1.03ms. In total, the shadow passes now take around 1.5ms to compute, or 9% of a frame. Needless to say, that is a considerable improvement! And consequently, the fps from these two performance updates alone boosted the Plains Demo scene performance from an average of 31 to 52 FPS.
That’s definitely a significant boost. But that's far from acceptable. Moving forward, we’ll be implementing more performance upgrades on our list to get this fps even higher!
Improvements: User Experience
A common complaint we’ve received is the sometimes clunky performance of the Editor. A large part of this is tied to the lacklustre performance of the rendering pipeline. However, there are a few editor specific issues that we’ve managed to address.
Firstly we’ve added clipping planes to quite a few editor-specific shaders, such as bounding box selection. This latter example has allowed a significantly more smooth object selection experience. Existing users will be glad to know the original selection delay has been largely eliminated.
Beyond this, we’ve also continued to tweak the UI experience of the Editor. A few updates ago, Bright Engine underwent a much-needed facelift which brought about a much prettier interface. However, like most overhauls, this also introduced some various bugs. We’ve squished most of the UI related bugs now. And also added new checks to hide certain settings that are not relevant to the user when editing specific systems.
Improvements: User Experience
Bright Engine has been in development for several years now. And that means a lot of old code is beginning to plague our codebase. We’re making an effort to eliminate as much old code as possible or outright replace it with something that doesn’t look like it was written by a drunken maniac.
As such, the pre-PBR lighting code has been completely removed, as has the majority of the pre-instanced rendering functions. Overall, about 3,000 lines of old or redundant code have been eliminated.
Bug Fixes[World Editor]
- Fixed bug where the shadow resolution drop-down settings in the render panel were not showing
- Fixed bug where dynamic foliage only appeared in the shadow map of the closest shadow casting light source to the camera
- Fixed Bug undoing an action on the terrain would set the constant emission colour of the selected texture layer to black
- Fixed bug where brush settings were not hidden when switching out of the Environment panel
- Fixed bug where hiding the Environment panel sometimes didn't work
- Fixed bug where hiding and then showing panels in the wrong order caused a crash
- Fixed bug where deleting a light source after changing its type would cause a crash
- Fixed bug where view-space normals were incorrectly calculated for terrain
- Fixed bug where objects with no assigned materials were not drawn correctly in the SSAO pass
- Fixed bug where framebuffers were switching incorrectly at the start of the main loop.
- Fixed bug where GPU memory barrier was not working correctly and caused problems on older GPU models
- Fixed bug where removing a sun did not clear the assigned texture from memory (memory leak)
- Fixed bug where removing a cloud layer did not clear the assigned textures from memory (memory leak)
- Fixed bug where weather cloud colour modifiers did not apply correctly when transitioning between rooms
- Fixed bug where transitioning into a room without any textures assigned to a cloud layer caused a crash
- Fixed bug where directional shadow maps were being sampled twice instead of once in PBR shaders
- Fixed bug where gBuffer textures were being shifted into 8-bit space resulting in inaccurate shadow rendering
- Fixed bug where changing render panel settings did not take effect until after defocusing the UI component
- Fixed bug where vertex colour painting for terrain did not save if the user switched out of the environment panel
- Fixed bug where vertex colour painting for foliage did not save if the user switched out of the environment panel
- Fixed bug where adding models with more than one material caused incorrect placement rendering
- Fixed bug where transforming models with more than one material caused it to sometimes disappear
- Fixed bug where removing models with more than one material caused other models to disappear
- Fixed bug where instanced data was calculated twice per frame instead of once
- Fixed bug where certain depth of field settings were not being hidden on startup depending on the method selected
- Fixed bug where clouds were not included in the bloom pass
- Fixed bug where deleting a node and then reloading the material would cause a crash if that material had a Time Data node in it
- Fixed bug where selecting a node after deleting one previously would sometimes cause a crash
- Fixed bug where deleting a texture node caused a crash
- Fixed bug where duplicating a texture node caused a crash
- Fixed bug where setting the alpha mode to blended produced red artefacts
- Fixed bug where writing directly after closing a string would sometimes cause a crash
- Fixed bug where writing on the last line of a script sometimes caused a crash
We’re quite proud of our achievements for Bright Engine v0.1.8c being the last update in this series. We think users will be pleased with the improved stability and new features it has to offer.
There is still a significant amount of room for improvement for these systems. Throughout the 0.1.9 update series, we’ll be addressing most of these. This includes faster performance, improved rendering quality for terrain and tessellation, re-introducing model LODs, advanced weather simulation layers, and a long-awaited feature - water simulation.
But beyond the visuals, we’ll also be making a return to scripting by introducing Game User Interface systems so that users can actually start getting their game menus, inventory systems, and character panels implemented.
It should be an exciting set of updates that pushes Bright Engine one step closer to a production-ready state. And we can’t wait to share the results soon!