Real-Time Planetary Lighting in UE4
ft. A Lesson in Building Early and Often
The final feature I wanted to improve before moving to the pre-alpha was the lighting. Early on in this project I decided that the lights in the engine wouldn't suffice. The point lights didn't have a large enough radius to be effective in a solar system, and lighting planets lit with individual spot-lights wouldn't be feasible when it cane to asteroids. Thus I implemented a system of unlit textures that would emit color only on faces whose normal was pointing towards the sun. This system though didn't have support for two features that I wanted with the lighting: reflections and shadows.
The Directional Light Solution:
Before attempting to rewrite the lighting system, I decided to give the built-in lighting one more chance. I'm insanely thankful that I did this because while doing some googling I found
this thread on the unreal forums that explains multiple methods of lighting planets in a scale model of a solar system. One of which is to have a single directional light that just constantly points to the player. This makes any planet that you visit correctly lit, but any planets on the other side of the star are lit from the wrong side.
This shouldn't be a problem in my game as planets on a different side of the star would be too far away to be rendered. Though I would like to fix this in the future, I can deal with only the locally correct lighting.
Reflections were then super simple to implement by using an already existent mask for oceans that was then plugged into the roughness of the material. I only had to add one line of code to get it working and I got a great result.
|
With 'Light' being a UDirectionalLightComponent |
|
Planet casting shadow on a moon |
|
Moon casting shadow on the planet |
|
Reflection of star on a planet's ocean |
Improving Stars Once Again:
With the remainder of time I had this week I decided to make some changes to improve the stars as the lighting had made me take a second look at them.
Particle Memory Leak
The first thing I wanted to fix was the issue that happened when you changed any property of the stars. If you were to change the color of the star for example, each time you were to commit a new value it would take up 5.8MB more space in RAM. Not a huge issue at first glance, but if you were to spin the color picker around for fun to watch the colors change, which I did, the editor would go from using ~2.5GB memory to over 20 in less than a second.
The first clue to what was happening here was that if toy did change the color, you could still see particles being emitted with the old color. This made me believe that the color change was spawning a new emitter system and not deleting the old one. This hypothesis was confirmed when I used breakpoints to see that the name of the Niagara component was increasing by one each time.
I eventually tracked the issue down to OnConstruction(). Basically, OnConstruction() is run each time PostEditChangeProperty() is, resulting in a new particle component being initialized each time. A simple nullptr check cleared that issue up quickly.
Emitter System Attachment
I wanted the particles to be attached to the star they were spawned with and more relative to them. This was simply achieved by changing the particle spawn from SpawnSystemAtLocation() to SpawnSystemAttached() and by checking 'local space' in emitter properties.
Optimizing the Surface Emitter
While looking at the particle system I had an idea to optimize the surface particles. Just as a refresher, the surface particles are meant to emulate the small spindly bits that wave on the surface of a star, e.g.
At the time I was making these particles with individual sprites, taking tens of thousands of sprites to cover the entire surface. The idea I had was to replace these sprite particles with ribbon particles. It was all going fine until I added the curl noise to make the ribbons wave around. When I did, it went from this:
To this:
No idea why this happens other than that it has something to do with changing the acceleration of ribbons on particle update. This issue had to be shelved for the moment though as I had discovered an even bigger problem with the very core of the game.
A Lesson in Building Early and Often:
As much as I hate to admit it, I haven't tried to package the game since I started; a cardinal sin of game development. The first time I tried to package was after I finished this iteration of the lighting to make 100% sure it worked in game. In the package log I came across a few show-stopping issues that could have been caught if I built the game any earlier in development:
- PostEditChangeProperty() (which I have used in most of my project files) is not allowed to be used in packaged projects
- The 'AssetTools' public dependency module is not allowed to be used in packaged projects
- The 'UnrealEd' public dependency module (an extension of the editor and used in the ProjectCleaner plugin) is not allowed to be used in packaged projects
- SetNiagaraVariableLinearColor() throws an exception when building for some reason
All of the issues can be fixed for packaging by including preprocessor macros like #if WITH_EDITOR. The overarching problem is not solved though; that being that AssetTools and UnrealEd are integral to the game. AssetTools is what makes creating procedural textures and static meshes possible. PostEditChangeProperty() not being available in a packaged build is okay though as I can work around that.
For the next week I will be attempting to remedy these issues before development on the pre-alpha is set to start in May.
Comments
Post a Comment