USW projects

Unreal Engine is a perfect toy to have some quality hobby time :slight_smile:
Figured I’d start posting some small projects with hints that might be helpful to the community.

1. Turn in place using distance curves

Hints:

First you need to see these talks:

Also, as I was a complete animation newbie before I started this toy project, I have got a lot of information from “Animation Blueprint Setup & Walkthrough” live training series with Jay Hosfelt. Here is the link for the first video: Animation Blueprint Setup & Walkthrough | Live Training | Unreal Engine Livestream - YouTube

Make actor follow control rotation. Basically, you back out rotation using ‘Rotate Root Bone’ node and use distance curve to look up angle and to scrub animation.

Some more notes:
You would want to do control in C++, blueprint spaghetti would be too much. Also, I see now why Epic is not releasing Paragon nodes, they’re most likely too custom to be made generic anim nodes.
If you can, have only a two step turn animation for 90 degrees. For 180 have 2 step most of the arc and then tiny step in the end. My animations are 3 and 4 step respectively and foot sliding on small turn angles is unavoidable. With 2 step animations you can have feet perfectly planted with no sliding.
If you use motion builder, put DistanceCurve on root bone as integer custom parameter and do ‘save as’ for your fbx - for some reason if you ‘export motion file’ it wipes out custom parameters.
When accessing animation curves, use EvaluateCurveData method on UAnimSequence instead of accessing raw curve data. When packaged, raw data becomes not accessible due to compression.

2. Stop location prediction

You don’t really need iterative method to predict stop location.
If ground friction is zero, solving simple kinematic equation is enough: d = (Vf^2 - Vi^2) / 2a where Vf - final velocity - BRAKE_TO_STOP_VELOCITY, Vi - initial velocity, a= - braking deceleration
With ground friction, you need to integrate V/a(V)dV where a(V) = V*Fr + BD where Fr - ground friction, BD - braking deceleration

Note that internally Epic uses BRAKE_TO_STOP_VELOCITY on character movement component so use this as final velocity and not zero. Otherwise location prediction will be a bit off.

Sample code:



float Friction = GetCharacterMovement()->GroundFriction * GetCharacterMovement()->BrakingFrictionFactor;
float BrakingDeceleration = GetCharacterMovement()->BrakingDecelerationWalking;
FVector Velocity = GetVelocity();
StopLocation = GetActorLocation();

if (Friction == 0.f)
{
    // No friction - simple kinematic equation
    float Distance = (Velocity.SizeSquared() - UCharacterMovementComponent::BRAKE_TO_STOP_VELOCITY * UCharacterMovementComponent::BRAKE_TO_STOP_VELOCITY) / (2.f * BrakingDeceleration);
    StopLocation += Velocity.GetSafeNormal() * Distance;
}
else
{
    // Integrate
    float Distance = BrakingDeceleration * FMath::Loge(BrakingDeceleration + UCharacterMovementComponent::BRAKE_TO_STOP_VELOCITY * Friction) / (Friction * Friction) - (UCharacterMovementComponent::BRAKE_TO_STOP_VELOCITY / Friction);
    Distance -= BrakingDeceleration * FMath::Loge(BrakingDeceleration + Velocity.Size() * Friction) / (Friction * Friction) - (Velocity.Size() / Friction);
    StopLocation += Velocity.GetSafeNormal() * Distance;
}


1 Like

3. Locomotion

So in typical fashion what starts as a weekend exercise ends up being year long on and off project. But finally: curve based locomotion start, stop prediction, curve based turn in place, orientation warping with procedural lean, curve based jump start, soft and hard landing, feet IK.
Was very interesting and satisfying to figure out. There is no speed or slope warping. Maybe some day.

1 Like

huge work keap up

4. Fast tri-planar mapping

