Material layers?

I have a ground mesh created from BABYLON.Mesh.CreateGroundFromHeightMap and I would like to layer multiple materials on top of each other. My initial thought was to use the MultiMaterial however that looks like its just for using materials side by side on the same mesh, maybe I’m wrong and they could overlap?

Two of the materials use custom shaders. One for the terrain detail like the worldmonger example, and another for a grid something like the GridMaterial extension. I was also going to use a DynamicTexture to draw on a canvas that output onto the same ground on top or in between the two other layers.

I can’t seem to get these layered on top of each other.
Thanks!

Hi again, AF. I once saw Tehran-based BJS super-hero @nasimiasl … use his “shaderBuilder” library… to do a two-texture “blending” on a ground plane.

https://www.babylonjs-playground.com/#1IAR36#17

Don’t ask me how it is done, but it sure is cool. Might apply to your needs.

Aside: ShaderBuilder is a shader-code “concatenator”… allowing shader code to be assembled on-the-fly from little pieces (as opposed to big fat chunks of pre-made shader code). It allows you to code only the small variables/parameters that matter, and the shaderBuilder fills-in the gaps. This allows a shader-experimenter… to quickly change one or two values, then re-run, without having to deal-with big fat shader-code chunks.

In the old days, we would do ground.clone() and set the clone.material to wireframe = true. Position it .001 higher Y than the primary ground. :slight_smile: But, that’s for grid-overlay purposes ONLY, and perhaps not the most modern way to do grid overlays.

There’s one more way that I can bring-to-mind, too. A BJS decal. Decals have been known to allow textures to be pasted atop bumpy terrains… following the contours. THEN… you could use ANY grid resolution, as the delineations would be pre-painted onto whatever texture you use for the decal. Note: Decals cannot be repositoned after they are created, because they are sort-of “grown” (to match contour of mesh beneath).

Multiple decals on a single mesh == a-okay.

Yes, a decal for the grid was my original thought and I was having issues positioning it on the mesh and then I was concerned about it being a clone of the mesh and the size of the texture and decided to look else where.

I saw the shaderbuilder but to be honest I could not find a single example that worked, most of the playgrounds posted by @nasimiasl don’t work as the additional included script is hosted on a site that no longer works. It seemed the way to go but wasn’t sure if it was the right solution or a solution that was overly powerful(lol) as specific shader code was combined instead of materials being overlayed it seemed. I might be wrong though I’ll look further

yes :slight_smile: i am sorry about that
but it is https and github sdn change url
if you notic any example i fix it for show and explain details

Seems I found a much less complicated solution :slight_smile: https://www.babylonjs-playground.com/#2Q4S2S#17 the multi material example with overlapping indexes seems to work

Actually, NOPE. Doesn’t seem to work on ground meshes… whats up with that? https://www.babylonjs-playground.com/#2Q4S2S#19

Hi.
Take a look at this or this

1 Like

@MarianG That seems to be different then what I’m looking for. The first one mixes textures into 1 material, and the second uses 1 material with rendering images etc to the context of the single material. I’m trying to combine two materials on a mesh created from the height map

if you need have shadow and lights you most use custommaterial
but the shaderbuilder option always can make them but as any shader material you most write all of them
i try make sample for both for you
it can help if you have any detail about what you wanna do

2 Likes

Hi AF!

https://www.babylonjs-playground.com/#2Q4S2S#21

This uses the applyDisplacementMap method… to create a heightMap. It uses a createGround() instead of a createGroundFromHeightMap(). Seems ok. :slight_smile: It allows your cool overlapped multi-mat trick, apparently.

2 Likes

@nasimiasl thanks for the offer to help! It seems what @Wingnut did works for what I’m looking for. Just the ability to layer different material’s over the top of each other.

Feel free to mark the correct answer :slight_smile:

1 Like

@Wingnut Sorry I finally got to testing it, wanted to do that before I accepted… it’s pretty close but there seems to be some oddities by doing it this way.

