note export default function transpileShader ( source , targetGLSLVersion , isVertex ) { switch ( targetGLSLVersion ) { case 300 : return isVertex ? convertShader ( source , ES300_VERTEX_REPLACEMENTS ) : convertFragmentShaderTo300 ( source ) ; case 100 : return isVertex ? convertShader ( source , ES100_VERTEX_REPLACEMENTS ) : convertFragmentShaderTo100 ( source ) ; default : throw new Error ( `unknown GLSL version ${ targetGLSLVersion }` ) ; } } type GLSLReplacement = [ RegExp , string ] ; /** Simple regex replacements for GLSL ES 1.00 syntax that has changed in GLSL ES 3.00 */ const ES300_REPLACEMENTS = [ [ /^(#version[ \t]+(100|300[ \t]+es))?[ \t]*\n/ , '#version 300 es\n' ] , [ /\btexture(2D|2DProj|Cube)Lod(EXT)?\(/g , 'textureLod(' ] , [ /\btexture(2D|2DProj|Cube)(EXT)?\(/g , 'texture(' ] ] ; const ES300_VERTEX_REPLACEMENTS = [ ... ES300_REPLACEMENTS , [ makeVariableTextRegExp ( 'attribute' ) , 'in $1' ] , [ makeVariableTextRegExp ( 'varying' ) , 'out $1' ] ] ; /** Simple regex replacements for GLSL ES 1.00 syntax that has changed in GLSL ES 3.00 */ const ES300_FRAGMENT_REPLACEMENTS = [ ... ES300_REPLACEMENTS , [ makeVariableTextRegExp ( 'varying' ) , 'in $1' ] ] ; const ES100_REPLACEMENTS = [ [ /^#version[ \t]+300[ \t]+es/ , '#version 100' ] , [ /\btexture(2D|2DProj|Cube)Lod\(/g , 'texture$1LodEXT(' ] , [ /\btexture\(/g , 'texture2D(' ] , [ /\btextureLod\(/g , 'texture2DLodEXT(' ] ] ; const ES100_VERTEX_REPLACEMENTS = [ ... ES100_REPLACEMENTS , [ makeVariableTextRegExp ( 'in' ) , 'attribute $1' ] , [ makeVariableTextRegExp ( 'out' ) , 'varying $1' ] ] ; const ES100_FRAGMENT_REPLACEMENTS = [ ... ES100_REPLACEMENTS , [ makeVariableTextRegExp ( 'in' ) , 'varying $1' ] ] ; const ES100_FRAGMENT_OUTPUT_NAME = 'gl_FragColor' ; const ES300_FRAGMENT_OUTPUT_REGEX = /\bout[ \t]+vec4[ \t]+(\w+)[ \t]*;\n?/ ; const REGEX_START_OF_MAIN = /void\s+main\s*\([^)]*\)\s*\{\n?/ ; function convertShader ( source , replacements ) { for ( const [ pattern , replacement ] of replacements ) { source = source . replace ( pattern , replacement ) ; } return source ; } /** Transform fragment shader source code to GLSL 3.00 */ function convertFragmentShaderTo300 ( source ) { source = convertShader ( source , ES300_FRAGMENT_REPLACEMENTS ) ; const outputMatch = source . match ( ES300_FRAGMENT_OUTPUT_REGEX ) ; if ( outputMatch ) { const outputName = outputMatch [ 1 ] ; source = source . replace ( new RegExp ( `\\b${ ES100_FRAGMENT_OUTPUT_NAME }\\b` , 'g' ) , outputName ) ; } else { const outputName = 'fragmentColor' ; source = source . replace ( REGEX_START_OF_MAIN , ( match ) => `out vec4 ${ outputName };\n${ match }` ) . replace ( new RegExp ( `\\b${ ES100_FRAGMENT_OUTPUT_NAME }\\b` , 'g' ) , outputName ) ; } return source ; } /** Transform fragment shader source code to GLSL ES 100 */ function convertFragmentShaderTo100 ( source ) { source = convertShader ( source , ES100_FRAGMENT_REPLACEMENTS ) ; const outputMatch = source . match ( ES300_FRAGMENT_OUTPUT_REGEX ) ; if ( outputMatch ) { const outputName = outputMatch [ 1 ] ; source = source . replace ( ES300_FRAGMENT_OUTPUT_REGEX , '' ) . replace ( new RegExp ( `\\b${ outputName }\\b` , 'g' ) , ES100_FRAGMENT_OUTPUT_NAME ) ; } return source ; } example function makeVariableTextRegExp ( qualifier ) { return new RegExp ( `\\b${ qualifier }[ \\t]+(\\w+[ \\t]+\\w+(\\[\\w+\\])?;)` , 'g' ) ; }