Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411507 Posts in 69374 Topics- by 58429 Members - Latest Member: Alternalo

April 26, 2024, 12:04:32 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsDesolus: A Surreal First Person Puzzle Game
Pages: 1 ... 21 22 [23] 24 25 26
Print
Author Topic: Desolus: A Surreal First Person Puzzle Game  (Read 109944 times)
Mark Mayers
Level 10
*****



View Profile WWW
« Reply #440 on: July 03, 2021, 02:16:38 PM »

That's so pretty. Looks like an ink blot spreading out.

 Toast Right
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
Mark Mayers
Level 10
*****



View Profile WWW
« Reply #441 on: July 03, 2021, 02:27:48 PM »

Update 165: 07/03/2021

SCREEN SPACE REFLECTION



Recently, I added Screen Space Reflections to Desolus.

In addition to reflecting the regular environment, I figured out how to create accurate reflections with portals.
(Notice that nice blue glow in the reflection!)




With SSR, a Depth/Normals buffer is used to calculate reflections.
However, to get reflections working with portals, I used the Depth/Normals buffer from each side of the portal and combined them together with a stencil mask.
This allows rays to travel 'through' a portal.

Additionally, I'm using the alpha value of the depth/normals buffer to store how reflective a material is.
This allows snow to be considerably more reflective than the architecture itself.





Snow actually feels frozen and reflective, and is no longer an entirely opaque.
You can see how much life this breathes into the scene in the before/after images.
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
oahda
Level 10
*****



View Profile
« Reply #442 on: July 04, 2021, 08:14:31 AM »

Nice! Looks really good. Kiss
Logged

Mark Mayers
Level 10
*****



View Profile WWW
« Reply #443 on: September 20, 2021, 11:39:55 AM »

Update 166: 09/20/2021

A CITY TO EXPLORE




Realized I haven't updated this in a while, here's a recent video of the city you explore in Desolus!
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
JobLeonard
Level 10
*****



View Profile
« Reply #444 on: September 21, 2021, 01:54:52 AM »

Aaaaah, a static shot without actual exploration, you tease!  Cheesy
Logged
Schrompf
Level 9
****

C++ professional, game dev sparetime


View Profile WWW
« Reply #445 on: September 24, 2021, 01:18:27 AM »

It's alive! Yeah! Still in love with these colours
Logged

Snake World, multiplayer worm eats stuff and grows DevLog
Mark Mayers
Level 10
*****



View Profile WWW
« Reply #446 on: October 20, 2021, 04:10:55 PM »

Update 167: 10/20/2021

CREATING A NEW TERRAIN SYSTEM FOR UNITY




Even since my early days with Unity, I have long desired to replace Unity's existing terrain system.

Anyone who has actually worked with Unity terrain probably knows how I feel. However, my issues with the system were getting to the point of impeding my overall progress in Desolus, and I needed to come up with a solution.

Here are a list of issues I have in particular, which I've been struggling with throughout development:

  • Level design iteration is pretty much impossible. You can't rotate terrain, and if you can you really only can in 90 degree angles. If you want to change a small terrain feature, you have to basically change the entire terrain. I could go on and on about why it's bad. To fix this issue, I ended up using a mesh-based terrain system, where I have 'terrain' prefabs scattered around which I can place, rotate, scale, etc. for level design purposes.
  • The actual terrain tools are not good, even with addons like Gaia. Brush strokes are ridiculously imprecise, so forget about using those. The stamp tool for Gaia is cool, but you can't undo easily down a change down the line if you need to make a level design revision.
  • The performance is abysmal. Most terrain adds thousands of draw calls, and millions of verts/tris. I understand they added batching to reduce draw-calls, but it won't work unless I fine tune the shader myself. I can write my own shader which is one draw call.
  • The visuals aren't great. Distance is handled with some CPU patching/tessellation algorithm, which rapidly shifts between patches. This creates poor transitions. Also the textures/normals get all screwed up between patches, which gives this super ugly jagged look. This can be solved with using GPU tessellation rather than whatever dated CPU solution Unity came up with in 2007 that still exists in the terrain.
  • The surface detail is too low for my purposes, and outside of using multiple terrains tiled together (thus exacerbating all of the issues above) it's locked to a 4096 resolution.  
  • For whatever reason, Unity terrain gets corrupted whenever you switch branches in git. It's been like this for years.

