Hi, everyone. This is John Jackson, one of the engine devs at Nerd Kingdom who is currently responsible for working on the latest iteration of our material system. For those of you who are new to the concept, materials are a collection of textures, properties, and shaders that when in used in combination describe how a particular object is to be rendered on screen. I’m going to discuss very briefly how our previous material system has worked, what it was lacking, what we ultimately wanted out of our materials, and how that’s being implemented.
Previous Material System
As stated before, materials are a collection of various items working in tandem to describe how any given game object should be rendered to the screen. In the simplest terms possible, for each object or collection of objects (in the case of instancing), we grab all of the textures and material specific properties to be used, “bind” the shader to be used with this particular material and submit to the graphics card to render our game object. As our artists follow a PBR workflow (Physically Based Rendering), we had a fairly clear understanding early on what our materials would need regarding all of these items.
Originally, all of our materials consisted of the base following properties:
With just these colors, textures, and shared shaders and not much else (a few other properties are not listed here), it was fairly easy to be able to satisfy most of the requirements given by the artists for the game objects they needed to represent – anything from rocks to glowing swords to glass windows. Of course, there were still many limitations to this system that eventually started to become more and more apparent as time went on.
Issues / What was lacking
Firstly, regardless of the needs of a particular material, every single one was required to have these given properties. You have no need for metallic, roughness, or emissive maps and a constant value defined in a shader will suffice? Tough. Your material will still have these arrays for textures and colors and we will still have to allocate memory for it.
This might not seem like too big a deal at first, but as an example to demonstrate the concern this causes an engine programmer, let’s assume we have a simple material and all it needs are Albedo and Normal texture maps to achieve the desired effect. Using this current material system, we’ve just wasted space due to 4 other pre-allocated texture slots as well as 6 pre-allocated colors.
Secondly, as versatile as this set-up potentially is for most materials, it’s still limited due to being a basic surface material. What do I mean by that? If you remember, all of these materials are restricted to a few shared shaders that the engine team has written for the artists and these shaders are ultimately responsible for telling your graphics card how to take all of the input textures and properties given to it and ultimately draw the object to the screen. What’s the problem with this? Well, what if none of the shaders have instructions for implementing a very custom, specific effect one of the artists requests? For example, what if I want to have a material that emits a blinking light based on a sin wave or animate its verticies using a noise texture or tile a material’s albedo texture depending on how close the camera is from a given pixel or…?
Okay, hopefully it’s obvious to you that we’re missing out on some cool stuff now. So what do we do about it?
Well, if this previous material setup is to continue to be used and these desired effects are to be implemented, we have two basic options to choose from:
Both of these, while feasible and will certainly work in the short term, have fairly significant problems in the end.
If the first option is chosen, our materials have now become even more wasteful than before. For instance, simply wanting to scroll a texture in a material requires the material holding a variable for panning speed, which is a 2 float vector. Even this small variable means that all of our materials are now inflated by another 8 bytes, which obviously doesn’t scale well at all when you consider just how many more variables you’ll start to add on for other effects.
The second option is actually what we originally implemented once certain effects were being requested. We have specific implementations written to handle animated materials, flipbook materials, dissolve materials and even added parameters for wind controls. Each of these materials derives from our base graphics material class, they each hold specific properties required for the effect, and each corresponds to its own specific, handwritten shader that handles how it is to be rendered.
For a small number of materials for specific effects, this is a perfectly acceptable solution and has worked for us for a while. But as the number of requested effects continues to grow and experimentation becomes more and more desirable for different materials, this solution becomes very restrictive and time consuming. Again, just as the first option, this simply doesn’t scale to meet the desires of our team.
Inspiration / Design for new system
Sometimes finding guidance for how to implement a new system can be a challenging task, especially if you’ve never worked on anything similar to it before. Luckily, there are plenty of great engines out there that serve as a source of inspiration regarding design and features, so it didn’t take long to do some research regarding how other engines handle their own material implementations and compile a list of features that we wanted ours to have.
After discussions amongst the team and a preliminary planning/research stage, we had a fairly decent idea what we wanted our material system to be:
New System Overview
Essentially, the new system works like this:
All shader graphs are defined by a collection of nodes that together describe how a particular material and its shaders are to be created. Each of these nodes has its own functionality and ultimately are responsible for outputting some snippet of predefined shader code. As there was a strong desire to make this system as data-driven as possible, all of the nodes are defined in a node template file which is used by the engine to fill out specific, unique data for each evaluated node in the graph. This makes it very easy to tweak the behavior of already established nodes or create new templates very quickly.
Shown above is an example of what one of these node templates looks like. Most of the properties should be fairly self-explanatory, such as “NumberInputs”, but fields like “VariableDeclaration” and “VariableDefinition” require some explanation.
All nodes used in the shader graph once evaluated boil down to variables of the type defined by their output. For instance, a ConstantVec2Node will evaluate to some variable of type vec2, as is illustrated above under the “Output” section of the template.
For each node to be evaluated, we must declare its variable equivalent in the header of our generated shader as well as define this variable in the main body of our shader. This is what these two sections are responsible for doing. Obviously simply declaring and defining a vec2 is trivial, but using this system it is possible to define whole blocks of complicated shader code under the “VariableDeclaration” and “VariableDefinition” sections of the template.
Node Template Mark-up
What’s also notable are sections of the node template that contain custom-defined markup language that is replaced upon evaluation of the shader graph. For instance, all nodes have a unique name that is used for its shader variable name as well, so anytime the system sees the markup #NODE_NAME it knows to replace this text with the given name for that particular node. The #INPUT() markup looks at the “Inputs” field for the template and uses the specified input fields in the parentheses as replacements (in this instance, the “X” and “Y” fields respectively). There are many others, such as #G_WORLD_TIME for global world time of the engine as well as markup for vertex, camera, and pixel information.
All materials following this new system have now been reduced to two main properties:
The shader graph editor, even though in a very early stage, can be used to edit and create new shader graphs used for materials. The final output for the material is the “MainNode”, which can be seen in the picture below.
Here we have an example of a shader graph that creates the simple material that I was describing at the beginning of this post and requires only texture inputs for the albedo and normal channels. All other channels will use default values that will be constants within the shader and therefore not require any extra storage.
Examples of Graphs and Materials
My hope is that moving forward with this new material system will allow our artists and designers to explore and iterate on more and more creative options.
Below are some examples of some material effects that I threw together to show off what can be done with the new material system. What’s most important to me is that iteration on these was very easy and took only a small amount of time to create.
Have a great weekend!
New patch is out that fixes a few of the issues that were introduced in the last update:
Forum thread here.
Update to Survival Games! We have finalized the starting crafts and resource variations for weapons and tools and made many improvements to the terrain generation for Survival Games maps. We are adding the first stage of our body morphing system that will start players with a random set of physical features and clothing pieces before each match.
Many improvements and bug fixes went into this build to make a generally much smoother experience for everyone. Enjoy!
Temp health indicator added to Survival Games
New Main Menu UI
Custom Survival Games Map
Added the ability to create a custom survival map using creative mode
Must be run on a dedicated server
Place a world center object and at least one spawn point to set up
New Assets Added
9 variations of the axe, hammer & pick
9 variations of the sword
3 variations of the gourd torch
5 variations of each rock
3 variations of each stone resource and each wood resource used in tool and weapon crafts
3 variations of the club
3 variations of the spear
New equipment pieces
New hair styles
New player animations
Major performance optimizations
Crafting now works anywhere
Improved generation speeds
Multiple generation bugs fixed
Fixed multiple stability issues
Properly cull animated models that are outside of generation
New particle based clouds.
Improved character kinematics control
Fixed tree collision
Improved character collision with world
Morph Target animation system
Randomly selected body weight, equipment, and hair
Previous saves will not be compatible.
Crafting no longer requires a table
New crouch and crawl states
Added new sound FX
Improved the dedicated servers features, output, and stability
Spread out spawn locations
Key Command Changes
“Q” – Removes the currently held item from the players hand
“C” – Toggle Crouch; while in fly mode, causes the player to descend
“Q” – Drops the currently held item on the ground
“C” – Toggle Crouch
Game can crash when closing the client
You can delete a saved world you are currently in, which crashes the game if you then try to pause/exit.
A high amount of particle effects in the same area can cause a severe drop in performance.
Certain world generations have terrifyingly high/deep columns of ground types.
In Survival, dropping/placing small objects like sticks near collision can cause them to fall through the world (or object).
In Creative mode, placing large objects close to yourself can cause you to fall through the world.
In Survival Games, the gourd torch light and FX remains in place if you place it and pick it up off the ground
Block placement interface/outline appears when holding a weapon/tool in Survival and not when holding a block
In Survival Games, when placing blocks, sometimes more blocks will enter your inventory
Visible seams on the Seedlings
Some clothing types display as black textures
Body stretches unnaturally when crouching and swinging a tool/weapon
In a multiplayer game, other players appear to sink into the ground slightly when crouched
In a multiplayer game, a player states aren’t correctly updated for new players joining the match. For example, holding weapons/tool or Seed/Wisp state.
On a rematch in Survival Games games, sometimes parts of the map may be missing.
Rarely, when a player spawns in a game, they can spawn inside of (and be trapped in) an object
When placing objects in Creative Mode, the object may not place exactly the same as the preview suggests
Rarely, when using a custom made Survival map on a dedicated server, portions of the map/cell may appear missing for some players. Clients seeing this bug should reconnect.
When selecting a world to load, there are issues when trying to select a specific save
In a single-player, local Survival Games map, if you die, the game will crash.
Crouched hurt and swing animations are missing
The player’s speed is too fast in first person
32-bit clients cannot connect to servers.
To get the latest updates on Nerd Kingdom tech sent right to your e-mail, fill out the form below