Home  >  Q&A  >  body text

Edited title: Difficulties in rendering 2D images on 3D surfaces

I'm working on a code tutorial from youtube but I'm running into an issue where I have 13 icosahedral geometry spheres successfully rendered to my screen. However, the video makes the 2D image I display on the ball invisible. There are no errors in my console. This project is Three.js and tailwindcss. There is no typescript.

NOTE: This is my first thrre.js project and I am still new to software development following the self-taught route. Thank you for your patience, understanding and help. Thank you very much.

main problem How can I get the icon image from "tech" to render correctly to the side of the ball?

The intention seems to be to make it look similar to a golf ball with the company logo on it, like you would find on any golf course or driving range.

I've been trying to solve this problem for over a week now and I'm completely stumped.

The tutorial I followed on YouTube is here: https://www.youtube.com/watch?v=0fYi8SGA20k&t=6190s

The following are the code files most relevant to this issue. I'd be happy to provide more if useful. Here is the Tech.jsx file that contains the component that displays the ball:

import { BallCanvas } from './canvas'
import { SectionWrapper } from '../hoc'
import { technologies } from '../constants/index'

const TechComponent = () => {
  return (
    <div className='flex flex-row flex-wrap justify-center gap-10'>
      {technologies.map((technology) => (
        <div className='w-28 h-28' key={technology.name}>
            <BallCanvas icon={technology.icon} />
        </div>
      ))}
    </div>
  )
}


const Tech = SectionWrapper(TechComponent, "about");

export default Tech;

The next step is to import the Balls.jsx file into Tech.jsx:

import { Suspense } from 'react'
import { Canvas } from '@react-three/fiber'
import { Decal, Float, OrbitControls, Preload, useTexture } from '@react-three/drei'

import CanvasLoader from '../Loader'
import PropTypes from 'prop-types';

const Ball = (props) => {
  const [decal] = useTexture([props.imgURL])

  return (
    
    <Float speed={1.75} rotationIntensity={1} floatIntensity={2}>
      <ambientLight intensity={0.25}/>
      {/* eslint-disable-next-line react/no-unknown-property */}
      <directionalLight position={[0, 0, 0.05]} />
      {/* eslint-disable-next-line react/no-unknown-property */}
      <mesh castShadow receiveShadow>
        {/* eslint-disable-next-line react/no-unknown-property */}
        <icosahedronGeometry attach="geometry" args={[4, 3]} />
        {/* eslint-disable-next-line react/no-unknown-property */}
        <meshStandardMaterial color="#fff8eb" polygonOffset polygonOffsetFactor={-5} flatShading />
        <Decal position={[0, 0, 1]} map={decal}/> 
        {/*decal not loading*/}
      </mesh>
    </Float>
  )
}

const BallCanvas = ({ icon }) => {
  return (
    <Canvas
      frameloop="demand"
      shadows
      camera={{ position: [20, 3, 5], fov:25}}
      gl={{ preserveDrawingBuffer: true}}
      >
        <Suspense fallback={<CanvasLoader />}>
          <OrbitControls 
            enableZoom={false}
          />
          <Ball imgURL={icon}/>
        </Suspense>

        <Preload all />
    </Canvas>
  )
}

Ball.propTypes = {
  imgURL: PropTypes.string.isRequired,
};

BallCanvas.propTypes = {
  icon: PropTypes.string.isRequired,
};

export default BallCanvas;

Next is a snippet from the index.js file that contains navigation information to locate the icon that needs to be displayed. This will be the import and "technical" constant. Note that these files do all exist in my project and when I click on them they show up in the vscode window:

import {
    mobile,
    backend,
    creator,
    web,
    javascript,
    typescript,
    html,
    css,
    reactjs,
    redux,
    tailwind,
    nodejs,
    mongodb,
    git,
    figma,
    docker,
    meta,
    starbucks,
    tesla,
    shopify,
    carrent,
    jobit,
    tripguide,
    threejs,
  } from "../assets";