Have any solutions for these issues…

  1. Apply displacement map seems to flip the image upside down?

    Take a look at this original, using the CreateGroundFromHeightMap function
    https://www.babylonjs-playground.com/#2Q4S2S#24
    Now the same code (and camera angle etc) using the applyDisplacementMap function
    https://www.babylonjs-playground.com/#2Q4S2S#25
    Initially this could be resolved by just flipping the image in photoshop, but strange that two similar functions wouldn’t just do the same thing?

  2. Flickering grid? Looks like a bug chewed through my screen(grid)! :wink:

    https://www.babylonjs-playground.com/#2Q4S2S#23
    this demo is super smooth, what I’m looking for…
    https://www.babylonjs-playground.com/#1UFGZH#12
    The bug screen even looks worse in my project.

:sob: why won’t it just do what I want it to do, and perfectly!! :laughing: lol…

So this leads me to messing around with other things, like CreateGroundFromHeightMap(…).clone() doesn’t work… so I went ahead and just created the same ground multiple times from CreateGroundFromHeightMap() (seems like a lot of wasted resources) and I put a different material on each ‘ground’ so it looks like it’s layered materials… and I got to thinking about this solution vs submeshes… Are submeshes clones of the original mesh? Are submeshes more efficient rendering in some way vs just creating multiple ground layers from the same height map (other then the cost to recreate from the image)?

(Edit: also I should note that doing the CreateGroundFromHeightMap() multiple times I have to add an offset to each mesh of y+=0.1 to eliminate the flickering that is similar to what happened with the MultiMaterial example.)

Thanks!

(edit fixed links)

this is alternative option
https://www.babylonjs-playground.com/#2Q4S2S#26

if you wanna attach any more texture you can read this CustomMaterial Samples

3 Likes

@nasimiasl awesome, I’ll check it out.

Hi AF. Cool experiments!

#1: As far inverting… ONCE I needed to “hack” the TWO primary functions used for ground.applyDisplacementMap().

https://www.babylonjs-playground.com/#2Q4S2S#20

