Converting ThreeJS shader to Babylon

This is my first time touching shaders, and I am trying to convert this GLSL shader to be used in Babylon: https://codesandbox.io/p/sandbox/laughing-jackson-eml2nj?file=%2Fvertex.glsl%3A34%2C17-34%2C24

ThreeJS version:

uniform float u_effectBlend;
uniform float u_remap;
uniform float u_normalize;

varying vec2 v_uvs;

float inverseLerp(float v, float minValue, float maxValue) {
  return (v - minValue) / (maxValue - minValue);
}

float remap(float v, float prevMin, float prevMax, float newMin, float newMax) {
  float t = inverseLerp(v, prevMin, prevMax);
  return mix(newMin, newMax, t);
}

void main() {
  v_uvs = uv;

  vec2 vertexOffset = vec2(
    remap(uv.x, 0.0, 1.0, -u_remap, 1.0),
    remap(uv.y, 0.0, 1.0, -u_remap, 1.0)
  );

  vertexOffset *= vec2(-1.0, 1.0);

  if (u_remap == 1.0) {
    vertexOffset = mix(vertexOffset, normalize(vertexOffset), u_normalize);
  }

  vec4 worldViewPosition = modelViewMatrix * vec4(position, 1.0);

  worldViewPosition += vec4(mix(vec3(0.0), vec3(vertexOffset, 1.0), u_effectBlend), 0.0);

  gl_Position = projectionMatrix * worldViewPosition;
}

It looks fairly simple, but I’m not sure I’m getting all the equivalencies right.

Here’s my code so far:

attribute vec3 position;
attribute vec2 uv;

uniform mat4 world;
uniform mat4 view;
uniform float u_effectBlend;
uniform float u_remap;
uniform float u_normalize;
uniform mat4 projection;

varying vec2 vUV;

float inverseLerp(float v, float minValue, float maxValue) {
  return (v - minValue) / (maxValue - minValue);
}

float remap(float v, float inMin, float inMax, float outMin, float outMax) {
  float t = inverseLerp(v, inMin, inMax);
  return mix(outMin, outMax, t);
}

void main() {
  vec2 vertexOffset = vec2(
    remap(uv.x, 0.0, 1.0, -u_remap, 1.0),
    remap(uv.y, 0.0, 1.0, -u_remap, 1.0)
  );
  vertexOffset *= vec2(-1.0, 1.0);
  if (u_remap == 1.0) {
    vertexOffset = mix(vertexOffset, normalize(vertexOffset), u_normalize);
  }
  vec4 worldViewPosition = world * view * vec4(position, 1.0);
  worldViewPosition += vec4(mix(vec3(0.0), vec3(vertexOffset, 1.0), u_effectBlend), 0.0);
  vUV = uv;
  gl_Position = projection * worldViewPosition;
}

and

const shaderMaterial = new ShaderMaterial(
      'shader',
      scene,
      { vertex: 'custom', fragment: 'custom' },
      {
        attributes: ['position', 'uv'],
        uniforms: ['world', 'view', 'projection'],
        uniformBuffers: undefined,
        shaderLanguage: ShaderLanguage.GLSL,
      },
    );

    // Assign the shader material to the mesh
    tree.material = shaderMaterial;

    // Set initial uniform values
    shaderMaterial.setFloat('u_remap', 1.0);
    shaderMaterial.setFloat('u_normalize', 1.0);
    shaderMaterial.setFloat('u_effectBlend', 1.0);
    shaderMaterial.setMatrix('view', scene.getViewMatrix());

    const foliage = tree.getChildMeshes().find((mesh) => mesh.name === 'foliage');
    if (!foliage) {
      return;
    }
    foliage.material = shaderMaterial;
    shaderMaterial.setMatrix('world', foliage.getWorldMatrix());

As you can see, the vertex shaders have the following differences:

modelViewMatrix has been replaced with world * view.
projectionMatrix has been replaced with projection.

However, the shader isn’t working the same way as it does in ThreeJS.

Here’s the ThreeJS version:

And here is mine:

In mine, the mesh as a whole seems to rotate instead of the individual quads.

Any ideas would be greatly appreciated!

I think this should be view * world (as we want the world to transform the vertices first)

3 Likes

this was it, thank you!

I’m curious if you have any ideas why I am able to see the branches through the Babylon version, but not the ThreeJS version. I can’t really tell whether the branches are sticking through or if the quads are having some weird transparent behavior or what.

Setting the depth function to “always” makes the branches not visible, but then I can see the leaves on the opposite side of the tree. But this makes me think it is something to do with depth.

Disabling depth write on the trunk seems to achieve the desired result.