December 18, 2016

UE4 - Quick Tips, Tricks, and Optimizations

I have been learning a lot about Unreal Engine over the past few years. My training in the engine officially began in the fall of 2012, and since that time I have spent all of my efforts in learning all I can about the new engine and how it works. Whether you're a seasoned veteran or just getting started, there are plenty of tricks that you can use to help improve your material building:

General Material Building
  • UE4 has a lighting, reflection, and shadow environment built-in. Using the standard system will always give you the best results. Purely custom code will not blend as well in the environment and won't be as optimized.
  • Reuse code often: even if you have a cheaper method to calculate something, when you reuse branches of code for other purposes in the shader, the result is cached and duplicated. When you use cheaper methods, you recalculate what is already done.
    • If you can reuse code for vertex displacement in the normals, you can save a LOT of instructions for very complex materials!
  • Use textures instead of procedural methods. While this may sound like a bad idea, textures use a simple array lookup while procedural methods require tons of calculations. Textures are also very complex and artist-driven, so it is much better and easier to use a large 2k texture for wind displacement than some complex procedural code.
    • Keep in mind transforms only work in world position offset, not tessellation. Limit your tessellation usage to only what's necessary.
  • For objects with high vertex instruction cost, limit the vertex count as much as possible. Vertex shading is multiplied per-vertex. Grass meshes only need 4 vertices. Water planes, unless you need translucent fog, only need 4 as well. When translucent vertex fog is necessary, try to keep the vertex count under 4,000.
  • Power of 0.5 and 2 are cheaper than a power of 3 or 4, those are cheaper than powers of 5, and that is cheaper than powers of 5.75. This is due to the Power node using different methods to compress exponents: inverse square and square compress as if you multiplied with or without one-minusing the result, and is much cheaper than anything else. Multiples of that (4, 8, 16, etc.) are cheaper than that. Exponents that are a whole number calculate using the Exponent expression in HLSL. Decimals require the Power expression, and are the least efficient. But all this can be done simply using the Power node without the need for multiplying by itself.
  • Don't just slap a texture, normals, and roughness on an object and call it done! Overlay two textures on top of each other and get some more variation out of it! These layered texturing techniques were pioneered in Banjo Kazooie and Ocarina of Time. They are minimally difficult to render, and the end result is well worth the effort.
  • Frenel shading in a PBR environment is welcome! Custom reflections, however, are not. Use reflection captures and stationary skylights for areas that need a reflection. If you must use a custom reflection, the object may not match the physical environment it's placed in.
Translucency
  • Translucent rendering is cheapest when unlit. However, you lose the benefit of shadowing and local lighting. Non-directional per-vertex (on lower polycounts) is the second cheapest. Per-vertex directional is third cheapest. Unless you need localized reflections on translucency, those lighting models can save you a hundred or more instructions over Surface Translucency Volume and Forward Shading models.
  • All forms of translucent rendering don't feature GGX specular rendering. You can bring it back by adding a light vector blueprint in your level and using the engine's HLSL code for GGX specularity.

    Custom Node:

    float a = Roughness * Roughness;
    float a2 = a * a;
    float d = ( NoH * a2 - NoH ) * NoH + 1;
    return a2 / ( PI*d*d );
  • Traditionally, translucency does not fare well for cheap depth of field methods like Gaussian blur. Seperate translucency is unaffected by DoF, and disabling separate translucency is always affected by Dof, there is no in-between. However, you can fade out the translucent object as it recedes into the distance, and you can use MipValueMode > MipBias to blur a textured translucent surface into the distance. The cheapest and easiest way is to use the pixel depth to increase the bias and blur into the distance. Use a power of 0.5 to get the blending just right. Keep in mind, this is all done with "Seperate translucency" enabled so your translucent material stays nice and crisp up front, but blurrier in the back.
