So I have an open world map that I’m adding a bunch of grass to, and all seems well except one issue-
When the grass is close to the camera it looks really weird. You can see this in the screen shot below:
However, in this playground the issue does not seem to happen:
Lastly, I had question about baking wind-blowing animations into the grass - I was thinking I would have a physics sim play out in blender and just record it, but does anyone have any tips or resources for turning that into an animation?
BTW, this open world map looks very cool now! Is the high mountains accessible for the character?
Sure is! Part of my journey has been figuring out ways to make this work in the browser within reasonable parameters (not using too much ram, etc.) such that it works on most peoples laptops. I’ll be posting it in the demos topic later. I’ll probably use it for the GUI competition
Did you change the UV scale for the grass texture in the open world map? You can find the texture in BJS inspector and check.
No, everything I did was pretty standard - the editing that I did do was split this grass model out from a larger gltf file that had other grass models from a pack I found on sketchfab. I then re-centered the model on itself, but I can’t imagine any of that being the issue.
You could make a snapshot of a frame with Spector and see if you can find something strange in it. We could also have a look if you are able to provide a link to a live repro.
Woah, nice tool, I’ll try that out. I’ll also try to get this up live, I was only just starting to play with adding grass to my map; I’ll move quickly to just have it added to the areas around the base of each tree using that same algo I used in the playground. The live demo itself is here: https://g7r7sz.colyseus.dev/client/index.html
(frame rate is awful for some reason compared to my localhost version)
@Evgeni_Popov Unfortunately I won’t be able to work on this till Tuesday probably. I realized I need to use some precomputed results regarding the placement of each grass instance, and doing that right will take a little more time.
@Evgeni_Popov btw, I had another tangential question regarding thin instances I was hoping to ask you-
if I have 140k grass model thin instances but I want to reduce the burden on the gpu, if I were to set the scaling to zero for all thin instances not located in the players current grid region on the map (I have it divided up in to 20x20 grid regions) would this stop the gpu from computing for those instances? And if not, would it at least reduce by some noticeable amount?
Yes, it would reduce the burden a bit because the triangles with vertex scaling = 0 would have no area so the fragment shader will never be executed for them. You could reduce the burden even further by making sure those triangles have 0 area at the geometry level: you can do this by copying the 2nd index into the 3rd of the index array for each triangle that should be excluded from rasterization.
For eg, to remove the first triangle of the ground plane:
[…] Ah, by re-reading your question you want to do this for thin instances… So, this latter technic won’t work because you have a single instance of the mesh. If you remove a triangle this way it will be removed for all instances.
So I tried out turning off the grass thin instances via setting scaling to zero, and turning those thin instances back on again via the reverse operation of setting their scaling back to it’s original value. Unfortunately it takes a really long time to do so.
To solve this issue of wanting to have 1400 - 1600 instances be shown only when the player is the corresponding grid region, I was thinking maybe I could make a copy from the grass source mesh for each grid on the map. I would then create thin instances that pertain to a given region and have the visibility of that grid’s source mesh toggled on and off. Would I still be getting the performance savings by doing it this way?
The alternative, that I’m trying right now, would be the same ^ but use regular instanced meshes instead of thin instances.
Do either of these sound like they would work? Are there any better ways to go about this?
This strategy looks good to me as long as your grid cell is big enough (if the source mesh has only 5 thin instances it’s probably not interesting), as your grid is already your visibility unit.
I think it’s a perf saving if you track which grid cells become visible/disappear when you move the camera instead of scanning all the grid cells to update their visibility status. In the former strategy, you will update the status of only a fraction of the grid cells, whereas in the latter you will update all the grid cells. Though it probably warrants some testing as just looping over all the cells is easier than tracking which cells become visible/invisible…
Using regular instanced meshes is probably not the right strategy here as you would need to create tens (or more) thousands of them and the system won’t be able to handle so many meshes.
Each grid’s source mesh for the grass would be responsible for ~ 1400 - 1600 thin instances. So if this strategy works, it would definitely show.
I have a system that checks whether the users position has crossed any grid boundaries, and if so freezes the previous grid and unfreezes the new current grid; so I’m only ever iterating over two grids worth of objects / instances (~ (1400 - 1600) x 2). I was considering adding some logic that also operates on the surrounding grids as well, but I’ll save that for later.
I don’t really understand this error message, as I’ve already tested out loading all grass across the entire map at once, so why would loading 1/(20 x 20) of that become a space allocation issue? This is the the method that I use to clone the grass mesh per grid:
_loadGrid(row: row, col: col) {
/* grid[row][col] = return value of getGridResourceTemplate */
const grid = this._eeResourcesGrid[row][col]
console.log('loadGrid: ', grid)
for (const [_eeName, eeResource] of Object.entries(grid)) {
const matrices = eeResource['matrices']
const sourceMesh = eeResource['sourceMesh']
const thinInstanceBuffer = createThinInstances(
sourceMesh,
matrices,
)
eeResource['thinInstanceBuffer'] = thinInstanceBuffer
this._setGridLoaded(row, col, true)
}
}
export const createThinInstances = (
sourceMesh: Mesh | AbstractMesh,
matricies: Matrix[],
) => {
const matricesData = new Float32Array(matricies.length * 16)
let mat: Matrix
for (let i = 0; i < matricies.length; i++) {
mat = matricies[i]
mat.copyToArray(matricesData, i * 16)
}
sourceMesh.parent = null
sourceMesh.position.setAll(0)
sourceMesh.scaling.setAll(1)
sourceMesh.rotation.setAll(0)
sourceMesh.rotationQuaternion = new Quaternion()
//@ts-ignore
sourceMesh.thinInstanceSetBuffer(
"matrix", matricesData, 16
)
return matricesData
}
I’m wondering if when I create these per grid region clones of the grass mesh, I’m doing it in such a way that babylon.js thinks each one shares all the same thin instances (across all grids)?
Okay yeah that was it. I’m launching this on the live demo link now so that you can see how the grass behaves.
Just know that you’ll need to give the game like 15 seconds after the you see the character load in, for some reason loading the audio first kills the frame rate.
Shoot so sorry, I’ve not yet created a checklist behind each deployment where changes to the environment are being made. There’s a plain json file that holds the blueprints for the world map’s trees, rocks, grass etc. My installer simply checks if the file exists atm, but not if you have the newest version downloaded; working on a fix for that.