Because of this (non-exhaustive) list of issues I have with Unity terrain, I finally decided to develop my own system from scratch, with a time budget of a single work week.
In total, this ended up being almost exactly 40 hours of work (41 hours), done over the course of 12 calendar days.

My goals for the system were as follows:

  • The terrain system is generated entirely in Unity, with one button solution.
  • All textures are procedural, no painting or manual effort involved.
  • System is entirely GPU driven, and is simply a material + shader.
  • Terrain resolution is controlled by GPU tessellation, to allow for smooth resolution interpolation.
  • Uses static meshes instead of brushes for easier level design.
  • Performance! Only one draw call per terrain tile.
  • Erosion simulation, for hydraulic, thermal, and tidal erosion.

---

FOMER TERRAIN WORKFLOW: STATIC MESHES -> UNITY TERRAIN

My terrain workflow (up until now), to solve at least the level design issues described above, is to use static 'terrain like' meshes and then bake them into Unity terrain.

The problem with just having static meshes is that you're going to get super ugly geometry that is all kit-bashed together, as I described before. There's going to be a ton of overdraw. My solution to this was to sample all of the meshes into a Unity Terrain heightmap, by raycasting a ridiculous amount of times (like higher than the actual terrain resolution) and then averaging the raycast hit points into a heightmap. This works super well!

I formerly still used this tool, but I was at the mercy of the Unity terrain system itself, since that's what it's built on. Outside of the level design iteration issue being fixed, Unity terrain itself still has all of those other issues I discussed above.



Static meshes used to create terrain, left shows problems with overdraw.



Results from older terrain workflow, converting static meshes to terrain.

You can read all about this in my DevLog from a while back.

---

(post is continued below)
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
Mark Mayers
Level 10
*****



View Profile WWW
« Reply #447 on: October 20, 2021, 04:11:18 PM »

NEW TERRAIN SYSTEM

(continued from previous post)

NEW TERRAIN SYSTEM: STATIC MESHES -> CUSTOM TERRAIN



Fortunately, for converting static meshes to terrain, I was able to repurpose my previous raycasting system which I developed a while ago.

For conversion I previously was using the 'Object2Terrain' C# script, which is a free editor script.

This is great for a free tool, however this has some problems:
-The script only supports one mesh, and the static mesh terrain is composed of many meshes.
-It autosizes your terrain to the bounds of the mesh, meaning terrains never have the same resolution.
-It operates off of raycasts, which aren't great for quality conversion
-If you need to set additional parameters for your terrain, you have to do this manually.  

I'll go through how I fixed the major issues.

It Only Supports One Mesh:

The default script determines the dimensions of converted terrain with the mesh's bounds.
By default, this does not include children meshes and therefore only supports a single mesh.  
In order to support multiple meshes, you can encapsulate the bounds of children meshes into a single bounds object.



Consistent Terrain Resolution:

Unfortunately, Unity's terrains only support resolutions which are powers of two. (Ex. 256, 512, 1024).
The default script behavior would scale the terrain based off of the mesh bounds. However, resolution would always be a power of two.
This lead to an inconsistency in the quality of terrain, based on the difference between their resolution and size.

The solution to this problem was to fix the raycasting bounds based on powers of two, and set both the terrain's size and resolutions to the same value.
This can be determined with logarithms and base shifting.



Improving Raycast Quality:

To convert from meshes to terrain, the script relies off of raycasting.
This is an intuitive approach, however it creates precision errors which result in jaggedness of terrain, as you can see in the picture below.  

A solution to improving raycast conversion quality is through super sampling the raycasts.
By this, I simply mean make more raycasts than needed (such as a factor of 8x).
Afterwards, average adjacent raycasts together when creating the terrain's height map.

I was able to almost exactly use this script as it is, which uses raycasts to approximate a height map from a series of meshes.
However, the primary difference is instead of the data format being a 'Terrain Data' format, it's a multi-dimensional array of floats.

This floating point array (in the format of float[,]) is then baked into a heightmap texture.

---

NEW TERRAIN SYSTEM: GENERATING AND SERIALIZING HEIGHT MAPS