Lighting
  • Use a skylight! Skylights account for all light not directly hit by the sun. This is what allows your environment to be lit all around.
  • To save on the cost of rendering dynamic GI in an outdoor environment, use a skylight and change the bottom color to a greenish hue. This will emulate the bounce light provided by green grass. The results are not perfect, but very similar. You can change the color to whatever you want, and the result is easy to render and looks pleasing.
  • Keep your use of dynamic lights to a minimum. While the engine was designed to handle multiple lights overlapping each other, the pixel cost spikes in areas where lights overlap. If you do have overlapping lights and are using the precomputed lightmass process for GI, do not exceed 4 lights overlapping at once. Otherwise, lightmass will need to bake more than 4 channels for light GI to be rendered. The fewer stationary overlap the better, but if you need more, switch to dynamic and use soft static lights for indirect lighting.
Particles
  • Small particles like sparks are best handled by the GPU. They can collide cheaply and spawn in the millions. Large particles like dust clouds are best handled by the CPU. You get the power of particle cutouts and subimage UVs to limit overdraw while providing variation. Unless you have more than 1000 CPU particles onscreen at once, you do not need to worry too much.
  • Very small particles can get cut out with various AA methods. TXAA is the most aggressive. Switch to FXAA if you need small particles, or increase your particle size.
Landscape
  • Draw calls are cheap nowadays. Maximize efficiency by making more component sizes at fewer quads per section. This allows unused components to be culled out.
  • In some cases, tessellation is preferable to parallax occlusion. For exceptionally steep, not-too-sharp detail and low-vertex environments, tessellation is already implemented on Landscape, so you can handle triangle explosion without killing the card. But for crisp, sharp detail that doesn't need to be tessellated, POM is much better.

October 15, 2016

Jake Progress




Jake is my personal pet project. I use this project to learn new techniques, and I've made some good progress over time. I first started working on the project with the 4.6 build of UE4, and I've continued to update it ever since. Somewhere around version 4.10 I decided to completely scrap the old project and start all over. I didn't like the direction of the original, which had very small islands, portals, and very little space to work with. Now, it has taken a life of its own.

I'll get into the details of the character Jake and the rest of the gameplay later. For now, you can just enjoy this gorgeous WIP shot of the windmill section in this game.

And now the technical details: The grass and flowers are procedurally generated using UE4's grass tool, developed for the Kite Demo. It uses the landscape's surface information to position the grass. Unfortunately, the flowers and grass must be combined together, so you cannot have multiple different kinds of grass layers with different flowers.

The grass and flowers blow in the wind based off of some combined normal maps that pan over the surface (via world position mapping). Normal maps contain information in a -1:1 range. The normal maps are then converted from local space to world space to determine the actual normals and positioning of the meshes. This saves a lot of shader instructions because we use textures instead of expensive procedural methods or vertex normal tangents to determine the normals and positioning, and the same code feeds both. It's relatively easy to set up, too. However, this method is not to be confused with tessellation as tessellation does not support local space transforms. The object vertices are the only ones needed for the animation.

The rope fence is one of my favorite personal touches and details in this project. The project looked very barren without some environmental aspect to it, but I needed something non-invasive to really blend in with the landscape. Then I recalled Super Mario Sunshine's elastic ropes. My ropes do sway in the breeze with vertex animation, again using local transform to swing the ropes from side to side. The post itself is a custom Blueprint where I can select the next post in sequence and the rope is spawned automatically. Using look at rotation, I can spawn the ropes with the right rotation and scale them to the right length. A squareish shape for the rope actually aids in the gameplay (flat top side is obviously up) and also in the vertex count. A single rope only has 160 vertices.

The windmills themselves have the Perfect Tile System assets applied, and they also have parallax occlusion mapping on them. The modeling is very basic, but I take good care of my surface materials to make sure it looks right in the scene. The cloth uses the two-sided shader. The wood is opaque. Shadows are cast on the cloth, and the cloth casts shadows as well.

The sky is a custom shader I made myself that takes normals into account for the clouds. It's entirely unlit, but I use light vector information to model shading in the clouds. The horizon is much brighter than the zenith, giving a sense of atmosphere in the distance and better contrast with the clouds up above.

September 28, 2016

Tessellation for Landscape