CAUTION! ground.applyDisplacementMap() and applyDisplacementMapFromBuffer() are NON-DEFAULT in that playground. They are already customized/modified. Default versions are…

        /**
         * Modifies the mesh geometry according to a displacement map.
         * A displacement map is a colored image. Each pixel color value (actually a gradient computed from red, green, blue values) will give the displacement to apply to each mesh vertex.
         * The mesh must be set as updatable. Its internal geometry is directly modified, no new buffer are allocated.
         * @param url is a string, the URL from the image file is to be downloaded.
         * @param minHeight is the lower limit of the displacement.
         * @param maxHeight is the upper limit of the displacement.
         * @param onSuccess is an optional Javascript function to be called just after the mesh is modified. It is passed the modified mesh and must return nothing.
         * @param uvOffset is an optional vector2 used to offset UV.
         * @param uvScale is an optional vector2 used to scale UV.
         * @param forceUpdate defines whether or not to force an update of the generated buffers. This is useful to apply on a deserialized model for instance.
         * @returns the Mesh.
         */
        Mesh.prototype.applyDisplacementMap = function (url, minHeight, maxHeight, onSuccess, uvOffset, uvScale, forceUpdate) {
            var _this = this;
            if (forceUpdate === void 0) { forceUpdate = false; }
            var scene = this.getScene();
            var onload = function (img) {
                // Getting height map data
                var canvas = document.createElement("canvas");
                var context = canvas.getContext("2d");
                var heightMapWidth = img.width;
                var heightMapHeight = img.height;
                canvas.width = heightMapWidth;
                canvas.height = heightMapHeight;
                context.drawImage(img, 0, 0);
                // Create VertexData from map data
                //Cast is due to wrong definition in lib.d.ts from ts 1.3 - https://github.com/Microsoft/TypeScript/issues/949
                var buffer = context.getImageData(0, 0, heightMapWidth, heightMapHeight).data;
                _this.applyDisplacementMapFromBuffer(buffer, heightMapWidth, heightMapHeight, minHeight, maxHeight, uvOffset, uvScale, forceUpdate);
                //execute success callback, if set
                if (onSuccess) {
                    onSuccess(_this);
                }
            };
            BABYLON.Tools.LoadImage(url, onload, function () { }, scene.offlineProvider);
            return this;
        };
        /**
         * Modifies the mesh geometry according to a displacementMap buffer.
         * A displacement map is a colored image. Each pixel color value (actually a gradient computed from red, green, blue values) will give the displacement to apply to each mesh vertex.
         * The mesh must be set as updatable. Its internal geometry is directly modified, no new buffer are allocated.
         * @param buffer is a `Uint8Array` buffer containing series of `Uint8` lower than 255, the red, green, blue and alpha values of each successive pixel.
         * @param heightMapWidth is the width of the buffer image.
         * @param heightMapHeight is the height of the buffer image.
         * @param minHeight is the lower limit of the displacement.
         * @param maxHeight is the upper limit of the displacement.
         * @param onSuccess is an optional Javascript function to be called just after the mesh is modified. It is passed the modified mesh and must return nothing.
         * @param uvOffset is an optional vector2 used to offset UV.
         * @param uvScale is an optional vector2 used to scale UV.
         * @param forceUpdate defines whether or not to force an update of the generated buffers. This is useful to apply on a deserialized model for instance.
         * @returns the Mesh.
         */
        Mesh.prototype.applyDisplacementMapFromBuffer = function (buffer, heightMapWidth, heightMapHeight, minHeight, maxHeight, uvOffset, uvScale, forceUpdate) {
            if (forceUpdate === void 0) { forceUpdate = false; }
            if (!this.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)
                || !this.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)
                || !this.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
                BABYLON.Tools.Warn("Cannot call applyDisplacementMap: Given mesh is not complete. Position, Normal or UV are missing");
                return this;
            }
            var positions = this.getVerticesData(BABYLON.VertexBuffer.PositionKind, true, true);
            var normals = this.getVerticesData(BABYLON.VertexBuffer.NormalKind);
            var uvs = this.getVerticesData(BABYLON.VertexBuffer.UVKind);
            var position = BABYLON.Vector3.Zero();
            var normal = BABYLON.Vector3.Zero();
            var uv = BABYLON.Vector2.Zero();
            uvOffset = uvOffset || BABYLON.Vector2.Zero();
            uvScale = uvScale || new BABYLON.Vector2(1, 1);
            for (var index = 0; index < positions.length; index += 3) {
                BABYLON.Vector3.FromArrayToRef(positions, index, position);
                BABYLON.Vector3.FromArrayToRef(normals, index, normal);
                BABYLON.Vector2.FromArrayToRef(uvs, (index / 3) * 2, uv);
                // Compute height
                var u = ((Math.abs(uv.x * uvScale.x + uvOffset.x) * heightMapWidth) % heightMapWidth) | 0;
                var v = ((Math.abs(uv.y * uvScale.y + uvOffset.y) * heightMapHeight) % heightMapHeight) | 0;
                var pos = (u + v * heightMapWidth) * 4;
                var r = buffer[pos] / 255.0;
                var g = buffer[pos + 1] / 255.0;
                var b = buffer[pos + 2] / 255.0;
                var gradient = r * 0.3 + g * 0.59 + b * 0.11;
                normal.normalize();
                normal.scaleInPlace(minHeight + (maxHeight - minHeight) * gradient);
                position = position.add(normal);
                position.toArray(positions, index);
            }
            BABYLON.VertexData.ComputeNormals(positions, this.getIndices(), normals);
            if (forceUpdate) {
                this.setVerticesData(BABYLON.VertexBuffer.PositionKind, positions);
                this.setVerticesData(BABYLON.VertexBuffer.NormalKind, normals);
            }
            else {
                this.updateVerticesData(BABYLON.VertexBuffer.PositionKind, positions);
                this.updateVerticesData(BABYLON.VertexBuffer.NormalKind, normals);
            }
            return this;
        };

(found via document search in https://raw.githubusercontent.com/BabylonJS/Babylon.js/master/dist/preview%20release/babylon.max.js)

In the playground… between lines 54 and 66, we are working in “context 2d land”. Let’s visit the transformations section of the W3C context2d spec. HTML Canvas 2D Context

context scale, rotate, translate… all available. I have a nopped-out rotate() in line 62 of above PG. It’s something to goof-around with. There might be easier solves. Let’s listen for more/better comments.

#2: I’m not sure why you have “z-fighting”-like problems with that. Using subMaterials with overLapping vert startAt and endAt values… is something BRAND NEW to me… and is “precarious” and possibly “dangerous” (subject-to unpredictability). Some kind of texture “summing” is being used, and I noticed that all the squirrels and other small animals… ran away and hid… when you started using that method. heh.

I will think on it, and texture experts… are nearby. Perhaps they will comment.

some new grid material
https://www.babylonjs-playground.com/#02ZUE0#9

1 Like