Baking the heightmap floating point data into a texture is actually fairly simple.
After creating an appropriate texture, you can iterate through all of its pixels (in x, y coordinates) and use 'Texture2D.SetPixel' to set the data for that specific pixel.

What wasn't so simple, is what format of texture to use, and how to serialize it.

Initially, I was naïve, and thought that a .PNG would be a sufficient format to store the data in, which could be encoded using this function.
However, I was very wrong. Your average .PNG file only has 8 bits of precision per channel, which means there are only 255 possible heights when storing a heightmap in one of the colors of the image.

You can clearly see these 255 heights in the below image of an earlier version of the terrain: notice the weird terraced slopes.


8 bits of height data with weird terraces, vs. 32 bits of height data which is nice and smooth

Naturally, I decided to use a 32 bit single channel texture to store the heightmap data instead. In Unity terms, this is called a 'TextureFormat.RFloat'

However, one issue of this format is that it can't be encoded in a common texture format to store on disk.
I tried looking into something like EXR, as Unity supports encoding textures into this format, but I found it impractical for my use.

Fortunately, I discovered that you can serialize texture data in binary.

Using the GetRawTextureData function, you can get a byte array from a texture, and the LoadRawTextureData loads data into a texture from a byte array.
Referring to some other documentation on how to write a byte array to disk, I was able to successfully load and save this texture data in an intuitive binary format.  

---

NEW TERRAIN SYSTEM: GENERATING NORMAL MAPS




Now that I had a high precision height map texture which I could save and load, the next step was to generate a normal map texture for the terrain.

The terrain height map is used to offset the vertices of our height plane, procedurally. However, these vertices have no normals.
One can compute normals procedurally in a shader, (say by sampling adjacent heights), however, this is a bit expensive and will result in many texture samples per-vertex.
One could also compute the normals using pixel derivatives in the fragment shader, but this will result in only flat normals and not smooth normals.

To create smooth normals, I instead opted to use a Sobel filter to pre-generate a normal map texture, based on our height map data.

The Sobel filter function allows for creating smooth normals using weighted derivatives in each direction.
I adapted an implementation from good old Stack Overflow into C# and Unity.

Essentially what this does, is given a height map coordinate, sample the eight adjacent coordinates to create a normal vector.
This is done for every pixel in the height map texture.

Pixel coordinates are as follows:
[n6][n7][n8]
[n3][n4][n5]
[n0][n1][n2]

float scale = NORMAL_STRENGTH; //Usually 64 is a good number for this
float nX = scale * -(n2 - n0 + 2 * (n5 - n3) + n8 - n6);
float nY = scale * -(n6 - n0 + 2 * (n7 - n1) + n8 - n2);
float nZ = Mathf.Sqrt((nX * nX + nY * nY));

//Clamp normal values and normalize
nX = Mathf.Max(0f, Mathf.Min(1f, nX));
nY = Mathf.Max(0f, Mathf.Min(1f, nY));
nZ = 1f - Mathf.Max(0f, Mathf.Min(1f, nZ));

Vector3 c = new Vector3(nY, nZ, nX);
c = Vector3.Normalize(c);

//NOTE: height is always the middle (g) component in Unity
Color normalColor = new Color(c.x, c.y, c.z, 1);

Something which definitely was tedious for me to figure out was the proper way to convert the results of the Sobel filter into an appropriate normal map color.
After loading the texture into a material and some debugging in the frame debugger, I discovered the correct orientation and values for generating correct normal maps.

The orientation was a bit challenging to figure out, however something also important is that we want the normals to be clamped to 0-1 values, and for the normal vector to be normalized (of course lol).

Also important, is the normal strength value, which determines the weighted strength of the red and blue channels of the normal map. Determining this value is more of an art than a science, and depends on the dimensions of the height map itself.

The code for the Sobel filter and color generation is included above for some lost soul who wanders into my DevLog.

---


NEW TERRAIN SYSTEM: TERRAIN HEIGHTMAP DATA AND THE SHADER



After generating the necessary maps needed for nice terrain, it was time to move on to working more on the actual terrain shader itself.
The concept is simple, given a height map, and a series of 2D plane meshes, offset the vertices of each plane based on the height map. However, there are a few important caveats which need to be considered.