Tessellation is something of a holy grail for graphics: the ability to physically shape the surface based off of displacement maps and smooth out the surface with more polygons on the fly. But for the longest time it was seen as an impossibility that was extremely inefficient at best. Tessellation first appeared in UDK's DX11 renderer 5 years ago, but it wasn't until UE4 that the method was really perfected. The preferred method to tessellation was a pre-tessellated higher-poly base mesh, and for landscape some extremely inefficient parallax occlusion mapping. However, with UE 4.13, tessellation will only be applied to the lowest LOD. This allows the triangle explosion to only impact the closest meshes. And with DirectX 12 significantly reducing the impact of draw calls, I was able to achieve a better framerate using tessellation than POM on a landscape material with very few blends.

Pros to tessellation on landscape:
  • Use artistic displacement maps to bump real geometry!
  • Does not require premade geometry, only landscape material and displacement map!
  • Proper sillhouettes! Proper ambient occlusion and shadows! Proper depth! Etc.
  • Extremely and easily customizable! Since tessellation is realtime generated, surface values can change over time and are impacted immediately.
  • Can be blended with other materials very easily.
  • Tessellation can be baked into physical properties of the landscape heightfield.
  • Cheaper than POM (in some cases)!
Limitations of tessellation on landscape:
  • ...None!
  • Some distant objects will render flat, but their depths would typically not be noticed anyways, and other methods would reveal more artifacts.
  • In some extremely large landscapes with far less efficient LODs, POM or simple parallax is more performant than tessellation.
It is recommended that landscapes with tessellation have a very efficient LOD system. More sections and components at somewhat smaller sizes (31x31 quads or smaller) would render more efficiently than larger sizes and fewer components. This is because the triangle explosion result of tessellation would yield too many polygons for large sections and components to render efficiently. Also, DirectX 12 cuts draw call times by 1/8, so you can render more draw calls and components efficiently.

In order for tessellation to be enabled, go down the basic material settings under the Tessellation category and under D3D11 Tessellation Mode choose "Flat Tessellation." PN Triangles does smooth out the surface through splines, but the differences won't be noticeable under either method, and Flat Tessellation will be cheaper. I left adaptive tessellation checked on to lower the LOD in the distance, but it might be forcibly enabled just by being a landscape material, so your selection there may not matter.


The easiest setup for tessellated displacement is to multiply your heightmap by the vertex normal, then multiply that by a scalar parameter for your displacement. If you are blending heightmaps together, the final blend will be fine. I also recommend setting the tessellation multiplier down to 0.5 to save a bit on performance. The "reference plane" defaults to the ground, and all tessellation is lifted from the standard surface level. To change this to displace downwards (like POM default settings), subtract your heightmaps by 1. Subtract by 0.5 for an even 50:50 displacement above and below the surface (though the exact balance depends on the range of your heightmaps as well).

To make less triangles explode, under Project Settings > Rendering > Tessellation > Adaptive pixels per triangle, set this to a higher number (default is 48). For a game running in 4K using tessellation on landscape for medium-sized bumps, you might be able to get away with 200-600 pixels per triangle. Keep in mind the distance LOD is controlled by the Tessellation Multiplier while the number of triangles is controlled by the adaptive pixels-per-triangle value in the Project Settings when optimizing.

Also make sure to keep in mind that tessellation, while correctly calculating shadows, will not calculate the new surface normal. You will still need to provide a proper normal map with your displacement map to get accurate shading and lighting. Once you enable tessellation, you can even do things like animated displacement for lava flow. And since tessellation is compatible with texture maps, you are not limited to vertices for your shading. Your only limit is your imagination!



September 15, 2016

Seamless Mountain Texturing in UE4


Landscape materials in UE4 can be quite daunting to tackle. Most of what your player will be looking at in your environment is the landscape, yet if the landscape material is too complex, it will become the worst performance hit in your project. Multiple surfaces need to blend in with each other, and the surface needs to work both up close and further in the distance, which means expensive materials all around! But mountain ranges are especially tricky with those tall, vertical mountainsides that stretch out textures beyond belief. So, how exactly is anyone expected to make a good mountain range or cliff using landscape in a videogame, exactly? Turns out, the answer is pretty novel: use world positioning to map a texture at the X and Y planes, blend around the edges, then blend that with a top surface to prevent too much texture stretching at the top. Along with that, you can use another simple color blend texture (1 pixel wide, 64+ pixels tall) to wrap up and create those nice Grand-Canyon-esque layers in the rock.

