Jump to content

Finalizing Terrain


I'm wrapping up the terrain features now for the initial release. Here's a summary of terrain in Ultra Engine.

Terrains are an entity, just like anything else, and can be positioned, rotated, or scaled. Non-square terrains are supported, so you can create something with a 1024x512 or whatever resolution. (Power-of-two sizes are required.)

Editing Terrain

Ultra Engine includes an API that lets you modify terrain in real-time. I took something very complicated and distilled it down to a very simple API for you:

terrain->SetElevation(x, y, height);

That's it! The engine will automatically update normals, physics, raycasting, rendering, and other data for you behind the scenes any time you modify the terrain, in a manner that maximizes efficiency.

Materials

Leadwerks supports 32 terrain texture layers. Unity supports eight. Thanks to the flexibility of shaders in Vulkan, any terrain in Ultra can have up to a whopping 256 different materials applied to it, and it will always be rendered in a single pass at maximum speed.

To apply a material at any point on the terrain, you just call this method. The weight value lets you control how much influence the material as at that point, i.e. its alpha transparency:

terrain->SetMaterial(x, y, material, weight)

This example shows how to paint materials onto the terrain.

terrain_setmaterial.jpg

I'm quite happy with how the documentation system has turned out, and I feel it is representative of the quality I want your entire user experience to be.

Untitled.thumb.jpg.52f6008e39abcff77529a49b05be0cb3.jpg

All the API examples load media from the web, so there's no need to manually download any extra files. Copy and paste the code into any project, and it just works.

//Create base material
auto diffusemap = LoadTexture("https://raw.githubusercontent.com/Leadwerks/Documentation/master/Assets/Materials/Ground/river_small_rocks_diff_4k.dds");
auto normalmap = LoadTexture("https://raw.githubusercontent.com/Leadwerks/Documentation/master/Assets/Materials/Ground/river_small_rocks_nor_gl_4k.dds");
auto ground = CreateMaterial();
ground->SetTexture(diffusemap, TEXTURE_DIFFUSE);
ground->SetTexture(normalmap, TEXTURE_NORMAL);

Tessellation and Displacement

Tessellation works great with terrain, which is modeled with quads. There are a lot of high-quality free PBR materials that are great for VR available on the web, so you will have no shortage of interesting materials to paint your terrains with. Here are a few sites for you to peruse:

https://matlib.gpuopen.com/main/materials/all
https://icons8.com/l/3d-textures
https://ambientcg.com/
https://polyhaven.com/textures
https://freepbr.com/
https://www.cgbookcase.com/textures

Untitled.jpg.fc8712ac76404feb00502c2115de3b3b.thumb.jpg.ac9792ddd3ef5eb77c2b75d9e2f30bac.jpg

Cutting Holes

You can cut holes in the terrain by hiding any tile. This is useful for making caves, and can be used in the future for blending voxel geometry into a heightmap terrain.

Untitled.thumb.jpg.d1df97c009882ed961242c1b20e0aa49.jpg

Shader Design

Shaders in Ultra are big and quite complicated. I don't expect anyone to make 100% custom shaders like you could with the simpler shaders in Leadwerks. I've structured shaders so that the entry point shader defines a user function, then includes the base file, which calls the function:

#version 450
#extension GL_GOOGLE_include_directive : enable
#extension GL_ARB_separate_shader_objects : enable

#define LIGHTING_PBR
#define USERFUNCTION

#include "../Base/Materials.glsl"
#include "../Base/TextureArrays.glsl"

void UserFragment(inout vec4 color, inout vec3 emission, inout vec3 normal, inout float metalness, inout float roughness, inout float ambientocclusion, in vec3 position, in vec2 texcoords, in vec3 tangent, in vec3 bitangent, in Material material)
{
	// Custom code goes here...
}

#include "../Base/base_frag.glsl"

This organizes shaders so custom behavior can be added on top of the lighting and other systems, and paves the way for a future node-based shader designer.

Future Development

Ideas for future development to add to this system include voxel-based terrains for caves and overhangs, roads (using the decals system), large streaming terrains, and seamless blending of models into the terrain surface.

  • Like 5

12 Comments


Recommended Comments

Josh

Posted

3 hours ago, Thirsty Panther said:

Ooooh caves!

You could also take another terrain and turn it upside down to make the ceiling of a cave level.

  • Like 2
Cromartie

Posted

For me personally, tessellation is an integral part of level design.

Genebris

Posted

Since you have large shaders, do you have multi compiled shader variants like Unity does it?

Thirsty Panther

Posted

 

terrain->SetElevation(x, y, height);

Are the value of these in metres.? ie the distance from 1,0 to 2,0 is 1m.

 

 

klepto2

Posted

I think these coordinates are based on the terrain resolution. The real size and height later displayed on screen is based on the used scale factors u set with setscale. I would assume with a scale of 1 it could be interpreted as 1 m but this depends what you define in your app as 1m. 

  • Like 1
Josh

Posted

7 hours ago, Thirsty Panther said:

terrain->SetElevation(x, y, height);

Are the value of these in metres.? ie the distance from 1,0 to 2,0 is 1m.

By default, a terrain's size is (resolution.x, 1.0, resolution.y), so one terrain point occurs every meter, and the maximum height is 1.0. You can scale the terrain however you want to adjust the maximum height. So this would give you a terrain that is 512x512 meters, with a max height of 100 meters:

auto terrain = CreateTerrain(world, 512, 512);
terrain->SetScale(1,100,1);

When you call SetElevation(), the vertical scale of the terrain is taken into account, so the height you set is in meters. If you call SetHeight() instead, then the range is between 0.0 and 1.0.

I tried some other designs, but this is the only one I could consistently remember when working with it, so I think it is the most intuitive.

  • Like 1
Josh

Posted

11 hours ago, Genebris said:

Since you have large shaders, do you have multi compiled shader variants like Unity does it?

I don't know exactly what they're doing, but in Ultra a material uses a shader family instead of individual shaders. This is a JSON file that specified variation of the shader for all the different rendering options it may use:

https://www.ultraengine.com/community/blogs/entry/2443-shader-families/

  • Like 2
klepto2

Posted

I think the shader families comes close to that what unity does, but slightly different. Under the hood, there are different shaders compiled and linked for each configuration ( i guess only when needed of course) . In unity, you define this more or less directly in the shader and not in a separate file format. It is more like CG and HLSL style of shaders. 

I like the separation in UltraEngine, so shaders are shaders and nothing more. And with SPIRV under the hood you have a lot of more control flow in the shaders than in direct GLSL, just to name the #include directive as one example. 

Josh

Posted

One big change in Ultra is I use a lot of branching logic (if statements). I've seen absolutely no downsides to this on modern GPUs, and it lowers the total number of shaders used, which in turn means most of the scene is drawn in one or two super batches.

klepto2

Posted

If the branching is based on uniform data the performance impact is nearly zero. ( depends on gpu and driver) other branching has still a big impact on performance. Normally the shader will execute every possible branch for each processing unit ( vertex, fragment, etc.) and only keep the latest valid result. As the first this can be optimized by the gpu itself or the driver but can vary from gpu to gpu and vendor to vendor.

Josh

Posted

6 hours ago, klepto2 said:

Normally the shader will execute every possible branch for each processing unit ( vertex, fragment, etc.) and only keep the latest valid result.

I don't think that is true on today's hardware.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...