Skip to content

THREE.WebGLRenderer: Context Lost` when switching shader effects on a GLTF model #3566

@KillshotELOx

Description

@KillshotELOx

I am encountering a THREE.WebGLRenderer: Context Lost error in my React application. The application is designed to showcase different visual effects on a 3D model loaded from a .glb file. The error occurs
when I switch between different visual effects that are applied as shaders to the model.

The application uses @react-three/fiber and @react-three/drei to render the 3D scene. The model is loaded using useGLTF, and the effects (shaders) are applied within a useEffect hook in a ModelViewer
component.

Steps to Reproduce:

  1. Load the application.
  2. A default 3D model is displayed.
  3. Select a visual effect from the sidebar (e.g., "Cel Shading"). The effect applies correctly.
  4. Select a different visual effect from the sidebar (e.g., "Jelly Effect").
  5. The WebGL context is lost, and the console displays the error THREE.WebGLRenderer: Context Lost.

Expected Behavior:

When switching between different visual effects, the new effect should be applied to the 3D model seamlessly. The 3D view should remain active and responsive, displaying the model with the newly selected
shader, without any WebGL context loss or errors in the console.

Relevant Code Snippets:

ModelViewer.tsx (before attempted fix):

1 import React, { useRef, useEffect } from 'react';
2 import { useGLTF } from '@react-three/drei';
3 import * as THREE from 'three';
4 import { applyCelShader } from '../components/shaders/CelShader';
5 import { applyJellyShader } from '../components/shaders/JellyShader';
6 
7 interface ModelViewerProps {
8   modelPath: string;
9   effectName: string | null;

10 }
11
12 export const ModelViewer: React.FC = ({ modelPath, effectName }) => {
13 const { scene } = useGLTF(modelPath);
14 const modelRef = useRef<THREE.Group>(null);
15
16 useEffect(() => {
17 if (modelRef.current) {
18 // Reset materials before applying new effects
19 modelRef.current.traverse((child) => {
20 if (child instanceof THREE.Mesh) {
21 child.material = new THREE.MeshStandardMaterial();
22 }
23 });
24
25 switch (effectName) {
26 case 'Cel Shading':
27 applyCelShader(modelRef.current);
28 break;
29 case 'Jelly Effect':
30 applyJellyShader(modelRef.current);
31 break;
32 default:
33 break;
34 }
35 }
36 }, [effectName, scene]);
37
38 return ;
39 };

Attempted Fix:

I attempted to resolve the issue by memoizing the scene object to prevent the useEffect hook from re-running unnecessarily. This did not resolve the context loss error.

ModelViewer.tsx (after attempted fix):

1 import React, { useRef, useEffect, useMemo } from 'react';
2 // ... imports
3 
4 export const ModelViewer: React.FC<ModelViewerProps> = ({ modelPath, effectName }) => {
5   const { scene } = useGLTF(modelPath);
6   const modelRef = useRef<THREE.Group>(null);
7 
8   const memoizedScene = useMemo(() => scene.clone(), [scene]);
9 

10 useEffect(() => {
11 // ... (same effect logic as before)
12 }, [effectName, memoizedScene]);
13
14 return ;
15 };

Environment:

  • @react-three/fiber: ^9.3.0
  • @react-three/drei: ^10.6.1
  • three: ^0.179.1
  • react: ^19.1.0
  • Browser: Chrome Version 139.0.7258.67 (Official Build) (64-bit)
  • OS: Windows 11 Home (Version 10.0.26100)

Anything else we need to know?

The core functionality of the application is to dynamically apply different shaders to a GLTF model. The logic for this is contained within the ModelViewer.tsx component.

The process flow is as follows:

  1. The useGLTF hook from @react-three/drei is used to load the model's scene.
  2. A useEffect hook listens for changes to the effectName prop.
  3. When the effect changes, the useEffect hook first traverses the entire model and resets the material of every THREE.Mesh to a new THREE.MeshStandardMaterial().
  4. After the materials are reset, a switch statement calls a specific function (e.g., applyCelShader) which then modifies the newly applied standard material with custom shader code.

The first effect always applies correctly. The WebGLRenderer: Context Lost error only occurs when switching from one effect to a different one.

An attempt was made to memoize the scene object using useMemo and scene.clone() to prevent the useEffect hook from re-triggering unnecessarily. This did not resolve the issue. It seems the problem lies
within the material swapping or shader application logic itself when performed multiple times.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions