0
votes

I'm learning WebGL shaders, but they confuse the living hell out of me. This is what I got so far:

<script type="x-shader/x-vertex" id="vertexshader">

    #ifdef GL_ES
        precision highp float;
    #endif

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

</script>

<script type="x-shader/x-fragment" id="fragmentshader">

    #ifdef GL_ES
        precision highp float;
    #endif

    void main()
    {
        gl_FragColor    = vec4(1.0, 0.0, 1.0, 1.0);
    }

</script>

So far so good, it compiles and I get a pink cube.

Now the confusion sets it. As far as I understood, fragment shaders are used to modify colors, vertex shader is used to modify shapes.

I don't understand does the gl_FragColor sets the color for the whole object or it's drawn in some kind of order where I could manipulate the cordinates in the shader so that it gets colored randomply for example?

If so, how does it know what shape and the coloring order?

In addition, why do I need to define vertexshader if I want to only use fragmentshader and what does the default gl_Position line does and why is it needed?

All the GLSL tutorials I tried so far, the code never works and three.js fails to compile it. Any suggestions where to start?

1
I'm no expert so I wont even try to explain the little that I know, but my experiments may help you.2pha
Also, This playlist on youtube has helped me a lot2pha
@2pha, thank you very much, your experiments are easy to follow!user3578847
Which tutorials have you read? Do these help?gman
really sweet series gman!pailhead

1 Answers

2
votes

This question is pretty broad.

Let's say you do something like this:

var myRenderer = new THREE.WebGLRenderer();
var myScene = new THREE.Scene();
var myTexture = new THREE.Texture();
var myColor = new THREE.Color();
var myMaterial = new THREE.MeshBasicMaterial({color:myColor, map:myTexture});
var myColoredAndTexturedCube = new THREE.Mesh( new THREE.CubeGeometry(), myMaterial); 
var myCamera = new THREE.Camera();

if you wire all this, you will get a cube rendered on your screen, and if you supply the color and texture, it will show both (texture tinted by the color).

A lot happens under the hood though. Three.js will issue instructions to the gpu via the WebGL API. These are very low level calls like 'take this chunk of memory and have it ready to be drawn' 'prepare this shader to process this chunk of memory' 'set blending mode for this call'.

I don't understand does the gl_FragColor sets the color for the whole object or it's drawn in some kind of order where I could manipulate the cordinates in the shader so that it gets colored randomply for example?

If so, how does it know what shape and the coloring order?

You should read a bit about the rendering pipeline, maybe you wont understand it all at first, but it can definitely clarify some things.

gl_FragColor sets the color for the pixel in a buffer (can be your screen, can be an offscreen texture). Yes, it sets the color for the 'entire object' but this entire object can be a particle cloud (which you could interpret as many objects). You can have a grid of 10x10 cubes, each colored differently, but still being rendered with one draw call (one object).

So going back to your shder:

//you dont see this, but three injects this for you, try intentionally adding a mistake to your shader, when your debugger complains, youll see the entire shader and these lines in it

uniform mat4 projectionMatrix; //one mat4 shared across all vertices/pixels
uniform mat4 modelViewMatrix; //one mat4 shared across all vertices/pixels
attribute vec3 position;  //actual vertex, this value is different in each vertex


//try adding this
varying vec2 vUv;

void main()
{

    vUv = uv;  //uv, just like position, is another attribute that gets created for you automatically, this way we are sending it to the pixel shader through the varying vec2 vUv.


    //this is the transformation 
    //projection matrix is what transforms space into perspective (vanishing points, things get smaller as they get further away from the camera)
    //modelViewMatrix are actually two matrices, viewMatrix, which is also part of the camera (how is this camera rotated and moved compared to the rest of the world)
    //finally the modelMatrix - how big is the object, where it stands, and how it's rotated

    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position , 1.0 ); //will do the same thing
}

Every material you make with three, has this part of the shader. This is not enough to do lighting for example, because it has no normals.

Try this fragment shader:

varying vec2 vUv; //coming in from the vertex shader

void main(){
    gl_FragColor = vec4( vUv , 0.0 , 1.0);
}

Or better yet, let's show the world position of the object in color:

vertex shader:

varying vec3 vertexWorldPosition; 

void main(){
    
    vec4 worldPosition = modelMatrix * vec4( position , 1.0 ); //compute the world position, remember it, 
    //model matrix is mat4 that transforms the object from object space to world space, vec4( vec3 , 1.0 ) creates a point rather than a direction in "homogeneous coordinates"

    //since we only need this to be vec4 for transformations and working with mat4, we save the vec3 portion of it to the varying variable
    vertexWorldPosition = worldPosition.xyz; // we don't need .w

    //do the rest of the transformation - what is this world space seen from the camera's point of view,
    gl_Position = viewMatrix * worldPosition;
    
    //we used gl_Position to write the previous result, we could have used a new vec4 cameraSpace (or eyeSpace, or viewSpace) but we can also write to gl_Position

    gl_Position = projectionMatrix * gl_Position; //apply perspective distortion
}

fragment shader:

varying vec3 vertexWorldPosition; //this comes in from the vertex shader
void main(){
    gl_FragColor = vec4( vertexWorldPosition , 1.0 );
}

if you create a sphere at 0,0,0 and dont move it, one half will be black, the other will be colored. Depending on the scale, it might be white. Lets say the radius is 100, you'll se a gradient going from 0 to 1, and the rest will be white (or r,g,b, clamped to 1.0). Then try something like this

 gl_FragColor = vec4( vec3( sin( vertexWorldPosition.x ) ), 1.0 );