Different Materials on Plane side A and side B

Hey,
I’m pretty new to threejs and have been googling my way through but can’t seem to find a solution to my seemingly easy problem:

How can I add a material A to side A of a plane and material B to side B of the same plane?

At the moment I get a plane which is (depending on my settings THREE.DoubleSide/FrontSide/BackSide) either transparent from side B or has the same material on both sides. I created a plane in Blender, exported the file as GLTF from threejs/editor and imported it via GLTF-Loader like this:

assetLoader.load(
  monkeyUrl.href,
  function (gltf) {
    const model = gltf.scene;

    //SIDE A MATERIAL
    const frontCard = model.getObjectByName("Front_Card");
    const materialFrontCardFront = new THREE.MeshPhongMaterial({
      side: THREE.FrontSide,
    });
    frontCard.material.color.setHex(0xff0000);
    frontCard.material = materialFrontCardFront;
   


    //SIDE B MATERIAL
    const materialFrontCardBack = new THREE.MeshPhongMaterial({
      side: THREE.BackSide,
    });
    frontCard.material.color.setHex(0xbb0000);
 frontCard.material = materialFrontCardBack;

    scene.add(model);
  },
  undefined,
  function (error) {
    console.error(error);
  }
);

Thanks for the help!

1 Like

three docs examples might have something. Otherwise poke around in Unity forms and also use the search term cg shader
https://en.wikibooks.org/wiki/Cg_Programming/Unity/Two-Sided_Surfaces

Youll be doing the same routine if you need to write a custom two pass shader

Base idea if I recall is, if you can get the function to know which direction the normal of the face is pointing you swap which texture it needs to render.

There are many approaches (with a second pair of triangles, with a twin-plane, with a flat box, with a custom shader…)

Here is the case with a flat box:

var plane = new THREE.Mesh(
       new THREE.BoxGeometry( 3, 2, 0 ),
       [  null,  null, null, null,
          new THREE.MeshLambertMaterial( {color: 'crimson'}),
          new THREE.MeshLambertMaterial( {color: 'yellow'}),
      ]
  );	
1 Like

I vote for this :slight_smile:

import {mergeGeometries} from "three/addons/utils/BufferGeometryUtils.js";
...
let plane = new THREE.Mesh(
  mergeGeometries(
    [
      new THREE.PlaneGeometry(3, 2),
      new THREE.PlaneGeometry(3, 2).rotateY(Math.PI)
    ],
    true // allow groups
  ),
  [
    new THREE.MeshBasicMaterial({ color: "red" }),
    new THREE.MeshBasicMaterial({ color: "blue" })
  ]
);
scene.add(plane);
4 Likes

For a more complex object, it should be possible to clone geometry, flip the normals and use mergeGeometries like in the example

2 Likes

You can do this economically and without creating a custom shader.
Create a second object that shares the geometry of the first, and assign 2 different materials with FrontSide and BackSide respectively. This will work for large models without a hit on performance, unless of course you are introducing a large bitmap for both sides of the object.

Here you can see the same technique on a 5Mb glb of a Basball shirt in trinityjs.

see also How to have different colors/textures on bottom and top side of a Plane?