3

I am developing a portfolio application using Nextjs and 3D libraries such as Three and React-Three.

I use Vercel to deploy the app.

I am experiencing a problem where the 3D model in the deployed app is displaying as a blank white square on Android phones:

  • Xiaomi red mi note 7 / MI browser

  • Samsung s10 / chrome browser.

Is there a solution to this issue?

Deployed Site: https://portfolio2-nu-ruddy.vercel.app/

Git Repo: https://github.com/ItayTur/Portfolio

3D model component:

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { useLoader, Canvas } from "@react-three/fiber";
import { Suspense, useEffect, useState } from "react";
import { OrbitControls, Preload } from "@react-three/drei";
import Loader from "./Loader";

const Computers = ({ isMobile = false }) => {
  const gltf = useLoader(GLTFLoader, "/desktop_pc/scene.gltf");
  return (
    <mesh>
      <hemisphereLight intensity={0.15} groundColor="black" />
      <pointLight intensity={1} />
      <spotLight
        position={[-20, 50, 10]}
        angle={0.12}
        penumbra={1}
        intensity={1}
        castShadow
        shadow-mapSize={1024}
      />
      <primitive
        scale={isMobile ? 0.7 : 0.75}
        position={isMobile ? [0, -3, -2.2] : [0, -3.25, -1.5]}
        rotation={[-0.01, -0.2, -0.1]}
        object={gltf.scene}
      />
    </mesh>
  );
};

const ComputersCanvas = () => {
  const [isMobile, setIsMobile] = useState(false);

  useEffect(() => {
    const mediaQuery = window.matchMedia("(max-width: 500px)");

    setIsMobile(mediaQuery.matches);

    const onMediaQueryChange = (event: MediaQueryListEvent) => {
      setIsMobile(event.matches);
    };

    mediaQuery.addEventListener("change", onMediaQueryChange);

    return () => {
      mediaQuery.removeEventListener("change", onMediaQueryChange);
    };
  }, []);
  return (
    <Canvas
      frameloop="demand"
      shadows
      camera={{ position: [20, 3, 5], fov: 25 }}
      gl={{ preserveDrawingBuffer: true }}
    >
      <Suspense fallback={<Loader />}>
        <OrbitControls
          enableZoom={false}
          maxPolarAngle={Math.PI / 2}
          minPolarAngle={Math.PI / 2}
        />
        <Computers isMobile={isMobile} />
      </Suspense>
      <Preload all />
    </Canvas>
  );
};

export default ComputersCanvas;

Itay Tur
  • 683
  • 1
  • 15
  • 40

2 Answers2

0

The issue has been reproduced on my Samsung Galaxy A32.

Solution

The problem lies in Hero.tsx; add alpha: true to the webGL configuration.

// Hero.tsx
<Canvas
  frameloop="demand"
  shadows
  camera={{ position: [20, 3, 5], fov: 25 }}
  // add alpha: true here.
  gl={{ preserveDrawingBuffer: true, alpha: true }}
>
{/* ... */}
</Canvas>

Explanation

The reason isn't very clear at current moment. I can only guess, since the console message shows that the r3f instances are not properly destroyed. The message was fetched from chrome://inspect.

enter image description here

WARNING: Too many active WebGL contexts. Oldest context will be lost., THREE.WebGLRenderer: Context Lost.

These messages mean that every time a 3d canvas gets mounted, there's another unnecessary webGL instance is created. Proper implementation of multiple canvas should hold only one webGL instance and share it among canvases manually or by using portal.

I assumed that webGL buffer is somehow got corrupted, so forced the background to be transparent by changing the configuration.

The error is not reproducible in desktop browser; this may need another deep investigation.

sungryeol
  • 3,677
  • 7
  • 20
  • I deployed the update, but unfortunately, the white issue still persists. Despite no warning signs, a white square still appears in place of the 3D model. Additionally, the app sometimes crashes altogether. – Itay Tur Jun 09 '23 at 17:05
  • @ItayTur are you sure you only applied above solution? it should have nothing to do with the app crash. and try run without mobile-desktop code altogether. it's the only possible reason that may cause the problem. – sungryeol Jun 13 '23 at 08:20
  • I tried commenting out the mobile desktop code, but the problem remains. You can view the change here: https://github.com/ItayTur/Portfolio/commit/f73aa0014d42bd03229bac5582f1aca406ca90da. – Itay Tur Jun 13 '23 at 15:31
0

I tried to debug your code. Something is preventing rendering of your Canvas component in android's chrome browser. I have tried to resolve it and it seems to be working. Could you try updating Computers.txs file with the following code.?
The remaining code should be same.