Benefits to this method:
  • Completely seamless texturing!
  • Supports normal maps, roughness maps, parallax occlusion, and all other texturing methods!
  • Looks great on both steep cliffs and smooth slopes!
  • World-aligned means you can set up geologic layers in the rock, and no matter how your models are positioned the material will still look appropriate!
  • World-aligned also means you don't need to setup UVs on any objects! Yay!
  • Cheaper than most complex materials to achieve XYZ texturing (22 extra instructions to go from basic UV texturing to XYZ seamless).
Limitations of this method:
  • The cost is more expensive than basic texturing via UV coordinates: plenty of blending and interpolation needs to be done to get it to function properly. But this cost is not bad for the final result.
  • Can be tricky to get complex textures to look good. Wrapping textures and smooth tops work the best. Complex blends can work with a more advanced mask setup, but also costs more in performance.
Make sure your texture tiles well when blended smoothly from side to side. This is crucial because if the texture does not look good when blended smoothly, it will break the illusion of a seamless blend. It also needs to tile seamlessly from all sides. Emphasize horizontal streaks in your texture. A normal map is also recommended. While my example uses a simple 0,0,1 smooth top normal, your top texture and normal can be whatever you want. However, more detail will not tile as well as less detail and doesn't blend as nicely. A complex blend for the top that effectively masks unused portions of the texture can be constructed on top of this method to create a very seamless and detailed surface, but it will require a greater performance hit than the method shown here.

This method was used on a rock surface costing only 110 instructions (vs. 88 for basic texturing). All things considered, this is the cheapest and best-looking XYZ blendable method I know of.


Absolute World Position, divided by your desired size, split into two separate masks: GB and RB. Or, a planar map from the X direction, followed by the Y direction. Plug those into two copies of each texture you wish to apply. Lerp between these two texture maps at their extremes, which I calculated by using the absolute value of a dot product between the vertex normal and X direction. The absolute value simply flips the opposite side, the end result is a smooth gradient mask across the object with surfaces angled towards the X direction as white, and perpendicular to the X direction as black. This is how the blend wraps around appropriately. Then, I lerped that with a dot product between 0,0,1 and vertex normal to determine a mask for the top surface. We will blend between the X and Y surfaces first to get a nice cylindrical blend, then the top blend later. You can use power, lerp, and clamping adjustments along with complex blends to control this top mask. For this example I lerped [-0.3;1.0] and raised it to a power of 4 to get a sharper blend towards the top and prevent the textures and normals from simply washing out.

Side blend (-X and +X are white, -Y and +Y are black)
Top blend (Z+ is white, perpendicular and bottom is black)
Combined Blend (red is Side Blend, green is Top Blend)
Final lit blend
The horizontal "layered" bands are also calculated in world position. While it is possible to put this coloring into the wrapping texture, it's nice to keep this separate to break up the repetition while giving the coloring a more consistent, logical approach. Eliminating this strand from the rendering process can optimize the technique.



This technique works on any kind of rocky assets! Just note that texture seams in the UVs will also cause the vertex normal to split, and, by association, this texturing method as well. If you're serious about using this method for perfectly seamless texturing on rocks, you can import your model without any UVs at all. Smooth normals alone will be fine. But if you like your Z-Brush normals, you can use a world-space version of those instead of the vertex normals :)

August 15, 2016

Water for Games - Cheap, Simple, Effective

71% of the Earth's surface is water. Yet, of all the materials I've ever made, water seems to be the most difficult to get right. Even in UDK with planar reflections and Phong shading, water was still incredibly complex. Once you crack the code to good water, your game can turn from a nice scene to a gorgeous, luscious environment. For the sake of this blog post, I will avoid delving into vertex displacement and dive right into translucent rendering. This kind of water is good for a calm ocean, a shimmering lake, the famous Carribbean seas, inter-coastal waterways, and pool water, with more emphasis on translucency and reflective effects. Raging oceans and rivers will be very different.




