Jump to content

How big of a terrain to balance performance + appearance on low-end machines?

Recommended Posts

OK, I had an opportunity to make a playground:  http://www.babylonjs-playground.com/ts.html#IIFQWH#2

This demonstrates three of the problems I've run into:

1) Can't navigate uphill at all.

2) Framerate is terrible when moving.

3) Calling GroundMesh.optimize( 64 ) makes the terrain disappear completely.

Prior to starting I thought I saw at least one thread on here about movement on terrain, but now I can't find that.  Everything I can find in the way of how-tos is about moving on perfectly flat surfaces.  If anyone knows the thread I'm referring to, or another reference for first-person movement on arbitrary meshes, that would be really helpful.  Shoulda bookmarked it! The stuff I'm finding here on the forum is mainly about being knocked back by collisions, which is also an issue I've run into, but it's way farther down the list. :) 


Link to post
Share on other sites

60FPS here. :) Your subdivisions was much too high for a terrain of 512 * 512.


Try to display your land in wireframe mode to see your subdivisions.

Putting 200 in subdivision for a terrain of 512 * 512 is more than enough. (You can push at 220 can be, but too much subdivision (when using collisions in the field) lower the FPS.

Now an Octree usually fixes this problem, but there is a bug in Chrome with Octree I think, because the terrain should not disappear. Unless it's a bug in Babylon. Deltakosh will tell us more.

I add to your code :


This brings up the camera for slides better on the ground and to improve the collision with the ground.

When a collider is too deep in the ground, it tends to drop the PFS


Link to post
Share on other sites

These are some good leads. :)  Thanks!

10 hours ago, Dad72 said:

60FPS here. :) Your subdivisions was much too high for a terrain of 512 * 512.


Try to display your land in wireframe mode to see your subdivisions.

Putting 200 in subdivision for a terrain of 512 * 512 is more than enough. (You can push at 220 can be, but too much subdivision (when using collisions in the field) lower the FPS.

Have I possibly misunderstood what a "subdivision" is?  I thought it pretty much referred to the polygon density.  I.e. if you set 100 subdivisions, you get a mesh that is 100x100 quads (or 100x100x2 triangles).  So if you do 200 subdivisions on a 512x512 heightmap, aren't you losing a lot of the terrain precision?

I did as you suggested and flipped to wireframe,  though I had to drop subdivisions to 64 in order to be able to stand walking along the edge and counting polys. :)  That did seem to reduce the mesh to 64 quads (128 tris) per side.

Given that's the case, what would be the advantage of designing a 512x512 heightmap and using 200 subdivisions versus a just designing a 200x200 heightmap and keeping it 1:1?

With my main codebase, I am using a 2048x2048 heightmap with 2048 subdivisions and as I reported a couple of days ago, it's suddenly working much better, though I can't figure out a specific change that's responsible for that.

10 hours ago, Dad72 said:

I add to your code :


This brings up the camera for slides better on the ground and to improve the collision with the ground.

When a collider is too deep in the ground, it tends to drop the PFS

I copied this into my code and I'll give it a shot.

I noticed that you decreased the X & Z size of the camera ellipsoid as well.  I had it higher because in an earlier test with a mesh building it seemed if you ran up to a wall, part of it could get in front of the viewing frustum therefore wouldn't be drawn.  But I used your settings with the building just now and did not observe that issue.  I'll leave it as you have for now and keep an eye out for problems.

2 hours ago, Deltakosh said:

3/ The optimize as to be called AFTER the ground is ready . Did you call it in the onReady callback?


No, I definitely did not call optimize from the onReady callback!  I've made that change to my main codebase, and it works fine.  (Whether it has any effect, positive or negative, I can't say because really it runs very well either way now.  Clearly that's due to Babylon.JS and not me, since it's fairly obvious I'm stumbling through this in relative ignorance.  :) )

If I understand optimize() correctly, it is subdividing the one large mesh into smaller ones.  Once that's done, is there a way to combine that with the auto-LOD stuff that @dbawel previously mentioned to further reduce the impact of distant parts of visible terrain.  (The dynamic terrains stuff mentioned by @JohnK addresses did work for me, though it tends to cut off mountains and such that I'd like to keep visible from farther away if possible.) 

Right now, though, everything is fine, so I can focus on the controls.

Further searching pointed me at the Esplit tutorial, which makes it look easy to do the one thing that so far completely eludes me: climb a flight of stairs. 🙂  Is the source for that available?  I tracked it down on Github but the "demo.js" I found is only 16 lines and looks like some kind of stub.  Not sure where to go from there.