const ComputersCanvas = () => {
    const [isMobile, setIsMobile] = useState(false);

    useEffect(() => {
        const mediaQuery = window.matchMedia("(max-width: 500px)");

        setIsMobile(mediaQuery.matches);

        const onMediaQueryChange = (event: MediaQueryListEvent) => {
            setIsMobile(event.matches);
        };

        mediaQuery.addEventListener("change", onMediaQueryChange);

        return () => {
            mediaQuery.removeEventListener("change", onMediaQueryChange);
        };
    }, []);

    const [initialized, setInitialized] = useState(false)

    useEffect(() => {
        if (!initialized) {
            setInitialized(true);
        }
    }, [initialized]);

    if (!initialized) {
        return <div></div>
    }

    return (
        <Canvas
            frameloop="demand"
            shadows
            camera={{position: [20, 3, 5], fov: 25}}
            gl={{preserveDrawingBuffer: true, alpha: true}}
        >
            <Suspense fallback={<Loader/>}>
                <OrbitControls
                    enableZoom={false}
                    maxPolarAngle={Math.PI / 2}
                    minPolarAngle={Math.PI / 2}
                />
                <Computers isMobile={isMobile}/>
            </Suspense>
            <Preload all/>
        </Canvas>
    );
};

This works because initially we are rendering div element.
After the first render we are forcing to it to re-render with Canvas element.
Hope this resolves your issue.
Thanks :)

Update:
I am sorry I am unable to resolve your issue because it needs experimenting with different approaches. However I can give some suggestions and ideas that may ease your process. The main issues lies with canvas behavior on android/mobile browser. Browser limits the number of canvas context. I have seen it to be eight in general, but they can be any number.
You may have noticed the console message THREE.WebGLRenderer: Context Lost.

  • You can either use one canvas context and reusing the same.This could be cumbersome, especially with your app.
  • You can try a workaround in your app in a way that your app listens to scroll and create canvas only for the view that is active/visible. In the current state, your app is creating canvas for all the views/components on the page.
  • You can of course try googling and searching, how others overcame similar situation.
  • You can increase the bounty on this question to attract attention and wait for the good solution.
  • etc.
Mearaj
  • 1,828
  • 11
  • 14
  • The recent update has made some improvements. However, I still face issues where the white square reappears or the app crashes upon refreshing the page, accompanied by the error message: `Application error: a client-side exception has occurred`. – Itay Tur Jun 13 '23 at 07:03
  • Could you try in incognito mode? because I am unable to reproduce the issue again after the update. May be, it could be happening because of cache? If this doesn't solves, can you provide any specific instructions/steps to reproduce it? – Mearaj Jun 13 '23 at 13:18
  • I am not so deep in this area, but does this [link](https://stackoverflow.com/questions/62183371/threejs-three-buffergeometry-computeboundingsphere-gives-error-nan-position) provides any insights or helps? Because the console is showing the same error multiple times in android chrome browser. – Mearaj Jun 13 '23 at 14:09
  • I noticed the error, but I don't think it's the root cause. The error appears on the desktop as well, but the app functions properly. I have attempted to resolve the issue by using incognito mode, in the Chrome browser, and my Samsung S10 phone, but unfortunately, the problem persists. – Itay Tur Jun 13 '23 at 15:22
  • Just for debugging purpose, could you try commenting out inside every canvas and test it's impact on mobile?. Just to see if it crashes. – Mearaj Jun 14 '23 at 03:22
  • I have commented out the , and you can now review the modifications made in this link: https://github.com/ItayTur/Portfolio/commit/fac4ce411d2b46870d66a7f6dafb1edede6044a8 The app doesn't crash anymore, but the white square issue still persists – Itay Tur Jun 17 '23 at 16:24
  • Does it work fine, without any crash on android browser? – Mearaj Jun 17 '23 at 16:25
  • The app doesn't crash anymore, but the white square issue still persists – Itay Tur Jun 17 '23 at 16:26
  • 1
    Ok thanks, I will debug it and will be back with a solution most probably. Also whenever you test it, test it in incognito (for this android issue). – Mearaj Jun 17 '23 at 16:28
  • Forgot to mention, during testing please keep reactStrictMode:false in next.config.js. https://stackoverflow.com/questions/61523632/why-when-i-put-my-component-into-react-strictmode-its-constructor-called-twice – Mearaj Jun 17 '23 at 16:42
  • I set `reactStrictMode: false`, you can view it in this commit: https://github.com/ItayTur/Portfolio/commit/f9e87831300d6c0efbed7c640f1f046d3e207795 It seems that the app crashes frequently and when it does work, the white square appears. I tested in incognito, chrome, Samsung s10. – Itay Tur Jun 18 '23 at 17:13
  • Hi @ItayTur, I have replied in my answer. Sorry for that. – Mearaj Jun 18 '23 at 18:09
  • 1
    I appreciate all the help you provided. Thank you so much. – Itay Tur Jun 18 '23 at 18:17