While building my landscape material by deconstructing wonderful work from Joe Garth (Brushify), tri-planar mapping got me thinking. Common approach is to use distance blends for global-macro-detail maps for example. But for slope blending this causes a bit of performance hit since you need to do tri-planar mapping. One can look up tri-planar mapping implementation in WorldAlignedTexture node and see that it uses 3 texture samplers. So having 3 blends each using 3 samplers and having all this for normals as well makes things not ideal. This is why I figured brushify auto material uses only top mapping and guards full tri-planar under switch.

There is a way to do tri-planar mapping having only one texture sampler. The only downside is that there are no blends/lerps, so seams are visible. But for slope application this is not really an issue due to usual type of the textures (rock/cliff) which hide seams pretty well. Especially at a distance.

See below 3 screens, I disabled normal mapping and noise to check how bad seams would look.

Fast tri-planar implementation, checker map

Fast tri-planar

True tri-planar, 3 samplers with lerps

As you can see results are pretty good and seams are not really noticeable. Also see the difference in shader complexity. Of course I shouldn’t compare shade of green in editor viewport and just do performance profile for real numbers. But this comparison will do for now. Left part: fast tri-planar mapping is used for global and macro texture blends, regular tri-planar is used for detail blend. Right part: all three blends use regular tri-planar mapping.

Below is the fast tri-planar implementation.

You can see blending in action. Global/macro - fast tri-planar, detail - regular tri-planar.

2 Likes

i like it ---- u remember me with this one

Yup, I used this one as reference.

1 Like

Grass

So the new forum broke the images in previous posts and I see no way to edit them. No matter, moving on to the grass. I have an aging rig with 1070 and arbitrary decided that I want to try and fit my grass into 10ms tops on 2k resolution. Honestly, I was surprised with results. UE4 has come long ways…

I decided to use assets and material from Project Nature mainly because they have nice implementation for wind and interaction using Pivot Painter 2. Not only this looks very natural but also extremely performant. I also suggest watching Epic “From Ants To Outer Space” video (From Ants To Outer Space | Inside Unreal - YouTube) for foliage advice. I took foliage sinking trick from there as well as generic insights since I had no experience with foliage before.

For grass I used landscape procedural grass node. In project settings I set ‘Early Z-pass’ to ‘Opaque and masked meshes’ and switched on ‘Mask material only in early Z-pass’

I used 3 varieties for grass. Most dense set has the shortest cull distance, with density decreasing for others. Two closest sets use mesh sinking by setting cull start-stop values, last set has hard cull line. Here it is 9000. Along with this I set up grass LODs. I used 4 here and set the last LOD for 2% reduction at 0.01 screen size. Now balancing the above (cull distances on grass type vs lods) took quite some time, but the most performance gain is there. So it is important to find that sweet spot when grass looks good, does not ‘pop’, has good coverage and there is no significant overdraw. See sample screens below.

For material I used Project Nature one for wind/bending implementation but added fuzzy shader because I like its effect better. Material is very performant:


However, I still created simplified version of it without any world position offsets or subsurface effects and assigned to last 2 lods of my grass meshes. This shaved around 2-3ms.
I also reimplemented foliage-actor interaction (bending), but not because of performance in shader but rather for different use model.

In my project I use full dynamic lighting. Currently foliage generated with procedural landscape grass node does not support DFAO or DF shadows so for ‘grounding’ we must use cascaded dynamic shadows. Here I have distance of 6000 and 4 cascades. I tweaked exponent and transition fraction here to achieve best looking result for shadow transition. Unfortunately it is still very noticeable, but still surprisingly better than I anticipated.

Procedural grass here also uses 6 more varieties for flowers. But the last 2 islands of flowers are hand-painted. Those are usually clustered and need manual control anyway.

So in the end, I managed to get 5-10ms for my grass. This engine never ceases to amaze.

For actor interaction my implementation uses wobble marker that is trailing the actor with some drag and has delay on wobble power decay. It is a bit better than to look at actor speed as we want foliage to wobble a bit after actor has already stopped.

2 Likes

Sun

