three.js

GLTF Model Backside View Appears not Lighter


"I'm new to Three.js and have created a .gltf file using Blender. However, I'm facing an issue where the backside of my object appears lighter than expected.

Here's my current view: https://i.sstatic.net/jtI4aCSF.jpg
I'm trying to achieve a look similar to this: https://i.sstatic.net/pL5L8cfg.gif

In this GIF, the object's backside wireframe appears lighter. How can I achieve this effect in my code ?

I can't figure out what’s causing this difference. Any insights or suggestions would be greatly appreciated. Thank you!"


  private initThree(): void {
    const canvas = this.canvas.nativeElement;
    this.scene = new THREE.Scene();
    this.scene.background = new THREE.Color(0xffffff);

    const aspectRatio = canvas.clientWidth / canvas.clientHeight;
    this.camera = new THREE.PerspectiveCamera(1, aspectRatio, 0.0001, 500); // FOV, aspect ratio, near, far

    this.renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
    this.renderer.setSize(canvas.clientWidth, canvas.clientHeight);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.controls = new ArcballControls(this.camera, this.renderer.domElement);
    this.controls.rotateSpeed = 0.7;
  }

  public shapeObject(modelPath: string, isInclusionObject: boolean = false): void {
    this.controls.reset();
    if (!isInclusionObject) this.clearScene();
    this.loaderGLTF.load(modelPath, (gltf) => {
      const model = gltf.scene;
      const box = new THREE.Box3().setFromObject(model);
      const center = box.getCenter(new THREE.Vector3());
      model.position.sub(center);
      const axesHelper = new THREE.AxesHelper(1);
      this.scene.add(axesHelper);
      if (isInclusionObject) {
        this.addInclusionModel(model, 0xff0000);
      } else {
        this.addShapeModel(model, 0x4080ff);
      }

      this.scene.add(model);
    },
      (xhr) => console.log(`${(xhr.loaded / xhr.total) * 100}% loaded`),
      (error) => console.error('An error occurred while loading the GLTF model:', error));
  }

  private addInclusionModel(model: THREE.Object3D, wireframeColor: number | string): void {
    this.camera.position.set(0, 0, 3.5);
    const targetDimensions = new THREE.Vector2(0.05, 0.05);
    const box = new THREE.Box3().setFromObject(model);
    const size = new THREE.Vector3();
    box.getSize(size);
    const scaleFactor = Math.min(
      targetDimensions.x / size.x,
      targetDimensions.y / size.y
    );
    model.scale.set(scaleFactor, scaleFactor, scaleFactor);
    model.position.set(0.18, 0, 0);

    model.traverse((child) => {
      if ((child as THREE.Mesh).isMesh) {
        const mesh = child as THREE.Mesh;
        const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];

        materials.forEach((mat) => {
          const backWireframeMaterial = new THREE.LineBasicMaterial({ color: wireframeColor });
          const backWireframeGeometry = new THREE.EdgesGeometry(mesh.geometry);
          const backWireframe = new THREE.LineSegments(backWireframeGeometry, backWireframeMaterial);
          mesh.add(backWireframe);
        });
      }
    });
    this.activeInclusion++;
    this.addGuiControls(model, this.activeInclusion);
    this.inclusionIds.push(model.id);
    this.objectCounter++;
    this.sceneObjects.push({ id: this.objectCounter, name: `Inclusion ${this.objectCounter}`, object: model });
    this.inclusionStack.push(model);
    this.activeInclusion = this.sceneObjects.length;
    this.wireframeStates[this.objectCounter] = false;
    this.wireframeEnabled = false;
  }

  private addShapeModel(model: THREE.Object3D, wireframeColor: number | string): void {
    const targetDimensions = new THREE.Vector2(0.30, 0.30);
    const box = new THREE.Box3().setFromObject(model);
    const size = new THREE.Vector3();
    box.getSize(size);
    const scaleFactor = Math.min(
      targetDimensions.x / size.x,
      targetDimensions.y / size.y
    );
    model.scale.set(scaleFactor, scaleFactor, scaleFactor);
    box.setFromObject(model);
    box.getSize(size);
    const center = box.getCenter(new THREE.Vector3());
    //model.position.set(-center.x, -center.y, 0);
    model.position.set(-center.x, -0.05, 0);

    model.traverse((child) => {
      if ((child as THREE.Mesh).isMesh) {
        const mesh = child as THREE.Mesh;
        const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
        materials.forEach((mat) => {
          if ((mat as THREE.MeshStandardMaterial).wireframe !== undefined) {
            (mat as THREE.MeshStandardMaterial).wireframe = true;
            const wireframeMaterial = new THREE.LineBasicMaterial({ color: wireframeColor });
            const wireframeGeometry = new THREE.EdgesGeometry(mesh.geometry);
            const wireframe = new THREE.LineSegments(wireframeGeometry, wireframeMaterial);
            mesh.add(wireframe);
          }
        });
      }
    });
    this.isShapeAdded = true;
  }