BAKING MESH HEIGHTS FOR FRUSTUM CULLING

The first step for all of this, is that we want to bake the plane's base meshes with a low resolution height map.
We need correct mesh bounds so Unity's frustum culling won't break the terrain. This is so when we are looking at a terrain tile, it won't be culled by the camera at weird angles.

This is easy to do, all that is necessary is reading the height map texture we generated, and then offsetting each vertex's y value by our height map value. Then we use Mesh.RecalculateBounds on our resulting mesh.


BAKING MESH COLLISION DATA



For baking the mesh's collision data, I was able to use similar code to baking the basic height map data for the purpose of frustum culling.
However, we need significantly higher resolution for the player to walk on terrain tiles accurately.

I opted to sub-divide the base tile meshes by a factor of four, which gives reasonably high resolution for the terrain's collision.
Then, I use the exact same process for sampling the terrain's height map in world space, and offsetting the mesh vertices in C#.

The mesh collision data is currently serialized in the scene, but it's feasible to make it an asset instead for the future.


TESSELLATION OF THE TERRAIN'S PLANE MESH



The base mesh doesn't have enough resolution to render terrain, and needs to be tessellated.

Now that our basic height map is baked, we need to tessellate it into a higher resolution to actually render the terrain effectively. To control the terrain's resolution in game, I opted for using GPU tessellation. I chose to use the same code for tessellation as I do for my water shader.

You can find more about tessellation, and how to write it from scratch, in this tutorial over here!


SAMPLING IN WORLD SPACE: TILES

To actually sample the terrain, I decided that this should be done in world space rather than local space. This is because I wanted the terrain to be composed of mesh 'tiles' which form a larger shape.



The benefit of this is I wouldn't have to have a texture for every individual tile of terrain. For example, if there was an 8x8 grid of tiles, one would think there would have to be 64 different terrain textures which are split up from the main texture.

Fortunately, because the texture is sampled in world space, I only have to use one texture for all of the tiles.
Additionally, I can make the tiles whatever weird 2D shape that I want, and they will still work perfectly.

This would be beneficial in making sure only necessary areas of terrain are rendered, as to not waste computation.
In the future, I may write an occlusion culling algorithm for terrain which will automatically disable tiles which cannot be seen by the player.

To correctly sample in world space, I simply pre-compute the world space offset of the terrain from origin (0,0,0) and feed that into the shader.
When sampling the texture based off of the world space position of the vertex, I apply this offset.

Note that because we are sampling the texture in the vertex shader, we need to use the SAMPLE_TEXTURE2D_LOD function instead of SAMPLE_TEXTURE2D.

//Sample the texture based on the xz coordinates of the vertex in world space.
float2 samplePosition = position.xz;

//Subtract offset, and divide by resolution.
samplePosition.xy = samplePosition.xy - float2(offset.x,offset.z);
samplePosition /= _TextureResolution;
   
float4 heightMap = SAMPLE_TEXTURE2D_LOD(_Heightmap, sampler_Heightmap, samplePosition, float2(0,0));

//Sample the texture, and multiply by our height resolution
float height = heightMap.r * _HeightResolution;

This code results in correctly sampling the height map in world space, in our vertex function. The plane's vertices are then offset by the height value.


USING THE NORMAL MAP TEXTURE



What the terrain's normals look like, shown with a debug shader.

As discussed previously, because we are dynamically offsetting the height map's vertices, we would need to re-compute the mesh's normals to get correct rendering.
Otherwise, the mesh's normals would be incorrect, since without any offset, a flat plane's object space normal would always be (0,1,0).

Fortunately, we don't have to do this, because we pre-generated our normal map values ahead of time.
In the vertex function, we simply assign the object space and world space normal values, based off of our normal map.

---

NEW TERRAIN SYSTEM: EROSION






The final step to a good looking terrain height map, is an erosion simulation. I was aiming to simulate three types of erosion which I think could give cool results.

HYDRAULIC EROSION




Hydraulic Erosion simulates water droplets on terrain, and sediment being carried from terrain slopes to terrain valleys.

For a simulation of Hydraulic Erosion, I ended up using an algorithm which loosely referenced Sebastian Lague's research on the subject.
I had to write code which would work with my own terrain system, however the concept were pretty solid and very helpful in the overall implementation.


