@@ -25,9 +25,38 @@ class Shader {
2525 } ) ;
2626 }
2727
28+ static #resolveAssetUrl( relativePath ) {
29+ // GitHub Pages is typically hosted under `https://<user>.github.io/<repo>/`.
30+ // If the page is accessed without a trailing slash (e.g. `/repo`), the browser
31+ // resolves relative paths against `/`, causing 404s for `shaders/...`.
32+ // Normalize the base URL so assets stay relative to the project root.
33+ let base = window . location . href ;
34+ const url = new URL ( base ) ;
35+ const pathname = url . pathname ;
36+ const lastSegment = pathname . substring ( pathname . lastIndexOf ( "/" ) + 1 ) ;
37+ const looksLikeFile = lastSegment . includes ( "." ) ;
38+ if ( ! pathname . endsWith ( "/" ) && ! looksLikeFile ) {
39+ url . pathname = pathname + "/" ;
40+ url . search = "" ;
41+ url . hash = "" ;
42+ base = url . toString ( ) ;
43+ }
44+
45+ return new URL ( relativePath , base ) . toString ( ) ;
46+ }
47+
2848 async #loadShader( shader , name ) {
29- const program = await d3 . text ( "shaders/" + name + ".essl" ) ;
30- this . material [ shader ] = program ;
49+ const shaderUrl = Shader . #resolveAssetUrl( `shaders/${ name } .essl` ) ;
50+ try {
51+ const program = await d3 . text ( shaderUrl ) ;
52+ this . material [ shader ] = program ;
53+ } catch ( e ) {
54+ console . error (
55+ `Failed to load shader '${ name } .essl' from '${ shaderUrl } '. Current page URL is '${ window . location . href } '.` ,
56+ e
57+ ) ;
58+ throw e ;
59+ }
3160 }
3261
3362 // this function has to be explicitly called after the constructor from another async function like that:
@@ -51,4 +80,4 @@ class Shader {
5180 this . material . uniforms [ key ] = new THREE . Uniform ( value ) ;
5281 }
5382 }
54- }
83+ }
0 commit comments