Search code examples
three.jsshaderconsole.logfragment-shadervertex-shader

How can I see the whole shader's text content, with all the prepended code by Three.js, when using ShaderMaterial or RawShaderMaterial (r148)?


I know that Three.js prepends the shader text you provide with some code that includes definitions etc. I am not a big fan of "magic" and I would like to be able to see the final text content of the shader(s) that Three.js actually uses.

I have seen that in earlier releases of Three.js, if there was en error in a shader file (either the vertex shader of the fragment shader), the whole text content of the shader was logged in console, including all the prepended code by Three.js.

I am currently using r148 and in case of an error, only the line(s) containing the error(s) gets logged in console.

Has the functionality of logging the whole shader file in console been removed or is there any way to enable it? Or, more generally, is there any other way to access the actual text content of each of the shaders?


Solution

  • Had precisely the same issue when they made that change. I have written this small script to output the entire Three.js shader code:

    var Utils = {
        
        init: function(renderer, scene, camera, object) {
            renderer.compile(scene, camera); // Might be unnecessary, the shaders could already be compiled
    
            window.addEventListener('keydown', function(event) {
                if (event.keyCode == 70) { // F
                    Utils.findAndOutputShader(renderer, object, 'fragmentShader', 'Fragment Shader');
                }
                if (event.keyCode == 86) { // V
                    Utils.findAndOutputShader(renderer, object, 'vertexShader', 'Vertex Shader');
                }
            }, false);
            
        },
        
        findAndOutputShader: function(renderer, object, shaderIdentifier, shaderName) {
            var programs = renderer.properties.get(object.material).programs;
            for (var program of programs) {
                Utils.outputShader(" ----" + shaderName + " Code----", renderer.getContext().getShaderSource(program[1][shaderIdentifier]));
            }
        },
        
        outputShader: function(prefix, code) {
            code = code.replaceAll("\t", "  ");
            var lines = code.split("\n");
            var linedCode = "";
            var i = 1;
            for (var line of lines) {
                linedCode += (i < 10 ? " " : "") + i + ":\t\t" + line + "\n";
                i++;
            }
            
            var output = prefix + "\n" + linedCode;
            console.log(output);
        }
    }
    

    I call it once, after the draw loop has been started:

    Utils.init(renderer, scene, camera, sphere);
    

    Where sphere is the THREE.Mesh object whose shader code I want to see. Then I can press F to display the fragment shader and V to display the vertex shader.

    It has worked for me with r145 and r150. But note that it relies on the internal structure of Three.js, so if they change that, this would need to be updated. Still. I hope it will help you or anyone else who want to see the variables and other prepended functionality they have at their disposal for Three.js shader programming.


    As of r151 it is possible to add an onShaderError callback to the renderer.debug, which allows to restore easily the previous functionality of displaying the full shader. In other words, the following would output the entire shader code upon a compilation error:

    renderer.debug.onShaderError  = function(gl, program, vs, fs) {
    
        const parseForErrors = function(gl, shader, name) {
            const errors = gl.getShaderInfoLog(shader).trim();
            const prefix = "Errors in " + name + ":" + "\n\n" + errors;
            
            if (errors !== "") {
                const code = gl.getShaderSource(shader).replaceAll("\t", "  ");
                const lines = code.split("\n");
                var linedCode = "";
                var i = 1;
                for (var line of lines) {
                    linedCode += (i < 10 ? " " : "") + i + ":\t\t" + line + "\n";
                    i++;
                }
                
                console.error(prefix + "\n" + linedCode);
            }
        }
        
        parseForErrors(gl, vs, 'Vertex Shader');
        parseForErrors(gl, fs, 'Fragment Shader');
    };
    

    This is a bit better solution than the one before, as it no longer relies on the inner workings of Three.js and is also better scoped. You do have to make an intentional typo in the shader code to see it. But that might also be a plus as you can work with the shader directly, compared to the previous example, where you need to reference a particular object/mesh.