Hello,
After updating Babylon.js from an earlier version to 8.28.2, I started getting runtime errors related to StandardMaterial. Specifically, when using StandardMaterial parameters, the following error appears in the browser console:
Uncaught TypeError: Cannot read properties of undefined (reading 'isRefractionEnabled')
This issue did not occur in the previous version I was using (before 8.28.2). It seems to be a regression introduced in the latest update.
I wrote a small patch, but it’s just a temporary workaround to suppress the errors. Of course, I still need help from the Babylon.js developers to properly fix this in the library itself.:
/**
* GLTF Loader Patch for Babylon.js 8.28.2 Compatibility
*
* This patch fixes the "Cannot read properties of undefined (reading 'isRefractionEnabled')" error
* that occurs in Babylon.js 8.28.2 GLTF loader when processing materials.
*
* The issue is that the GLTF loader tries to access the isRefractionEnabled property on an undefined
* subSurface object during material processing.
*/
declare global {
interface Window {
BABYLON: any
}
}
export class GLTFLoaderPatch {
private static isPatched = false
private static originalConsoleError = console.error
/**
* Apply the global GLTF loader patch
* This should be called early in the application lifecycle
*/
static apply(): void {
if (this.isPatched) {
return
}
try {
// Method 1: Patch the global BABYLON object if available
this.patchBabylonGLTFLoader()
// Method 2: Intercept and suppress the specific error
this.patchConsoleError()
// Method 3: Add global error handler for unhandled promise rejections
this.addGlobalErrorHandlers()
this.isPatched = true
console.log('[GLTFLoaderPatch] ✅ GLTF loader compatibility patch applied successfully')
} catch (error) {
console.warn('[GLTFLoaderPatch] ⚠️ Failed to apply patch:', error)
}
}
/**
* Patch the Babylon.js GLTF loader if accessible
*/
private static patchBabylonGLTFLoader(): void {
// Try to access the global BABYLON object
if (typeof window !== 'undefined' && window.BABYLON) {
try {
// Look for the GLTF loader and patch its material processing
const BABYLON = window.BABYLON
// Patch any material-related functions that might access isRefractionEnabled
if (BABYLON.PBRMaterial && BABYLON.PBRMaterial.prototype) {
this.patchPBRMaterial(BABYLON.PBRMaterial)
}
if (BABYLON.StandardMaterial && BABYLON.StandardMaterial.prototype) {
this.patchStandardMaterial(BABYLON.StandardMaterial)
}
console.log('[GLTFLoaderPatch] Babylon.js materials patched')
} catch (error) {
console.warn('[GLTFLoaderPatch] Failed to patch Babylon materials:', error)
}
}
}
/**
* Patch PBRMaterial to ensure subSurface properties are defined
*/
private static patchPBRMaterial(PBRMaterial: any): void {
const originalConstructor = PBRMaterial
// Ensure subSurface is always initialized
if (originalConstructor.prototype && !originalConstructor.prototype._patchedSubSurface) {
Object.defineProperty(originalConstructor.prototype, 'subSurface', {
get: function() {
if (!this._subSurface) {
this._subSurface = {
isRefractionEnabled: false,
isTranslucencyEnabled: false,
isScatteringEnabled: false,
scatteringDiffusionProfile: null,
minimumThickness: 0,
maximumThickness: 1,
tintColor: new (window.BABYLON?.Color3 || Object)(1, 1, 1),
tintColorAtDistance: 1,
diffusionDistance: new (window.BABYLON?.Color3 || Object)(1, 1, 1),
useMaskFromThicknessTexture: false,
refractionIntensity: 1,
translucencyIntensity: 1,
useAlbedoToTintRefraction: false,
useAlbedoToTintTranslucency: false
}
}
return this._subSurface
},
set: function(value) {
this._subSurface = value
},
configurable: true,
enumerable: true
})
originalConstructor.prototype._patchedSubSurface = true
}
}
/**
* Patch StandardMaterial to ensure compatibility
*/
private static patchStandardMaterial(StandardMaterial: any): void {
// StandardMaterial usually doesn't have subSurface, but let's be safe
if (StandardMaterial.prototype && !StandardMaterial.prototype._patchedSubSurface) {
Object.defineProperty(StandardMaterial.prototype, 'subSurface', {
get: function() {
if (!this._subSurface) {
this._subSurface = {
isRefractionEnabled: false,
isTranslucencyEnabled: false,
isScatteringEnabled: false
}
}
return this._subSurface
},
set: function(value) {
this._subSurface = value
},
configurable: true,
enumerable: true
})
StandardMaterial.prototype._patchedSubSurface = true
}
}
/**
* Patch console.error to suppress specific GLTF loader errors
*/
private static patchConsoleError(): void {
console.error = (...args: any[]) => {
const message = args.join(' ')
// Suppress the specific isRefractionEnabled error
if (message.includes('isRefractionEnabled') ||
message.includes('Cannot read properties of undefined')) {
console.warn('[GLTFLoaderPatch] 🔇 Suppressed GLTF loader error:', ...args)
return
}
// Allow other errors through
this.originalConsoleError.apply(console, args)
}
}
/**
* Add global error handlers
*/
private static addGlobalErrorHandlers(): void {
if (typeof window !== 'undefined') {
// Handle unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
if (event.reason &&
(event.reason.message?.includes('isRefractionEnabled') ||
event.reason.message?.includes('Cannot read properties of undefined'))) {
console.warn('[GLTFLoaderPatch] 🔇 Suppressed unhandled promise rejection:', event.reason)
event.preventDefault()
}
})
// Handle global errors
window.addEventListener('error', (event) => {
if (event.message?.includes('isRefractionEnabled') ||
event.message?.includes('Cannot read properties of undefined')) {
console.warn('[GLTFLoaderPatch] 🔇 Suppressed global error:', event.message)
event.preventDefault()
}
})
}
}
/**
* Remove the patch (for testing purposes)
*/
static remove(): void {
if (this.isPatched) {
console.error = this.originalConsoleError
this.isPatched = false
console.log('[GLTFLoaderPatch] Patch removed')
}
}
}
export default GLTFLoaderPatch