Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreeJS predefined shader attributes / uniforms

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.

like image 433
Grüse Avatar asked Mar 27 '13 16:03

Grüse


People also ask

What are uniforms in shader?

A uniform is a global Shader variable declared with the "uniform" storage qualifier. These act as parameters that the user of a shader program can pass to that program. Their values are stored in a program object.

Can uniform variables be declared in a fragment shader?

Fragment shader reads a value from the same variable, automatically interpolated to that fragment. No need to declare these in the Java program.

What is a shader attribute?

Attributes are GLSL variables which are only available to the vertex shader (as variables) and the JavaScript code. Attributes are typically used to store color information, texture coordinates, and any other data calculated or retrieved that needs to be shared between the JavaScript code and the vertex shader.

What is vertex and fragment shader?

The difference between vertex and fragment shaders is the process developed in the render pipeline. Vertex shaders could be define as the shader programs that modifies the geometry of the scene and made the 3D projection. Fragment shaders are related to the render window and define the color for each pixel.


3 Answers

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

like image 184
WestLangley Avatar answered Oct 19 '22 18:10

WestLangley


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.

like image 29
Popov Avatar answered Oct 19 '22 17:10

Popov


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.

like image 6
Grüse Avatar answered Oct 19 '22 18:10

Grüse