three.jsraycastingreact-three-fiber

raycasting coordinate problems


Hello guys i w want to implement drag and drop functionality to my project but i have problem with selecting shader objects on a 3d model

i implemented a cursor follower and passed into fragments but the dot at fragments is always have a margin with cursor ot top right and the margin changes when i scroll far

i couldt find where is the issue please help me. you can see the red dot and cursor position(cursor is black circle)

  const OnlyUvMap = () => {
  extend({ ShaderMaterial });

  const canvasRef = useRef();

  function TShirtModel() {
    const { nodes } = useGLTF("tshirtv6.glb");
    const { camera, scene } = useThree();
    const meshRef = useRef();
    const raycaster = new THREE.Raycaster(); // For raycasting
    const mouse = new THREE.Vector2(); // Mouse position in normalized device coordinates

    const cursorUV = useRef(new THREE.Vector2(0.0, 0.0)); // Default UV at the center

    useEffect(() => {
      if (!meshRef.current) {
        return;
      }
      const handleMouseMove = (event) => {
        // Normalize mouse coordinates
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

        // Set raycaster and detect intersections
        raycaster.setFromCamera(mouse, camera);
        const intersects = raycaster.intersectObject(meshRef.current);

        if (intersects.length > 0 && intersects[0].uv) {
          // Update cursor UV when intersecting with the mesh
          const uv = intersects[0].uv;
          console.log("raycaser uv", uv);

          // cursorUV.current.set(uv.x, uv.y);
          cursorUV.current.set(intersects[0].uv.x, intersects[0].uv.y);
        } else {
          console.log("No intersection detected.");
          console.log("mouse", mouse);
        }
      };

      // Attach mousemove event listener
      document.addEventListener("mousemove", handleMouseMove);

      return () => {
        // Clean up the event listener
        document.removeEventListener("mousemove", handleMouseMove);
      };
    }, [camera]);

    if (!nodes) {
      console.log("Nodes not loaded yet");
      return null; // Wait until the model is loaded
    }

    console.log(meshRef.current);
    return (
      <Suspense fallback={<p>wait please</p>}>
        <mesh
          geometry={nodes.Scene.children[0].geometry}
          position={[0, -1.2, 0]}
          ref={meshRef}
        >
          <shaderMaterial
            uniforms={{
              lightPosition: { value: new Vector3(2.0, 2.0, 2.0) }, // Lighting position
              diffuseMap: { value: diffuseMapTexture },
              cursorUV: { value: cursorUV.current }, // Pass cursorUV to the shader
              dotRadius: { value: 0.02 }, // Dot size
              dotColor: { value: new THREE.Color(1.0, 0.0, 0.0) }, // Dot color (red)
            }}
            vertexShader={vertexShader}
            fragmentShader={fragmentShader}
          />
        </mesh>
      </Suspense>
    );
  }

  const vertexShader = `
    varying vec2 vUv;
    varying vec3 vPosition;
    varying vec3 vNormal;

    void main() {
      vUv = uv;
      vNormal = normalize(normalMatrix * normal); // Standard normal transformation
      vPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `;

  const fragmentShader = `
  uniform vec3 lightPosition;
  uniform sampler2D diffuseMap;
  uniform vec2 cursorUV; // Cursor UV coordinates
  uniform float dotRadius; // Radius of the dot
  uniform vec3 dotColor; // Color of the dot

  varying vec2 vUv;
  varying vec3 vNormal;
  varying vec3 vPosition;

  void main() {
    // Lighting calculation
    vec3 lightDir = normalize(lightPosition - vPosition);
    float lightIntensity = max(dot(adjustedNormal, lightDir), 0.0);

    // Sample base diffuse map texture
    vec3 baseColor = texture2D(diffuseMap, vUv).rgb;

    // Apply light intensity to base color
    baseColor *= lightIntensity;

    //  the dot in the center of the front part
    float dist = distance(vUv, cursorUV);

    // Blend the dot color into the base color
    baseColor = mix(baseColor, dotColor, smoothstep(dotRadius, dotRadius * 0.9, dist));

  // vec3 baseColortry = vec3(vUv, 0.0); // Visualize UV as RGB
    // Output the final color
    // gl_FragColor = vec4(baseColortry, 1.0);
  }
`;

  return (
    <Canvas
      ref={canvasRef}
      camera={{
        position: [0, 0, 1],
        fov: 50,
        up: [0, 3, 0],
        near: 0.1, // Adjusted near clipping plane
        far: 10, // Adjusted far clipping plane
      }}
      style={{
        backgroundColor: "rgb(244, 244, 244)",
        borderRadius: "30px",
      }}
    >
      <ambientLight intensity={0.9} />
      <directionalLight intensity={0.8} position={[2, 2, 2]} castShadow />
      <TShirtModel />
      <OrbitControls makeDefault />
    </Canvas>
  );
};

difference between cursor and red dot


Solution

  • found the issue canvas was occupying 60% of the screen and when i try to find and intersect with mouse coordinates and uv coordinates there is an offset as uv coordinates only related with canvas and mouse coordinates related with whole screen

    and the solution is getting relative coordinates:

    const rect = getCanvasBounds();
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;