How to set a decal to always be on top of the mesh

I’m using react and three.js.

I want to load the downloaded glb file and paste the image on top of it using Decal.

For some glb files, this was possible with a simple manipulation of z.
However, this is not possible with the current file.

If you move the position of the decal, the decal will be inside the mesh in the center of the top of the umbrella.

When scaled using the wheel, it is on top.

I don’t know what the problem is.

Below I attach my code.

import React, { useState, useRef } from 'react'
import { Canvas, useFrame } from '@react-three/fiber'
import { useGLTF, useTexture, AccumulativeShadows, RandomizedLight, Decal, Environment, Center } from '@react-three/drei'
import { easing } from 'maath'
import { useSnapshot } from 'valtio'
import { state } from './store'

export const App = ({ position = [0, 0, 2.5], fov = 25 }) => (
  <Canvas shadows camera={{ position, fov }} gl={{ preserveDrawingBuffer: true }} eventSource={document.getElementById('root')} eventPrefix="client">
    <ambientLight intensity={0.5} />
    <Environment files="https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/1k/potsdamer_platz_1k.hdr" />
    <CameraRig>
      <Backdrop />
      <Center>
        <Umbrella />
      </Center>
    </CameraRig>
  </Canvas>
)

function Backdrop() {
  const shadows = useRef()
  useFrame((state, delta) => easing.dampC(shadows.current.getMesh().material.color, state.color, 0.25, delta))
  return (
    <AccumulativeShadows ref={shadows} temporal frames={60} alphaTest={0.85} scale={10} rotation={[Math.PI / 2, 0, 0]} position={[0, 0, -0.14]}>
      <RandomizedLight amount={4} radius={9} intensity={0.55} ambient={0.25} position={[5, 5, -10]} />
      <RandomizedLight amount={4} radius={5} intensity={0.25} ambient={0.55} position={[-5, 5, -9]} />
    </AccumulativeShadows>
  )
}

function CameraRig({ children }) {
  const group = useRef()
  const snap = useSnapshot(state)
  useFrame((state, delta) => {
    easing.damp3(state.camera.position, [snap.intro ? -state.viewport.width / 4 : 0, 0, 2], 0.25, delta)
    easing.dampE(group.current.rotation, [state.pointer.y, -state.pointer.x / 5, 0], 0.25, delta)
  })
  return <group ref={group}>{children}</group>
}

function Umbrella(props) {
  const snap = useSnapshot(state)
  const texture = useTexture(`/${snap.decal}.png`)
  const { nodes, materials } = useGLTF('/umbrella.glb')
  console.log(nodes)
  useFrame((state, delta) => easing.dampC(materials.initialShadingGroup.color, snap.color, 0.25, delta))

  // 데칼 크기 상태
  const [decalScale, setDecalScale] = useState(1.35)

  // 마우스 휠 이벤트 핸들러
  const handleWheel = (event) => {
    // 휠 이벤트로 데칼 크기 조절
    const newScale = Math.max(0.1, Math.min(3, decalScale + event.deltaY * 0.01))
    setDecalScale(newScale)
  }

  // Decal position state
  const [decalPosition, setDecalPosition] = useState([-1, -2, 8])

  // Decal rotation state
  const [decalRotation, setDecalRotation] = useState([0, 0, 0])

  // Keyboard input handler
  const handleKeyboardInput = (event) => {
    const { key } = event

    // Decal movement amount
    const moveAmount = 0.05

    switch (key) {
      case 'ArrowLeft':
        setDecalPosition([decalPosition[0] - moveAmount, decalPosition[1], decalPosition[2]])
        setDecalRotation([0, 0, 0]) // Reset rotation when moving horizontally
        break
      case 'ArrowRight':
        setDecalPosition([decalPosition[0] + moveAmount, decalPosition[1], decalPosition[2]])
        setDecalRotation([0, 0, 0]) // Reset rotation when moving horizontally
        break
      case 'ArrowUp':
        setDecalPosition([decalPosition[0], decalPosition[1] + moveAmount, decalPosition[2]])
        setDecalRotation([0, 0, 0]) // Reset rotation when moving vertically
        break
      case 'ArrowDown':
        setDecalPosition([decalPosition[0], decalPosition[1] - moveAmount, decalPosition[2]])
        setDecalRotation([0, 0, 0]) // Reset rotation when moving vertically
        break
      default:
        break
    }
  }

  // Keyboard event listener
  window.addEventListener('keydown', handleKeyboardInput)

  return (
    <group>
      {/* Mesh */}
      <mesh
        castShadow
        geometry={nodes.Object_2.geometry}
        material={materials.initialShadingGroup}
        material-roughness={1}
        {...props}
        dispose={null}
        onWheel={handleWheel}
        scale={0.2}>
        {/* Decal */}
        <Decal
          mesh={nodes.Object_2} // Specify the parent mesh
          position={decalPosition}
          rotation={decalRotation}
          scale={decalScale}
          map={texture}
          map-anisotropy={16}
          renderOrder={1}
        />
      </mesh>
    </group>
  )
}

useGLTF.preload('/umbrella.glb')
;['/react.png', '/three2.png', '/pmndrs.png'].forEach(useTexture.preload)

I’m writing this in case you need an umbrella file.

https://sketchfab.com/3d-models/umbrella-4754f1bc1d6341d19b313ad6a51417de#download

This code is based on another project, please let me know if you need more information or if there is something I should include.

Leave a Comment