I first really got excited about realtime water with Super Mario Sunshine. For years I crowned its water as a technical and artistic achievement of the highest order. Keep in mind games like Goldeneye 007 released just 4 years prior to this game, and this was before we got a chance to experience the water from Half Life 2, Assassin's Creed, Uncharted, and Crysis. Before all these games, there was Super Mario Sunshine. The water was just so blue and seemed to look so beautiful without ever being inappropriate. But as it turns out, the water in Sunshine was, for the most part, a hoax. The water surface itself was just a texture that bobbed up and down with the waves. At close distance the water was completely translucent, while the "shimmering" texture would be emphasized further away. The water itself was clear: objects underneath the water were vertex painted to a clear blue color giving the water that strong Caribbean blue saturation. Sunshine used custom mipmaps to push the shimmering effect away from the player, but we can use other means to simulate this.

In real life, water reflects, refracts, and scatters light. You can make the shader much easier to render by mimicking the results instead of the effects. The combination of reflections and refractions looks like turbulence that bends around the bulges in the water's surface. Specularity can help to define actual reflections on the surface. The scattering can be simplified to a depth-based color.

Pros to my water technique (inspired by a combination of Super Mario Sunshine and Super Mario Galaxy's techniques):
  • Cheap! 45 instructions for texture highlights, 72 for Phong specular, 74 for GGX specular, 76 for texture+GGX specular.
  • Fluid! Looks like flowing, fluid water.
  • Translucent! You can see objects underneath. And those objects have proper depth.
  • Distance opacity! The depth method inherently makes the water more "reflective" in the distance and more translucent up close.
  • Tilable, but doesn't look it! 2048 and different tiling factors between the distortion asset and texture/normal asset reduces tiling in the long range.
  • Memory and texture sampler efficient! While the 2048 texture can seem like a lot, you can hide this in a channel of a compressed 2k mask, effectively getting this endless ocean for less than 1MB of texture data. And if you use 1k maps, you can cut that size by 1/4. And the distortion normal map only needs to be 256x256 pixels. Two textures for beautiful water!
  • Flexible. Since the lighting is handled using forward-rendering techniques, you can get benefits out of this system that you can't with UE4's deferred renderer, like changing the color of the specularity, coloring by sphere maps, iridescent effects.
  • Supports day/night cycle. By updating the sun's rotation in blueprints, the specularity on the water can change according to the sun's position.
  • Supports color gradient by depth. Expanding the depth fade and lerping the result to different colors, you can make deeper waters a much darker blue than shallow areas.
Limitations of my water technique:
  • No shadows. While you can multiply the specularity by a custom texture map or through vertex painting, there is no real process to get shadows baked on here.
  • Only supports one light. The sunlight. This method will not work for reflections from lights on a pier. But it can provide general water shading to support such methods.
  • Some manual effort required to provide light direction. 




All you need to create this effect is a simple distortion texture (a tiling bubbly normal map), a shimmery, circular noise grayscale texture, and that's it! For a more advanced version with GGX specular highlights from the sun, you'll also need a normal map of that circular noise grayscale texture.




This material only costs 46 pixel shader instructions and 45 vertex shader instructions despite being a translucent material with depth fading. If you have a sand texture underneath, this water will be cheaper to render than the sky. Making the material unlit and removing vertex fog will eliminate a ton of instructions.

In the picture above, the Roughness GGX Specular is not even used, so the distortion is actually doing most of the heavy lifting for this effect. Pan the bubbly normals, mask RG, and scale down by 0.05 to 0.1, or however strong you wish the distortion to be. Add that to the UV coordinates of your water texture (tile it as many times as you need). If the texture is imported as an uncompressed linear grayscale, multiplying it by itself will give you a cheap gamma-corrected version to add on. Multiply it by the brightness you wish the highlights to be, and add it to your ocean color. A deep saturated cerulean blue with some extra intensity works great. The depth fade in opacity will make your water more translucent with objects closer to the surface and more colored when objects beneath the water are further away. For an ocean and most water surfaces in general, this means you will get a more opaque appearance in the distance.

To get the right texture you need, start with a 256x256 pixel grayscale noise. Enlarge this to 2048x2048. Use the gradient map tool to make white circular bands according to the noise texture. Two bands through the gradient map adjustment is enough to get a good effect. Convert this "heightmap" to a normal map, and you can use the normal map instead (or both).




The GGX specular method calculates a specular highlight using the same principled GGX specular code that UE4 uses for calculating specular highlights for stationary and dynamic lights. The HLSL GGX code in the custom node is:

float a = Roughness * Roughness;
float a2 = a * a;
float d = ( NoH * a2 - NoH ) * NoH + 1;
return a2 / ( PI*d*d );

And the two inputs are Roughness and NoH, where NoH is the Blinn model. I added a Lambertian diffuse and multiplied it by itself to get a more focused, gamma-corrected result. The light vector is pulled from a Blueprint actor that is placed in the level and automatically grabs the directional light/sunlight's rotation.




This code will then feed to a Material Parameter Collection that communicates with the material to set up a specular highlight on the water.




This version of the water shader replaces the texture highlights with the GGX specular highlights calculated from the normals, so there is no texture 74 pixel shader instructions, 2 textures.




And this version adds both the texture and the normal specular for highlight rendering. The texture is not multiplied, only added. 76 instructions, 3 textures.


Who Wants to Be a Millionaire: Breakdown of Sound Design

I might be making a quiz game soon for a client, and I think I can learn from the design decisions of what has to be the greatest quiz game ever made, bar none: Who Wants to Be a Millionaire. More specifically, the sound and music. The sound doesn't just get more ominous as the game progresses, the sound design is actually quite complicated with many surprising twists along the way.

$100, $200, $300, $500, $1,000 Questions: https://www.youtube.com/watch?v=P4utAJ-JraE

At the beginning, there are two big "let's play" tracks for the start of the game at $100 and the continuation before $200. This is a fanfare that hones in the game at the beginning. The game as a whole is designed to be intimidating and make the player feel very uncomfortable. This makes winning high jackpots feel very rewarding. At the game's beginning, sound effects for wins are very unobtrusive and lightweight. The music is lighter, and bouncier, and more sensitive to wins. There are no sounds just for answering or playing because the assumption is you should be able to pass each question without trouble. It helps push players up and out of this section to move on to harder questions and bigger risks.

The surprise here is, why bother wasting time on questions you know people can answer? Shouldn't the whole game be intimidating? This section is included in the game to give it a fast start and a more dynamic introduction. You pull people in with these questions. You challenge them later on. Again, the real surprise here is that the soundtrack is actually more repetitive, less solemn, and less epic than the previous phase. You'd think the sound should get lonelier as you approach the top, but it doesn't. I believe this is, ironically, intended to push players closer to the $1,000,000 question faster by means of the anxious repetition, and from watching the show we know this is where many people screw up very quickly and drop back down to $32,000. The drama of the risk involved was soaked for all it was worth in the last phase, but now that the jackpot is so great, it would be much more interesting just to see someone just make it to that last question, and the faster, more repetitive music divides to accomplish both: it pushes the brightest to the top quicker while the lesser freefall sooner.



After the $1,000 mark, the music undergoes a dramatic change, getting much quieter, more ominous, and just selecting an answer now has a sound effect. Surprisingly, and I didn't realize this until after researching, every question's sound ascends keys in minor scale from $2,000 all the way up to $32,000. This is true for both the question's music and the final answer sound effect. You'd think the music should descend the scale, but the purpose of having higher sound as you progress is to indicate the rising jackpot, and the rising risks. The majority of the game is spent in this segment, squeezing the drama for all it's worth.



The $64,000 question marks another significant change as we go back to the bottom of the scale, this time with a different, more repetitive "heartbeat" soundtrack, interspersed with the ambient sitar for more atmospheric connotations. However, because the song is completely different, the change in scale is not as noticeable until the final answer sound effect at the end of the $64,000 mark. It is the same sound, same scale, same key as the $2,000 answer. Compared to the higher tone of the previous $32,000 question, this moment doesn't just mark the final phase as beginning, but as having already begun, and it catches everyone by surprise.


The final question is a doozy. It is the most repetitive, the most ominous, the most solemn, and of all the tracks heard by far it is definitely the darkest track of anything I've ever known. It's just a 3 second looping beat and a droning pad that never stops. It's easy to see why this choice was made as no other soundtrack could possibly be more conspicuous than a repetitive 3 second loop, but it flies in the face of game shows that try to make their larger prizes more like bonus rounds and fun, happy, crazy dances. This is not. This is the final question. There are no more questions after this. If you lose, you risk $468,000. If you win, you will be a millionaire.

Overall, the progression follows the show's intimidating and daunting experience, using key and soundtrack changes to push players through the easy questions, dramatize the difficult questions, and force them to fall quickly or climb to the top. The feedback is so subliminal, but very much intended by design as though it was a deliberate story. The complexity of Millionaire's sound design is something that many developers should learn from regarding how to use sound to set the drama of an interactive game into a story.

August 1, 2016

Rolling Displacement: Cheap AND Good-Looking Cloth for Console and PC

88 instructions - The number of instructions necessary to render beautiful silk cloth
75 instructions - Vertex shader cost of 3D cloth displacement

I have spent years trying to get APEX cloth to work on characters without needing NVIDIA's APEX integration with Maya or 3DS Max, but I cannot figure out how to authorize it, nor have I seen anyone else do it. In my open world project, Jake, the main character was always butt-naked. He was intended to have a red flowing cape. My folk's chargin with having a naked character and insistence that he wear something made me decide to try a different tactic: cloth displacement and normal calculation from textures. And surprisingly, the results from this method look even better than low-density APEX simulations.



The overall gist of this method is to offset sine and cosine waves flowing down the cloth by textures to flow in a unique pattern, transform from local space to world space and combine them to form the resulting vertex displacement, and using the sine data to drive a normal calculation. To the best of my knowledge, nobody has come up with a displacement/normal driven technique like this before. I'm calling this method "Rolling Cloth" due to the way the sine waves roll with the curvature of bends in the cloth rather than across them.

The benefits of this method are:
  • Extraordinarily cheap and detailed cloth displacement (great for console, mobile, and PC, assuming the API can handle texture displacement)
  • Rolling normals calculation looks great on low-poly cloth, even with no displacement at all
  • Highly scaleable to intended hardware
  • Normal calculation is fairly accurate
  • Artistically driven by textures for great flexibility in final results
  • Looks great with smaller textures (64-128 pixels)
  • Easily supports tiling cloth texture normals
The limitations of this method are:
  • Requires API to be capable of running textures to vertex displacement
  • Wind is manually driven by attributes in local space, not automatic in world space
    • Oddly enough, this is why the shader does not support triangle explosion through tessellation, because transforming from local to world space is incompatible with tessellation
  • The cloth is NOT simulated, so it will pass through world objects and characters
  • Movement rolls with bends in the fabric instead of across them

The whole material setup and finished result. Not too complicated, but looks great!
UVs on my cloth are set up so I can use a simple texture coordinate gradient as a mask and flow direction. In the mask, black areas will be swayed before white areas. The mask may require some fiddling to get right. For the trigonometry waves, I suggest the Face and Vertical waves are Sine and Cosine of the same period so you get a nice self-preserving circular motion. The negative of the first sine wave will just yield an unnatural linear displacement diagonally. The Lateral wave should be different than the first two swaying back and forth. If you want to prevent the cloth from looping, you can adjust the lateral motion or add more waves here at different periods.
The strengths of the Face/Lateral/Vertical transforms are purely visual. Eyeball it. If it looks right, it is right. The normals calculation, though, works the best when divided by the number of  transforms, and when it's scaled to their intensity. You can replace the 0,0,1 in the normals generator with a tiling normal map and it will work just as well. That "Normal of Mask" is actually a normal map of the grayscale mask. It's important the mask and normals are smooth, so don't be afraid to use a gaussian blur or low-res textures for them.
Multiply each wave times the mask, then add them together for the normals calculation. For the transform, multiply each transform by its corresponding Wave*Mask result, then simply add the results together. For this cloth, I wanted a silky cloth, so I used a 0.7 semi-metallic level and 0.6 roughness. You can mess with these numbers for different types of silk. Subsurface looks the best when I multiply the color by 0.2. This material is Two-sided, and using the Two-sided shading model. Cloth shading can yield nice cloth results as well. You can even use an opacity mask to show holes through the cloth.