const technologies = [
    {
      name: "HTML 5",
      icon: html,
    },
    {
      name: "CSS 3",
      icon: css,
    },
    {
      name: "JavaScript",
      icon: javascript,
    },
    {
      name: "TypeScript",
      icon: typescript,
    },
    {
      name: "React JS",
      icon: reactjs,
    },
    {
      name: "Redux Toolkit",
      icon: redux,
    },
    {
      name: "Tailwind CSS",
      icon: tailwind,
    },
    {
      name: "Node JS",
      icon: nodejs,
    },
    {
      name: "MongoDB",
      icon: mongodb,
    },
    {
      name: "Three JS",
      icon: threejs,
    },
    {
      name: "git",
      icon: git,
    },
    {
      name: "figma",
      icon: figma,
    },
    {
      name: "docker",
      icon: docker,
    },
  ];

Then we have my package.json and tailwind.config.js just to keep things sane.

First package.json:

{
  "name": "portfolio-rob-2023",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "@emailjs/browser": "^3.11.0",
    "@react-three/drei": "^9.66.6",
    "@react-three/fiber": "^8.13.0",
    "@types/three": "^0.152.0",
    "framer-motion": "^10.12.8",
    "maath": "^0.5.3",
    "prop-types": "^15.8.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-parallax-tilt": "^1.7.140",
    "react-router-dom": "^6.11.1",
    "react-tilt": "^1.0.2",
    "react-vertical-timeline-component": "^3.6.0",
    "three": "^0.152.2"
  },
  "devDependencies": {
    "@types/node": "^20.1.1",
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@vitejs/plugin-react": "^4.0.0",
    "autoprefixer": "^10.4.14",
    "eslint": "^8.38.0",
    "eslint-plugin-react": "^7.32.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.3.4",
    "postcss": "^8.4.23",
    "tailwindcss": "^3.3.2",
    "vite": "^4.3.2"
  }
}

Finally tailwind.config.js:

@type {import('tailwindcss').Config}
module.exports = {
  content: [
    "./src/**/*.{js,jsx}"
  ],

  mode: "jit",
  theme: {
    extend: {
      colors: {
        primary: "#050816",
        secondary: "#aaa6c3",
        tertiary: "#151030",
        "black-100": "#100d25",
        "black-200": "#090325",
        "white-100": "#f3f3f3",
      },
      boxShadow: {
        card: "0px 35px 120px -15px #211e35",
      },
      screens: {
        xs: "450px",
      },
      backgroundImage: {
        "hero-pattern": "url('/src/assets/herobg.png')",
      },
    },
  },
  plugins: [],
};

` I've tried showing the icons on a normal ball, but that only allowed them to stretch over the entire surface of the 3d ball, and there were multiple "no-undefined" es linting issues. It also involves completely removing the Ball.jsx file and rewriting it with a more basic grid. This is very unsatisfactory, but if I could display a logo on the side of any 3D ball it would be a huge win at this point. As mentioned before, I need to be able to see the icon/logo on each ball.

P粉891237912P粉891237912305 days ago455

reply all(1)I'll reply

  • P粉852578075

    P粉8525780752024-01-11 16:57:15

    Update your Balls.jsx file, here’s what I did:

    import { Suspense } from "react";
    import PropTypes from "prop-types";
    import { Canvas } from "@react-three/fiber";
    import {
      Decal,
      Float,
      OrbitControls,
      Preload,
      useTexture,
    } from "@react-three/drei";
    
    import CanvasLoader from "../Loader";
    
    const BallCanvas = ({ icon }) => {
      return (
        
          }>
            
            
          
    
          
        
      );
    };
    
    const Ball = ({ icon }) => {
      const [decal] = useTexture([icon]);
    
      return (
        
          
          
          
            
            
            
          
        
      );
    };
    
    BallCanvas.propTypes = {
      icon: PropTypes.string.isRequired,
    };
    
    export default BallCanvas;

    reply
    0
  • Cancelreply