A small hint for those who are implementing time-of-day. Having sun at zenith right above your head is not ideal for artistic purposes as all normal maps wash out, so we want to add some latitude. Sounds simple as one would add some north correction to X, set Z to latitude and start sweeping Y. Unfortunately transform suffers gimbal lock and you can’t just do setActorRotation with rotator from Euler angles. Instead you need to use quaternions. Sample code below.

float angle;
if (ProgradeRotation)
	angle = CurrentTime / (DayLength * 3600.f) * 360.f + 90.f;
else
	angle = -CurrentTime / (DayLength * 3600.f) * 360.f + 90.f;
FQuat SunRotation = FQuat(FRotator(angle, NorthDirection, 0.f)) * FQuat(FRotator(0.f, -Latitude, 0.f));
Sun->SetActorRotation(SunRotation);

Video:

While messing around and moving sun around I realized another thing. Don’t derive specular from roughness or do any fancy logic with them. Ditch the magic numbers if you have them. Keep specular at 0.5 or just unplug it. For roughness have simple scaler for maps or min/max and lerp if you use red channel of the albedo. The reason is that it is better to stay as close to PBR as possible. I have made my scene look nice with some magic number settings but when I changed afternoon to late evening the scene looked pretty bad. When you find yourself going inside the texture settings to compensate for something you did to specular… means you’re in trouble.

Sparkling snow

Spent quite some time on this and I think might be useful for some.
I tried to look up other solutions and even read one paper but came to conclusion that here we have to limit what we fake and be pretty close to real thing. If we completely fake (using PP or emissive etc.) then deepest shadows still sparkle even during the night so completely unusable for open world full dynamic light setups. On the other hand, adding a lot of detail noise to normal map does not give even remotely ok result either. Due to filtering and TemporalAA sparks can be seen only close-up. And surface looks like crap due to extreme noise.
I decided to actually make ‘crystals’ on my snow surface by aligning select pixels on the normal map so sun light is reflected directly into the camera. Here is the result:

Below is the screen of the material function:

‘Sparks mask / roughness’ - this creates sparks mask. I create a grid of black squares with increasing size the farther away from camera. At the same time I sample a noise texture to mask and leave out random pattern of the squares. Camera movement and rotation contribute to this pseudo-randomness. Noise texture is a bunch of sparsely placed white pixels. No mipmaps, filtering is set to ‘Nearest’.

‘Sparks normal’ - this uses sun direction stored in material parameter collection, and camera vector. These two point from the pixel to the sun and to the camera. So in order to reflect light directly into the camera normal must be sum of these vectors divided by 2. This normal is interpolated into snow normal using above sparks mask.

Sparks mask is multiplied with roughness maps/lerps to have 0’s for ‘crystals’.

Up top this gets masked by landscape layer and so on.

3 Likes

Okay, I take my comment regarding specular back. Landscape is too shiny. Even with roughness set to 1 and very rough normal map. Under direct light in the distance surface becomes too reflective, especially on glancing angles. Looks like UE just does not have enough per pixel texture precision for far away pixels. So we need to do a correction. I like how Quixel does it, get albedo and then use it in lerp from min to max specular. Similar to roughness when you don’t use explicit textures.

Quixel

Speaking of Quixel, take a look at their MedievalGameEnvironment project - it is like a treasure chest of techniques and solutions. I have only 16Gb of RAM and could not open it. If you have similar problem, go to the Config/DefaultEngine.ini and change starting map to something light and manageable, like Forge for example.

[/Script/EngineSettings.GameMapsSettings]
EditorStartupMap=/Game/Maps/Forge.Forge

Then you can start exploring all their materials, functions, etc.

RVT

Runtime virtual texture for landscape is pretty cool approach but unfortunately it suffers from tile pop, especially if you have some distance blending in your setup. I could not figure out how to control that. So unless Epic will allow us to force particular tile mips close to camera using RVT sampling for landscape is not practical.