This thread was the one I found earlier, so I'll work my way through the references given if I get time tonight, though many of them involve physics, which I was hoping to steer clear of, given this is a very basic walking simulator.  I'm not going to have bouncing items or guns or anything like that.  But I'll do what I have to to make it work. :)  Unfortunately I've already learned some of the stuff in that thread that sounds especially helpful, like @satguru's example, are now 404.  (The Playground really is a work of genius.)

In some other threads, I've seen @Deltakosh recommend reducing the scene.gravity.  If I drop it to -1, the player cannot move at all.  At -2 or any higher value, it all seems more or less the same.

It seems like what's happening is that the scene gravity is putting such a crushing weight on the player that they are immediately and swiftly pushed to the lowest point of any polygon with a non-zero slope, regardless of how small the slope is.

Which is my best bet, trying to track down the Esplit source or similar, or just biting the bullet (or possibly the Cannon) and just embrace the physics side?

Thanks to everyone for their continued help and good advice with this!  My progress may be slow, but it's steady and I'm pretty sure without you all I'd be nowhere. :)


Link to post
Share on other sites
On 9/27/2018 at 12:25 PM, Deltakosh said:

But I'm still convince that with less gravity and bigger camera.speed you could get something :)

Seems like (not surprisingly) you were right about that.  Although a value of -1 for scene.gravity prevents all movement, and -2 prevents all climbing (like the original -9.81 value), I tried some more intermediate values between -1 and -2 while trying to make an updated PG and they did work much better.

For now, I've settled on -1.1.  It seems to allow climbing some reasonable slopes, but not walking up cliff spaces or blatantly soaring into space.  And the limited downward sliding that occurs also seems reasonable.  This should work for what I need right now. :)

The latest PG is here: http://www.babylonjs-playground.com/ts.html#IIFQWH#8

My nexts steps will be trying to properly texture the terrain; the placeholder colormap I'm currently using just won't do, so it's time for me to learn about splat maps.  But I'll see how I do with that and if (when?) I run into trouble, you all will be the first to know. :)

Thanks again for all the help!

Link to post
Share on other sites
17 hours ago, Dad72 said:

@jdavidI use a value of -0.75 which gives a good result.

-9.81 is for the physics engine. For collisions without physics, I use a value between -1.0 a 0

You have something better here :  58 a 60 FPS


Did you make other changes than reducing the subdivisions?  (Is there a way to see a diff between different versions of a Playground?  I looked and Google'd and didn't find one.)

My "real" project uses a 2048x2048 heightmap with 2048 subdivisions and for some reason I don't understand it handily outperforms my playground version based on the 512x512 heightmap.  So right now I don't have any real reason to reduce subdivisions in the real project, since it performs well and I want the most detailed terrain I can get.

But I would sure like to understand the why it performs well and the playground version doesn't.

(One possibility is that I am using Babylon.JS 3.2 on the large project, and the Playground is using 3.3, so if 3.3 has the problem you mentioned, perhaps that is why the playground runs slower.)

Since I have speed set to 0.5, I think that means scene.gravity.y needs to be < -0.5 to keep the player from simply taking off and flying away?  I will play around with some values for -1 < scene.gravity.y < -0.5 and see if they work better for me than 1.1. 

It does look like I should take another look at why a setting of -1 renders the player unable to move in my "home" version, because the playground version doesn't seem to have that behavior.


Link to post
Share on other sites
  • 2 weeks later...

Hi all,

I am still working on this, though life continually interferes. 😕

Everything continues to run very smoothly, with the single significant exception that my experiments with WaterMaterial are having really brutal effects on the frame rate, cutting it from a very smooth 56-60 down to a very choppy 30 if (and only if) I have it reflect my ridiculously huge terrain.  (It's choppy enough to make me question how accurate the 30 is.)  There are also some weird graphical glitches.  It's not super clear if I've just hit the limits of the hardware, or if I've made some mistakes along the way.  It looks pretty great when it's working, so if it's possible to make it work well with the current terrain, I'd really like to do that.

For the past couple of days, I've been working on refactoring my code to make it easier to switch components on and off, and to make it easier to round-trip to a Playground.  That's almost done -- it's how I figured out that the specific combination of the terrain and the water was causing the problem.  Hopefully I'll be able to post an updated Playground in the next few days.

In the mean time, I wanted to update this thread so people don't think I just wandered off after all their help. 🙂

Here is a short extract of my current terrain code:

class Island implements ILoadable {

	// dytGround: BABYLON.DynamicTerrain
	gmsGround: BABYLON.GroundMesh
	scn:	   BABYLON.Scene
	tmtGround: BABYLON.TerrainMaterial

	constructor( i_scn : BABYLON.Scene ) {

		this.scn = i_scn

		this.tmtGround = new BABYLON.TerrainMaterial( "matGround", this.scn );
		this.tmtGround.specularColor = new BABYLON.Color3( 0, 0, 0 )

		this.gmsGround = BABYLON.MeshBuilder.CreateGroundFromHeightMap( "ground", "data/full_heightmap.png", {
			width: 2048,
			height: 2048,
			subdivisions: 256,
			minHeight: -20, //-56,
			maxHeight: 364, // 39,
			onReady: () => {
				this.gmsGround.optimize( 16 )
			updatable: false
		}, this.scn )
		this.gmsGround.checkCollisions = true
		this.gmsGround.material = this.tmtGround


	onLoaded( i_ldr: Loader ) : void {

		this.tmtGround.mixTexture = i_ldr.fetchTexture( "splatmap" )

		this.tmtGround.bumpTexture1 = i_ldr.fetchTexture( "beach_bump" )
		this.tmtGround.bumpTexture2 = i_ldr.fetchTexture( "grass_bump" )
		this.tmtGround.bumpTexture3 = i_ldr.fetchTexture( "rock_bump" )

		this.tmtGround.diffuseTexture1 = i_ldr.fetchTexture( "beach" )
		this.tmtGround.diffuseTexture2 = i_ldr.fetchTexture( "grass" )
		this.tmtGround.diffuseTexture3 = i_ldr.fetchTexture( "rock" )

		this.tmtGround.diffuseTexture1.uScale = this.tmtGround.diffuseTexture1.vScale = 32
		this.tmtGround.diffuseTexture2.uScale = this.tmtGround.diffuseTexture2.vScale = 32
		this.tmtGround.diffuseTexture3.uScale = this.tmtGround.diffuseTexture3.vScale = 32

		this.tmtGround.bumpTexture1.uScale = this.tmtGround.bumpTexture1.vScale = 32
		this.tmtGround.bumpTexture2.uScale = this.tmtGround.bumpTexture2.vScale = 32
		this.tmtGround.bumpTexture3.uScale = this.tmtGround.bumpTexture3.vScale = 32



and water code:

class Sea implements ILoadable {

	gmsSea:    BABYLON.GroundMesh
	matSea:    BABYLON.WaterMaterial
	scn:       BABYLON.Scene

	constructor( i_scn: BABYLON.Scene ) {

		this.scn = i_scn

		this.matSea = new BABYLON.WaterMaterial( "matSea", this.scn, new BABYLON.Vector2( 1024, 1024 ) )
		this.matSea.backFaceCulling = true

		this.gmsSea = BABYLON.MeshBuilder.CreateGround( "sea", {
			width: 2048,
			height: 2048,
			subdivisions: 128,
			updatable: false
		}, this.scn ) as BABYLON.GroundMesh
		// this.gmsSea.checkCollisions = true

		this.matSea.windForce = -5
		this.matSea.waveHeight = 0.5
		this.matSea.bumpHeight = 0.1
		this.matSea.waveLength = 0.1
		this.matSea.colorBlendFactor = 0

		this.gmsSea.material = this.matSea


	onLoaded( i_ldr: Loader ) : void {
		this.matSea.bumpTexture = i_ldr.fetchTexture( "water_bump" )

	reflect( i_msh: BABYLON.Mesh ) : void {
		this.matSea.addToRenderList( i_msh )


(In case any obvious mistakes jump out.  Assuming they don't, I'll post the Playground as soon as it's available.)

Edit: Oh, the code looked quite a bit better & more readable in the preview, with white-on-black and syntax highlighting.  If there's a way to keep that, I'll fix it.  Otherwise, it's probably for me best to wait until I have a working playground.


Link to post
Share on other sites

OK, I made that change; it added a few FPS according to the meter, but having the water in view still renders the entire system choppy and hard to use.  I'll really try to get this into a playground soon.

This may also be helpful, I'm adding a screen grab showing the distortion.  There are a couple of different issues.

First, the water seems to be reflecting at the wrong height (visible farther away as the water reflecting more of the island than is above the surface.  This is probably not that serious.

Second, the reflection calculates correctly most of the time, for both the skybox and the terrain, but below a certain point, the reflection just seems to copy the pixels from the line above.  The texture from the terrain underneath appears to show through correctly, so it's just affecting the reflection.  This seems to happen primarily very close to the shoreline.

I'm not sure if there's any relationship between the distortion and the performance problem, but it seems worth mentioning.



Link to post
Share on other sites

The current skybox size is 2048.  Is that a good size?

After stripping out and refactoring as much as I could, the playground should be: http://www.babylonjs-playground.com/ts.html#RTY6WT#2

Hopefully this code is relatively easy to follow.

Issue #1: Poor FPS with reflections

With both ground mesh and skybox reflections enabled, I'm getting about 26-28fps in the playground.

Commenting out the skybox reflection only (line 54) doesn't seem to change this.  (Nor does removing it completely.)

Commenting out only the ground mesh reflection (line 55) but leaving the skybox reflection gets 44-46fps.

Commenting out both reflections (lines 54 & 55) gets a solid 60fps.

Issue #2: Distorted reflections

The distorted/repeated reflections I'm seeing should be visible at the starting point, depending on your screen size.  If not, go close to the edge of the water, face the distant island, and pan the camera up and down a bit.

(As of yet I have no idea if these two issues are connected or coincidental.)


Link to post
Share on other sites

At home the FPS are at 60 stable on your scene.

The deformation of this product according to the orientation of the camera : See:

Camera down, it's perfect


Camera looking up, and deforming this product :



Then I realized that it is not the reflection or refraction that is not going well, it is the camera that produces its artifacts.

Watch his pictures:

1128732887_2018-10-1613_25_29-Greenshot.thumb.jpg.6c1c3c20c83294b3e16720d4120d0e9d.jpg 1911179093_2018-10-1613_25_40-Greenshot.thumb.jpg.e8c466799965f9929b1b09955e45fe2e.jpg

 This covers the entire width of the screen by a small band that create deformations.

this.camFPS.minZ = 0;  has reduced the height of the band, but still remains Then it makes me think that the camera is not big enough in height:

So I change its height: 

this.camFPS.ellipsoid = new BABYLON.Vector3( 0.5, 2.0, 0.5 )
Everything is perfect on this PG :


See :



Link to post
Share on other sites
17 hours ago, Dad72 said:

this.camFPS.minZ = 0;  has reduced the height of the band, but still remains Then it makes me think that the camera is not big enough in height:

So I change its height: 

this.camFPS.ellipsoid = new BABYLON.Vector3( 0.5, 2.0, 0.5 )

After I manually diff'd all your changes, I was able to dramatically reduce the amount of distortion, thanks!  I did not make the ellipsoid change because a 4m meter player is problematic for other reasons. 🙂 

The distortion is still there (regardless of camera height), but it's much smaller and you kind of have to be looking for it.  The factor that seems to have the largest effect on it seems to be the line you added to kill the wind:

this.matSea.windDirection = new BABYLON.Vector2(0, 0)

That makes a huge difference, though with no wind the result appears perfectly still; more like a mirror than water.  I'll keep fiddling.

As far as the FPS, may I ask what hardware you're using to get 60fps?  I did have the opportunity to try my app on a GTX 1080 and it also managed 60fps but it definitely felt like hiding my multitude of efficiency sins under a mountain of overpowered hardware.  I'm developing this on much more modest hardware, and I haven't even got to vegetation yet.

If the WaterTerrain is simply too ambitious, is there a less demanding approach to water that would work better on lower-end hardware, yet still look better than a flat blue plane? 🙂




Link to post
Share on other sites
8 hours ago, Dad72 said:

My PC is running with a GTX 1050 TI

For the wind, you can maybe try to put 0.3 to 0.5 in Z and leave 0 in X.

For the ellipsoid, 2 is a height of 2m . You can try 1.8m0 to make a character height, as you did at 0.75, so that the character is 75cm, which is very small.

With the ellipsoid set to a height of 2, the camera towers over structures with a height of 3, so I concluded from that it  must be a radius rather than a diameter.  Hence I set it to 0.75 to get an overall player height of about 1.5m, which has seemed to work up until now.

If it is a diameter (and I'm sure you're right about that... you would know!) why is the setting of 2 allowing the camera to easily look over the top of objects with a height of 3?


Link to post
Share on other sites

You have reason. I just checked it on my project and I set my ellipsoid at 0.875 which matches the height of my characters by 1.75 units.

It seems to me that by putting the camera higher on your PG the artifacts disappeared, but it may be due to some other modification that I had done before in your PG.

My elipoid is here for the size of a character new BABYLON.Vector3(0.3, 0.875, 0.125); (soit 60 cm de large, 1.75 m de haut et 25 cm de profondeur)

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   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.

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Create New...