Solution

  • I found my solution by using vertex shader and fragment shader

    private shapeObject(modelPath: string, isInclusionObject: boolean = false): void {
      this.controls.reset();
      this.camera.position.set(0, 0, 3.5);
      if(!isInclusionObject) this.clearScene();
      this.loaderGLTF.load(modelPath, (gltf) => {
            const model = gltf.scene;
            const box = new THREE.Box3().setFromObject(model);
            const center = box.getCenter(new THREE.Vector3());
            model.position.sub(center);
            const axesHelper = new THREE.AxesHelper(0);
            this.scene.add(axesHelper);
            if (isInclusionObject) {
                  this.addInclusionModel(model, 0xff0000);
            } else {
                  this.addShapeModel(model, 'vec3(0.25,0.50,1.00)', 'vec3(0.95,0.95,0.95)');
            }
            this.scene.add(model);
      },
            (xhr) => console.log(`${(xhr.loaded / xhr.total) * 100}% loaded`),
            (error) => console.error('An error occurred while loading the GLTF model:', error));
    }
    
    private addShapeModel(model: THREE.Object3D, outSideColor: any, insideColor: any): void {
      const targetDimensions = new THREE.Vector2(0.30, 0.30);
      const box = new THREE.Box3().setFromObject(model);
      const size = new THREE.Vector3();
      box.getSize(size);
      const scaleFactor = Math.min(
            targetDimensions.x / size.x,
            targetDimensions.y / size.y
      );
      model.scale.set(scaleFactor, scaleFactor, scaleFactor);
      box.setFromObject(model);
      box.getSize(size);
      const center = box.getCenter(new THREE.Vector3());
      //model.position.set(-center.x, -center.y, 0);
      model.position.set(-center.x, -0.05, 0);
    
      var vertexShader = `
      varying vec3 vNormal;
      void main() {
        vNormal = normalize(normalMatrix * normal);
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    `;
    
      var fragmentShader = `
      varying vec3 vNormal;
      void main() {
        float intensity = dot(vNormal, vec3(0.0, 0.0, 1.0)); // Light direction
        vec3 color = mix(${insideColor}, ${outSideColor}, step(0.0, intensity));
        gl_FragColor = vec4(color, 0.5);
      }
    `;
      model.traverse((child) => {
            if ((child as THREE.Mesh).isMesh) {
                  const mesh = child as THREE.Mesh;
                  const material = new THREE.ShaderMaterial({
                        vertexShader: vertexShader,
                        fragmentShader: fragmentShader,
                        side: THREE.DoubleSide,
                        transparent: true,
                        opacity: 0.5
                  });
                  mesh.material = material;
                  const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
                  materials.forEach((mat) => {
                        if ((mat as THREE.MeshStandardMaterial).wireframe !== undefined) {
                              (mat as THREE.MeshStandardMaterial).wireframe = true;
                        }
                  });
            }
      });
      this.isShapeAdded = true;
    }