RVT does save 2ms in my very primitive setup. This acts like a top down projection so there potentially could be some stretching on high slopes. But I feel that ditching auto slope and tri-planar is actually not an issue - I prefer to build slopes with cliff geometry anyway since it will look better in the end. You can’t have tessellation with RVT and number of maps is limited. Note that specular/roughness are stored as masks, you can’t pass color information through them. So my attempt to use specular as subsurface failed. In the end you have to apply them regularly which diminishes RVT performance gain. Finally, RVT is camera agnostic so all the CameraDistance logic and such stops working. You do have a workaround, but it is lacking in some areas, especially considering that far mips can pop near the camera. See below sample on how to do distance based blends for RVT.

But while RVT is not practical for sampling in landscape at the moment, it has wonderful use if you still output it. Blending mesh textures with landscape. It is much better approach as all other that include distance fields, TemporalAA dithering, vertex painting, etc. You keep DFAO and shadows and don’t have to do anything special with your mesh.

For this setup I made all my logic in landscape layer blending RVT friendly. Mesh samples RVT base and RVT height, masks and blends accordingly. For normal blending convert landscape normal to worldspace before writing to RVT. In mesh material uncheck tangent normals. I also copied Quixel approach of adding a bit of noise into the blend, looks much better.

Height blend

Another useful technique I borrowed from Quixel materials is height blend. It gives much better results on landscape layer blends. Both weight blended and custom ones like snow. For regular layers you need to output height information. For that just use displacement map. You can pass it around like Quixel does using Opacity pin. See below example of snow layer blended using regular alpha-blend approach:

Now see the same blend using height information:

Here is material function:

Of course UE5 may make all above obsolete but it is a real fun to play around with these. Poking RVT was frustrating at times and it segfaulted more times than I remember but it still amazes me what Epic developers have accomplished with this engine over the years.

Landscape blending will do wonders on your scattered debris.
You can get assets and textures from Quixel, author those with minimal involvement, have a landscape material with 3 weight blended layers, 2 alpha blended and a RVT on top. Cover this with foliage. And it runs under 18ms on gtx 1070. Magic.

Hey !

I came across this post while learning UE and DM. I was wondering if you had time for two questions:

  1. How did you derive the second equation when there’s friction? Any reference?
  2. Regarding the integral, did you mean something like this? Integrate[Divide[v,v*f+b],v] or Integrate[Divide[v,v*f+b],v] - Wolfram|Alpha

If so, I’m seeing a slightly different solution: v/f - (b log(b + f v))/f^2 + constant. (Not sure what constant is supposed to be).

Thanks and sorry for the noob questions!

Hey ,

Happy holidays!
Just wanted to shoot a message to say how wonderfully satisfying these videos are. What a fantastic way to show off your skills and give a bit of guidance as well. Its very much appreciated.

Personally can’t thank you enough for sharing your expertise and passion with the unreal community and teaching a bit along the way.

I hope to see more of your skills and sparkly snow around the forums soon. :grin:

1 Like

Hi @anonymous_user_0e16593a!
For equation with friction: Epic uses friction factor and braking deceleration constant, just from these two it is reasonable to assume that a(v)=v*fr+bd
But to be honest I just noticed that use model in someone’s code sample.
As for the second question, I think I missed a sign. -a(v) should be considered. I don’t think I can edit the post anymore. If you’d consider -a then your F will be correct. And constant is irrelevant since we compute definite integral and it will cancel out.

1 Like

Cool, thanks! :smiley:

UE5 is awesome. Most of the above is now obsolete. However, it was a good foundation.
Now we can have million tris generated snow, no LODs, this is just wonderful. Subsurface scattering seems to work now with Lumen. Can’t wait for 5.1 to try Nanite with foliage and trees.



VSMs are great. PSA: currently there seems to be a bug, with any landscape material you may find that cached page is always red. The reason I found is that generally we do “Use Material Attributes” for such and it seems that UE thinks that WPO is used even if it is not. Workaround is to not use the flag and just build material attributes regularly.