Tri plannar mapping in three.js

I am trying to do texture mapping with triplanner mapping in three.js in terrain created after heightmap data into plane mesh.

To do that i load the diffuse texture by

 let diffuseTex;
  loader.load(
    "./resources/images/satellite.png",
    function (image) {
      diffuseTex = image;
    },
    undefined,
    function () {
      console.error("An error happened.");
    }
  );

I did use that texture created from the diffusemap image that i have locally as an uniform

var uniforms1 = {
    diffuseTexture: { type: "t", value: diffuseTex },
  };

  const _meshMaterial = new THREE.RawShaderMaterial({
    uniforms: uniforms1,
    vertexShader: terrainShader._VS,
    fragmentShader: terrainShader._FS,
  });

My shader code is:

const _VS = `#version 300 es
precision highp float;

uniform mat4 modelMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

in vec3 position;
in vec3 normal;

out vec3 vNormal;
out vec3 vPosition;

#define saturate(a) clamp( a, 0.0, 1.0 )

void main(){
    vNormal = normal;
    vPosition = position.xyz;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    vPosition = vec3(gl_Position.xyz)*0.5 + 0.5;
    
  }
`;

const _FS = `#version 300 es

precision mediump sampler2DArray;
precision highp float;
precision highp int;

uniform mat4 modelMatrix;
uniform mat4 modelViewMatrix;
uniform vec3 cameraPosition;
uniform sampler2D diffuseMap;

in vec3 vNormal;
in vec3 vPosition;

out vec4 out_FragColor;

vec3 blendNormal(vec3 normal){
    vec3 blending = abs(normal);
    blending = normalize(max(blending, 0.00001));
    blending /= vec3(blending.x + blending.y + blending.z);
    return blending;
}

vec3 triplanarMapping (sampler2D tex, vec3 normal, vec3 position) {
  vec3 normalBlend = blendNormal(normal);
  vec3 xColor = texture(tex, position.yz).rgb;
  vec3 yColor = texture(tex, position.xz).rgb;
  vec3 zColor = texture(tex, position.xy).rgb;
  return (xColor * normalBlend.x + yColor * normalBlend.y + zColor * normalBlend.z);
}

void main(){
    vec3 color = triplanarMapping(diffuseMap, vNormal, vPosition);
    out_FragColor = vec4(color, 1.0);

  }
`;
export const terrainShader = { _VS, _FS };

Then i create a mesh with the geometry which is filled with the vertices data and material created with custom shader:

let geometry = new THREE.PlaneGeometry(
      partial.x,
      partial.y,
      partial.x - 1,
      partial.y - 1
    );
    geometry.setFromPoints(chunk[i]);
    geometry.computeVertexNormals();
    let mesh = new THREE.Mesh(geometry, _meshMaterial);
    mesh.castShadow = false;
    mesh.receiveShadow = true;

    scene.add(mesh);

I don’t know what i am doing wrong because i am getting this as output of terrain:

I was expecting mapping of this texture above this terrain.

texture to map:

image

I am new to this and struggling can anyone please help me ?

If I remember correctly, simondev had a tutorial about this on his YouTube channel. It shouldn’t be hard to find.

Its WebGL 2.0 ?

Here you go. Links to source should be posted in the description. It’s just a bit of custom shaders.

On a side note, I highly recommend you watch most, if not all, of his videos. Even if you don’t know your way around shaders that well, he explains everything really well to make things easy to understand. :grin:

Thank you so much, i wrote my code based on it and yes in this video he explain his way of doing triplanar mapping and triplanner splatting. My code is just his code that i wrote my way.

Can i ask one question, is that position that i am sending to the triplanarMapping() for sampling the texture correct ?

I am passing

gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
vPosition = vec3(gl_Position.xyz)*0.5 + 0.5;

and i am sampling the texture with the vPosition passed through that

vec3 xColor = texture(tex, position.yz).rgb;

I am multiplying by 0.5 and added 0.5 because i thought that position is in range of -1 and 1 so i wanted it to map to range of 0 and 1