28

I have started with ThreeJS's WebGL renderer after doing some "regular" WebGL with no additional libraries + GLSL shaders. I am trying to write custom shaders now in my ThreeJS program and I noticed that ThreeJS takes care of a lot of the standard stuff such as the projection and model / view matrices. My simple vertex shader now looks like this:

// All of these seem to be predefined:
// vec3 position;
// mat4 projectionMatrix;
// mat4 modelViewMatrix;
// mat3 normalMatrix;
// vec3 normal;

// I added this
varying vec3 vNormal;

void main() {
    vNormal = normalMatrix * vec3(normal);
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

My question is: Which other variables (I'm assuming they're uniforms) are predefined for vertex and fragment shaders that I could use? Does ThreeJS help out with light vectors / light color for instance (of course assuming I've added one or more lights to my ThreeJS scene)?

Update (Oct. 9, 2014): This question has been getting quite a few views, and the user Killah mentioned that the existing answers did not lead to a solution anymore with the current version of three.js. I added and accepted my own answer, see it below.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Grüse
  • 1,096
  • 2
  • 11
  • 24

4 Answers4

28

For uniforms, the short answer is the following:

In the vertex shader

"uniform mat4 modelMatrix;",
"uniform mat4 modelViewMatrix;",
"uniform mat4 projectionMatrix;",
"uniform mat4 viewMatrix;",
"uniform mat3 normalMatrix;",
"uniform vec3 cameraPosition;",

and in the fragment shader

"uniform mat4 viewMatrix;",
"uniform vec3 cameraPosition;",

For the complete answer, involving uniforms and attributes, your custom shaders have the string variables prefixVertex and prefixFragment pre-appended.

var vertexGlsl = prefixVertex + vertexShader;
var fragmentGlsl = prefixFragment + fragmentShader;

var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );

The prefixVertex and prefixFragment variable definitions can be found in WebGLProgram.js or in the non-minified version of three.js.

EDIT: Updated to three.js r.73

WestLangley
  • 102,557
  • 10
  • 276
  • 276
  • I'm sorry, the last section of your (very helpful) answer confuses me. What exactly do these variable definitions do? You list `var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader );` but I am not sure when we would need those. – Startec Nov 24 '15 at 05:45
11

The uniforms you can use in your shaders all depend on how you setup your material: have you enable lights ? vertex colors ? skinning ? ...

Three JS creates a program that depends heavily on some defines (#ifdef in the code) that are injected at the top of the program depending on the parameters I have spoken about above.

I found the best way to know what is going on is to print the shaders that three JS generates: as you already know GLSL, you will understand easily what the code means and what uniforms you can use. Look for buildProgram in three JS sources, then (r57):

var glFragmentShader = getShader( "fragment", prefix_fragment + fragmentShader );
var glVertexShader = getShader( "vertex", prefix_vertex + vertexShader );

After those lines, add:

console.log("fragment shader:", prefix_fragment + fragmentShader);
console.log("vertex shader:", prefix_vertex + vertexShader);

And you will be able to see the content of the shaders.

[EDIT] Rereading your question, I realize I answered a bit off, as you create your own shaders...

You can have a look at lines 6463 and 6490 of WebGLRenderer (https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L6463): you will see standard uniforms / attributes that three JS inject in your shaders. You can have a look to the Wiki where you have an entry about that (https://github.com/mrdoob/three.js/wiki - Which default attributes / uniforms / varyings are available in custom shaders?) but it directs you to the lines I outlined above.

Popov
  • 560
  • 5
  • 11
  • 1
    The link to the code is outdated, the lines have changed since the answer was given. – klh Mar 25 '14 at 11:30
  • @Killah I will research and update this question with another answer. Thanks for the heads-up – Grüse Jun 03 '14 at 13:34
7

An answer to this question can now be found in the three.js documentation: https://threejs.org/docs/#api/renderers/webgl/WebGLProgram

swiftcoder
  • 343
  • 1
  • 7
Kalisen
  • 467
  • 1
  • 5
  • 13
6

This question has been getting quite a few views, and the user Killah mentioned that the existing answers did not lead to a solution anymore with the current version of three.js. This is why I tried solving the problem again, and I'd like to outline a couple of options that I found:

  • The quickest and easiest way (while not very elegant) is to just put a random error in your shader. You will get a console error with the entire shader code, including everything that three.js adds.

  • The better solution is to output the shader source from where it's compiled, namely THREE.WebGLShader (as of the current three.js version, r68). I've done a quick copy and paste that should output all shader sources before they're compiled.

    Add this after including three.js and before your own code:

    THREE.WebGLShader = ( function () {
        var addLineNumbers = function ( string ) {
            var lines = string.split( '\n' );
            for ( var i = 0; i < lines.length; i ++ ) {
                lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];
            }
            return lines.join( '\n' );
        };
        return function ( gl, type, string ) {
            var shader = gl.createShader( type ); 
            console.log(string);
            gl.shaderSource( shader, string );
            gl.compileShader( shader );
            if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {
                console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' );
            }
            if ( gl.getShaderInfoLog( shader ) !== '' ) {
                console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', gl.getShaderInfoLog( shader ) );
                console.warn( addLineNumbers( string ) );
            }
            return shader;
        };
    } )();
    

    Note that this snippet is just copied (and very slightly changed) from the three.js sources and should be removed before actually using the code. Just for debugging!

  • There is one more option that is less invasive: you can inspect your ShaderMaterial after creating and rendering it at least once, like so:

    var material = new THREE.ShaderMaterial({
        uniforms: {
            uColorMap: { type: 't', value: THREE.ImageUtils.loadTexture('./img/_colors.png') },
            uSpecularMap: { type: 't', value: THREE.ImageUtils.loadTexture('./img/_spec.png') }, 
            uNormalMap: { type: 't', value: THREE.ImageUtils.loadTexture('./img/_normal.png') }
        },
        vertexShader: document.getElementById('vShader').innerText,
        fragmentShader: document.getElementById('fShader').innerText
    });
    

    Then, after rendering the object at least once:

    console.log(material.program.attributes);
    console.log(material.program.uniforms);
    

Hope this helps everyone! Feel free to add your comments if you know more and / or better ways to get your shader code.

Grüse
  • 1,096
  • 2
  • 11
  • 24