THERMAL EROSION



Thermal Erosion is the process of how heat changes affect terrain. Think about glaciers, or incredibly hot and dry areas of terrain.
Fortunately, I was able to adapt the thermal erosion code from Terrain Toolkit, which I have been using for years, into my own terrain system.


TIDAL EROSION



Tidal Erosion is the simulation of waves eroding sea-side terrain, to form interesting cliffs and beaches. Fortunately, I was again able to adapt the tidal erosion code from Terrain Toolkit, although it had to be changed slightly due to some data formatting issues.

---

FINAL RESULTS IN GAME



With all of the other game's assets included, such as the architecture, sky, trees, water, and procedurally generated snow, the world truly seems alive.

I'm very satisfied with the results of the terrain system. For about a week worth of work, it adds an immense amount to the game.

---
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
JobLeonard
Level 10
*****



View Profile
« Reply #448 on: October 21, 2021, 01:28:13 AM »

Damn this post is a treasure trove of information Coffee

EDIT: and the results look great too!
Logged
Mark Mayers
Level 10
*****



View Profile WWW
« Reply #449 on: October 21, 2021, 02:42:25 PM »

Damn this post is a treasure trove of information Coffee

EDIT: and the results look great too!

 Coffee
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
oldblood
Level 10
*****

...Not again.


View Profile
« Reply #450 on: October 22, 2021, 06:56:54 PM »

Wow. Incredible stuff all around, Mark.
Logged

Mark Mayers
Level 10
*****



View Profile WWW
« Reply #451 on: October 23, 2021, 01:01:48 AM »

Wow. Incredible stuff all around, Mark.

Thank you so much!
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
Mark Mayers
Level 10
*****



View Profile WWW
« Reply #452 on: October 26, 2021, 02:13:45 PM »

Update 168: 10/26/2021



Tomorrow I'm giving a virtual talk for Boston Unity Group! The talk takes place at 4pm PST, 7pm EST.
I'll be discussing how I've used Scriptable Render Pipeline to develop Desolus.
 
You can register here but the talk will be streamed live over Twitch.
I believe the talk will be uploaded to YouTube afterwards, I'll update this post with a direct link to the video for those who can't make it.

*Edit, the recorded talk!




---
« Last Edit: February 09, 2023, 08:14:29 PM by Mark Mayers » Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
JobLeonard
Level 10
*****



View Profile
« Reply #453 on: October 27, 2021, 04:02:53 AM »

Not a unity dev, so not really useful for me personally, but I can at least wish you good luck with the talk!  Coffee
Logged
Mark Mayers
Level 10
*****



View Profile WWW
« Reply #454 on: October 27, 2021, 11:59:27 PM »

Not a unity dev, so not really useful for me personally, but I can at least wish you good luck with the talk!  Coffee

Thanks friend, the talk went very well Smiley
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
oahda
Level 10
*****



View Profile
« Reply #455 on: October 28, 2021, 07:06:24 AM »

All that terrain stuff was really cool to read. I keep getting baffled at how much stuff you're doing for this game.
Logged

Mark Mayers
Level 10
*****



View Profile WWW
« Reply #456 on: February 18, 2022, 02:49:29 PM »

Update 169: 02/18/2022











Some new screenshots and vids to share

---
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
JobLeonard
Level 10
*****



View Profile
« Reply #457 on: February 18, 2022, 08:20:40 PM »

Always nice to see that the project is alive Smiley
Logged
Mark Mayers
Level 10
*****



View Profile WWW
« Reply #458 on: February 18, 2022, 09:48:20 PM »

Always nice to see that the project is alive Smiley

Haha it's alive as long as I am
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
Mark Mayers
Level 10
*****



View Profile WWW
« Reply #459 on: February 18, 2022, 09:49:43 PM »

Always nice to see that the project is alive Smiley

Haha it's alive as long as I am

I don't mean that in a morbid way, I'm going to release this thing LOL
Logged

Desolus Twitter: @DesolusDev Website: http://www.desolus.com DevLog: On TIG!
Pages: 1 ... 21 22 [23] 24 25 26
Print
Jump to:  

Theme orange-lt created by panic