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 :)