Debugging basic lighting for a cube in openGL - c++11

I was trying to add some basic lighting effects to an animating cube but for some reason it just displays a blank colored window. I'm totally lost in finding the problem. The code was working fine before adding the lighting. The first thing that I did to find the problem is to add the
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
to the end of fragment shader in order to avoid all the lighting calculations that might go wrong. But it just changed the color shown in the window. I'm not sure if this problem is because of camera settings or calculating normals or maybe other factors. Here is the obj file data that I'm using to load the cube:
v -1.0 -1.0 1.0
v 1.0 -1.0 1.0
v 1.0 1.0 1.0
v -1.0 1.0 1.0
v -1.0 -1.0 -1.0
v 1.0 -1.0 -1.0
v 1.0 1.0 -1.0
v -1.0 1.0 -1.0
vn 0.577350 0.577350 -0.577350
vn 0.577350 -0.577350 -0.577350
vn -0.577350 -0.577350 -0.577350
vn -0.577350 0.577350 -0.577350
vn 0.577350 0.577350 0.577350
vn 0.577350 -0.577350 0.577350
vn -0.577350 -0.577350 0.577350
vn -0.577350 0.577350 0.577350
f 0 1 2
f 2 3 0
f 1 5 6
f 6 2 1
f 7 6 5
f 5 4 7
f 4 0 3
f 3 7 4
f 4 5 1
f 1 0 4
f 3 2 6
f 6 7 3
This is the camera setting and matrix calculations:
float move = -4.0f;// sinf(SDL_GetTicks() / 1000.0 * (2 * 3.14) / 5); // -1<->+1 every 5 seconds
float angle = 0.0;// SDL_GetTicks() / 1000.0 * 45; // 45° per second
glm::vec3 axis_y(0, 1, 0);
glm::mat4 translation = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, move));
glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), glm::radians(angle), axis_y);
glm::mat4 scaling = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, 1.0f));
glm::mat4 model = translation * rotation * scaling;
glm::mat4 view = glm::lookAt(glm::vec3(0.0, 2.0, 0.0), glm::vec3(0.0, 0.0, -4.0), glm::vec3(0.0, 1.0, 0.0));
glm::mat4 projection = glm::perspective(45.0f, 1.0f*screen_width / screen_height, 0.1f, 10.0f);
glm::mat4 mvp = projection * view * model;
glm::mat4 mv = view * model;
glm::mat4 normalMatrix = glm::transpose(glm::inverse(mv));
glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, glm::value_ptr(mv));
glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, glm::value_ptr(normalMatrix));
and the vertex shader includes:
gl_Position = mvp * vec4(positions, fade);
I have defined the lighting properties as follows:
float shininess = 5.0f;
glm::vec3 lightDirection = glm::vec3(0.0f, 1.0f, 2.0f);
glm::vec4 lightAmbient = glm::vec4(1, 0.83, 0.75, 1.0f);
glm::vec4 lightDiffuse = glm::vec4(1.0f, 1.0f, 0.4f, 1.0f);
glm::vec4 lightSpecular = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
glm::vec4 materialAmbient = glm::vec4(1, 0.83, 0.75, 1.0f);
glm::vec4 materialDiffuse = glm::vec4(1.0f, 1.0f, 0.4f, 1.0f);
glm::vec4 materialSpecular = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
glUniform1f(uniform_shininess, shininess);
glUniform3fv(uniform_lightDirection, 1, glm::value_ptr(lightDirection));
glUniform4fv(uniform_lightAmbient, 1, glm::value_ptr(lightAmbient));
glUniform4fv(uniform_lightDiffuse, 1, glm::value_ptr(lightDiffuse));
glUniform4fv(uniform_lightSpecular, 1, glm::value_ptr(lightSpecular));
glUniform4fv(uniform_materialAmbient, 1, glm::value_ptr(materialAmbient));
glUniform4fv(uniform_materialDiffuse, 1, glm::value_ptr(materialDiffuse));
glUniform4fv(uniform_materialSpecular, 1, glm::value_ptr(materialSpecular));
and fragment shader is:
uniform float shininess;
uniform vec3 lightDirection;
uniform vec4 lightAmbient;
uniform vec4 lightDiffuse;
uniform vec4 lightSpecular;
uniform vec4 materialAmbient;
uniform vec4 materialDiffuse;
uniform vec4 materialSpecular;
varying vec3 vNormal;
varying vec3 vEyeVec;
void main(void){
vec4 Ia = lightAmbient * materialAmbient;
vec4 Id = vec4(0.0,0.0,0.0,1.0);
vec4 Is = vec4(0.0,0.0,0.0,1.0);
vec3 L = normalize(lightDirection);
vec3 N = normalize(vNormal);
float lambertTerm = dot(N,-L);
if(lambertTerm > 0.0){
Id = lightDiffuse * materialDiffuse * lambertTerm;
vec3 E = normalize(vEyeVec);
vec3 R = reflect(L, N);
float specular = pow( max(dot(R, E), 0.0), shininess);
Is = lightSpecular * materialSpecular * specular;
}
vec4 finalColor = Ia + Id + Is;
finalColor.a = 1.0;
gl_FragColor = finalColor;
//gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
}
The two varyings are coming from vertex shader:
vNormal = vec3(normalMatrix * vec4(normals, 1.0));
vec4 vertex = mv * vec4(positions, 1.0);
vEyeVec = -vec3(vertex.xyz);
What I was guessing is that if the normals and lighting calculations go wrong then the last line in the fragment shader gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); should cancel them. However, that didn't make any help. I tried different camera positions but that didn't make any difference.

Related

Threejs: Sphere geometry not being shaded properly GLSL

I'm having a problem with a GLSL shader that interpolates color in 3D space, and assigns it based on the 3D coordinates of the bounding box and I can't seem to fix it:
The stamen in this codepen: https://codepen.io/ricky1280/pen/BaxyaZY
this is the code that I feel like probably has the problem, the geometry of the sphere:
const stamenEndCap = new THREE.SphereGeometry( sinCurveScale/120, 20, 20 );
// stamenEndCap.scale(1,1.5,1)
stamenEndCap.scale(4,1,1) //find a way to rotate geometry relative to the sin curve at the end
stamenEndCap.toNonIndexed();
stamenEndCap.computeBoundingSphere();
stamenEndCap.computeBoundingBox();
stamenEndCap.normalizeNormals();
stamenEndCap.computeTangents();
console.log(stamenEndCap.attributes.position.array)
for (var i=0; i<stamenEndCap.attributes.position.array.length; i=i+3){
stamenEndCap.attributes.position.array[i]=stamenEndCap.attributes.position.array[i]+((centerEnd.x)) //offset
stamenEndCap.attributes.position.array[i+1]=stamenEndCap.attributes.position.array[i+1]+((centerEnd.y))
stamenEndCap.attributes.position.array[i+2]=stamenEndCap.attributes.position.array[i+2]+((centerEnd.z)) //height?
}
stamenEndCap.computeVertexNormals();
// let positionVector = new THREE.Vector3(spherePoint.x,spherePoint.y,spherePoint.z)
// console.log(positionVector)
stamenEndCap.attributes.position.needsUpdate = true;
console.log(stamenEndCap.attributes.position.array)
let merge = THREE.BufferGeometryUtils.mergeBufferGeometries([geometry2,stamenEndCap])
merge.attributes.position.needsUpdate = true;
It is shaded improperly, it looks like this:
The color harshly changes from white to that light blue color on the vertical axis, even though the stamen end cap (line 364 of the codepen) is merged with the tube geometry and the shader is calculated across the 3D space of the entire merged object. The geometry becomes "merge" on line 394, and then "stamenGeom" on line 400. Then its boundingbox is used in the vertex and fragment shaders that exist on lines 422-552.
I'm not sure how to shade this properly so that it transitions smoothly, without the line denoting the change in color from white-blue. It doesn't seem to respond to normals, unfortunately.
Viewing the stamen from plan (top-down?) shows that the color is transitioning properly, but viewed from the side it appears as the image.
If anyone has any advice or solutions please let me know, and thank you for reading all of this.
figured it out: in the shader code the colors weren't being blended properly.
previous fragment shader code:
`vec4 diffuseColor = vec4( diffuse, opacity );`,
`
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
float f = clamp((vPos.z - bbMin.z) / (bbMax.z - bbMin.z)+vertOffset, 0., 1.);
// + is slider for vertical color position, -1 to 1
float linear_modifier = (1.00 * abs(1.) * f);
//vertical gradient position!!
//moves from 0-10?
vec3 col = mix(color1, color2, linear_modifier);
//float f2 = clamp((vPos.x - bbMin.x) / (bbMax.x - bbMin.x), 0., 1.);
float f2 = clamp(vUv.x, 0., 1.);
vec2 pos_ndc = vPos.xy*centerSize2;
float dist = length(pos_ndc*centerSize);
//controls central gradient position!
//the lower the larger?
//0-20
// float linear_modifier2 = (1.00 * abs(sin(1.0)) * dist);
//col = mix(color3, col, dist);
//NOT USING DIST REMOVES VERTICAL CENTRAL GRADIENT
// vec4 diffuseColor = vec4( col, opacity );
float f3 = clamp(vUv.x+f3Offset, 0., 1.);
// ^ THIS controls brightness of lowlights. lower the more intense.
col = mix(color3, col, f3);
//not using this removes LOWLIGHTS
//f3 is subtle fade
//col = mix(color3, col, f3);
//col = mix(color3, col, f2);
//f2 is default
vec4 diffuseColor = vec4( col, opacity );`
fixed shader code:
`vec4 diffuseColor = vec4( diffuse, opacity );`,
`
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
float f = clamp((vPos.z - bbMin.z) / (bbMax.z - bbMin.z)+vertOffset, 0., 1.);
// + is slider for vertical color position, -1 to 1
float linear_modifier = (1.00 * abs(1.) * f);
//vertical gradient position!!
//moves from 0-10?
vec3 col = mix(color1, color2, linear_modifier);
float f2 = clamp((vPos.x - bbMin.x) / (bbMax.x - bbMin.x), 0., 1.);
//float f2 = clamp(vUv.x, 0., 1.);
vec2 pos_ndc = vPos.xy*centerSize2;
float dist = length(pos_ndc*centerSize);
//controls central gradient position!
//the lower the larger?
//0-20
// float linear_modifier2 = (1.00 * abs(sin(1.0)) * dist);
//col = mix(color3, col, dist);
//NOT USING DIST REMOVES VERTICAL CENTRAL GRADIENT
// vec4 diffuseColor = vec4( col, opacity );
float f3 = clamp(vUv.x+f3Offset, 0., 1.);
// ^ THIS controls brightness of lowlights. lower the more intense.
//col = mix(color3, col, f3);
//not using this removes LOWLIGHTS
//f3 is subtle fade
//col = mix(color3, col, f3);
//col = mix(color3, col, f2);
//f2 is default
vec4 diffuseColor = vec4( col, opacity );
`

Why am I seeing these blending artifacts in my GLSL shader?

I'm attempting to create a shader that additively blends colored "blobs" (kind of like particles) on top of one another. This seems like it should be a straightforward task but I'm getting strange "banding"-like artifacts when the blobs blend.
First off, here's the behavior I'm after (replicated using Photoshop layers):
Note that the three color layers are all set to blendmode "Linear Dodge (Add)" which as far as I understand is Photoshop's "additive" blend mode.
If I merge the color layers and leave the resulting layer set to "Normal" blending, I'm then free to change the background color as I please.
Obviously additive blending will not work on top of a non-black background, so in the end I will also want/need the shader to support this pre-merging of colors before finally blending into a background that could have any color. However, I'm content for now to only focus on getting the additive-on-top-of-black blending working correctly, because it's not.
Here's my shader code in its current state.
const int MAX_SHAPES = 10;
vec2 spread = vec2(0.3, 0.3);
vec2 offset = vec2(0.0, 0.0);
float shapeSize = 0.3;
const float s = 1.0;
float shapeColors[MAX_SHAPES * 3] = float[MAX_SHAPES * 3] (
s, 0.0, 0.0,
0.0, s, 0.0,
0.0, 0.0, s,
s, 0.0, 0.0,
s, 0.0, 0.0,
s, 0.0, 0.0,
s, 0.0, 0.0,
s, 0.0, 0.0,
s, 0.0, 0.0,
s, 0.0, 0.0
);
vec2 motionFunction (float i) {
float t = iTime;
return vec2(
(cos(t * 0.31 + i * 3.0) + cos(t * 0.11 + i * 14.0) + cos(t * 0.78 + i * 30.0) + cos(t * 0.55 + i * 10.0)) / 4.0,
(cos(t * 0.13 + i * 33.0) + cos(t * 0.66 + i * 38.0) + cos(t * 0.42 + i * 83.0) + cos(t * 0.9 + i * 29.0)) / 4.0
);
}
float blend (float src, float dst, float alpha) {
return alpha * src + (1.0 - alpha) * dst;
}
void mainImage (out vec4 fragColor, in vec2 fragCoord) {
float aspect = iResolution.x / iResolution.y;
float x = (fragCoord.x / iResolution.x) - 0.5;
float y = (fragCoord.y / iResolution.y) - 0.5;
vec2 pixel = vec2(x, y / aspect);
vec4 totalColor = vec4(0.0, 0.0, 0.0, 0.0);
for (int i = 0; i < MAX_SHAPES; i++) {
if (i >= 3) {
break;
}
vec2 shapeCenter = motionFunction(float(i));
shapeCenter *= spread;
shapeCenter += offset;
float dx = shapeCenter.x - pixel.x;
float dy = shapeCenter.y - pixel.y;
float d = sqrt(dx * dx + dy * dy);
float ratio = d / shapeSize;
float intensity = 1.0 - clamp(ratio, 0.0, 1.0);
totalColor.x = totalColor.x + shapeColors[i * 3 + 0] * intensity;
totalColor.y = totalColor.y + shapeColors[i * 3 + 1] * intensity;
totalColor.z = totalColor.z + shapeColors[i * 3 + 2] * intensity;
totalColor.w = totalColor.w + intensity;
}
float alpha = clamp(totalColor.w, 0.0, 1.0);
float background = 0.0;
fragColor = vec4(
blend(totalColor.x, background, alpha),
blend(totalColor.y, background, alpha),
blend(totalColor.z, background, alpha),
1.0
);
}
And here's a ShaderToy version where you can view it live — https://www.shadertoy.com/view/wlf3RM
Or as a video — https://streamable.com/un25t
The visual artifacts should be pretty obvious, but here's a video that points them out: https://streamable.com/kxaps
(I think they are way more prevalent in the video linked before this one, though. The motion really make them pop out.)
Also as a static image for comparison:
Basically, there are "edges" that appear on certain magical thresholds. I have no idea how they got there or how to get rid of them. Your help would be highly appreciated.
The inside lines are where totalColor.w reaches 1 and so alpha is clamped to 1 inside them. The outside ones that you've traced in white are the edges of the circles.
I modified your ShaderToy link by changing float alpha = clamp(totalColor.w, 0.0, 1.0); to float alpha = 1.0; and float intensity = 1.0 - clamp(ratio, 0.0, 1.0); to float intensity = smoothstep(1.0, 0.0, ratio); (to smooth out the edges of the circles) and now it looks like the first picture.

cocos2dx shader rotate a shape in fragment shader

This problem is cocos2d-x related since I am using cocos2d-x as game engine but I can think it can be solved use basic opengl shader knowledge.
Part 1:
. I have a canvas size of 800 * 600
. I try to draw a simple colored square in size of 96 * 96 which is placed in the middle of the canvas
It is quite simple, the draw part code :
var boundingBox = this.getBoundingBox();
var squareVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
var vertices = [
boundingBox.width, boundingBox.height,
0, boundingBox.height,
boundingBox.width, 0,
0, 0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION);
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
And the vert shader:
attribute vec4 a_position;
void main()
{
gl_Position = CC_PMatrix * CC_MVMatrix * a_position;
}
And the frag shader:
#ifdef GL_ES
precision highp float;
#endif
uniform vec2 center;
uniform vec2 resolution;
uniform float rotation;
void main()
{
vec4 RED = vec4(1.0, 0.0, 0.0, 1.0);
vec4 GREEN = vec4(0.0, 1.0, 0.0, 1.0);
gl_FragColor = GREEN;
}
And everything works fine :
The grid line is size of 32 * 32, and the black dot indicates the center of the canvas.
Part 2:
. I try to separate the square into half (vertically)
. The left part is green and the right part is red
I changed the frag shader to get it done :
void main()
{
vec4 RED = vec4(1.0, 0.0, 0.0, 1.0);
vec4 GREEN = vec4(0.0, 1.0, 0.0, 1.0);
/*
x => [0, 1]
y => [0, 1]
*/
vec2 UV = (rotatedFragCoord.xy - center.xy + resolution.xy / 2.0) / resolution.xy;
/*
x => [-1, 1]
y => [-1, 1]
*/
vec2 POS = -1.0 + 2.0 * UV;
if (POS.x <= 0.0) {
gl_FragColor = GREEN;
}
else {
gl_FragColor = RED;
}
}
The uniform 'center' is the position of the square so it is 400, 300 in this case.
The uniform 'resolution' is the content size of the square so the value is 96, 96.
The result is fine :
Part 3:
. I try to change the rotation in cocos2dx style
myShaderNode.setRotation(45);
And the square is rotated but the content is not :
So I tried to rotate the content according to the rotation angle of the node.
I changed the frag shader again:
void main()
{
vec4 RED = vec4(1.0, 0.0, 0.0, 1.0);
vec4 GREEN = vec4(0.0, 1.0, 0.0, 1.0);
vec2 rotatedFragCoord = gl_FragCoord.xy - center.xy;
float cosa = cos(rotation);
float sina = sin(rotation);
float t = rotatedFragCoord.x;
rotatedFragCoord.x = t * cosa - rotatedFragCoord.y * sina + center.x;
rotatedFragCoord.y = t * sina + rotatedFragCoord.y * cosa + center.y;
/*
x => [0, 1]
y => [0, 1]
*/
vec2 UV = (rotatedFragCoord.xy - center.xy + resolution.xy / 2.0) / resolution.xy;
/*
x => [-1, 1]
y => [-1, 1]
*/
vec2 POS = -1.0 + 2.0 * UV;
if (POS.x <= 0.0) {
gl_FragColor = GREEN;
}
else {
gl_FragColor = RED;
}
}
The uniform rotation is the angle the node rotated so in this case it is 45.
The result is close to what I want but still not right:
I tried hard but just can not figure out what is wrong in my code and what's more if there is anyway easier to get things done.
I am quite new to shader programming and any advice will be appreciated, thanks :)

QT OpenGL is drawing my Vector to infinity

I want to create a simple Cube of Lines in OpenGL and QT 5.5.
The Problem is, if I´m drawing a simple line to another Z-coordinate (from z -1.0f to z 1.0f), the line will look like it goes to z=infinity.
Vertex stackVerts[] = {
{QVector3D(-1.0f, -1.0f, 1.0f), // 0
QVector3D(+1.0f, +0.0f, +0.0f)}, // Colour
{QVector3D(1.0f, -1.0f, 1.0f), // 1
QVector3D(+0.0f, +1.0f, +0.0f)}, // Colour
{QVector3D(-1.0f, 1.0f, 1.0f), // 2
QVector3D(+0.0f, +0.0f, +1.0f)}, // Colour
{QVector3D(1.0f, 1.0f, 1.0f), // 3
QVector3D(+1.0f, +1.0f, +1.0f)}, // Colour
{QVector3D(-1.0f, -1.0f, -1.0f), // 4
QVector3D(+1.0f, +0.0f, +0.0f)}, // Colour
{QVector3D(1.0f, -1.0f, -1.0f), // 5
QVector3D(+0.0f, +1.0f, +0.0f)}, // Colour
{QVector3D(-1.0f, 1.0f, -1.0f), // 6
QVector3D(+0.0f, +0.0f, +1.0f)}, // Colour
{QVector3D(1.0f, 1.0f, -1.0f), // 7
QVector3D(+1.0f, +1.0f, +1.0f)}, // Colour
};
GLushort stackIndices[] = {
0,1,
1,3,
3,2,
2,0,
0,4,
};
...
glDrawElements(GL_LINES, 10, GL_UNSIGNED_SHORT, 0);
The Matrix is set a Uniform:
_transMatrix.setToIdentity();
_transMatrix.perspective(60.0f, (float)(width()/height()), 0.1f, 100.0f);
_transMatrix.translate(0, 0, -3);
_transMatrix.rotate(64,0,1,0);
_program.setUniformValue("transMatrix", _transMatrix);
The Vertex.glsl:
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
attribute vec3 a_position;
attribute vec3 a_color;
uniform mat4 transMatrix;
varying vec3 theColor;
void main()
{
vec4 v = vec4 (a_position, 1.0);
gl_Position = v * transMatrix;
theColor = a_color;
}
The GL_DEPTH_TEST is enabled and if i change the translation of z to -1000, it will still go to infinity.
Translate z=-3
Translate z=-1000

GLSL Shadows with Perlin Noise

So I've recently gotten into using WebGL and more specifically writing GLSL Shaders and I have run into a snag while writing the fragment shader for my "water" shader which is derived from this tutorial.
What I'm trying to achieve is a stepped shading (Toon shading, cell shading...) effect on waves generated by my vertex shader but the fragment shader seems to treat the waves as though they are still a flat plane and the entire mesh is drawn as one solid color.
What am I missing here? The sphere works perfectly but flat surfaces are all shaded uniformly. I have the same problem if I use a cube. Each face on the cube is shaded independently but the entire face is given a solid color.
The Scene
This is how I have my test scene set up. I have two meshes using the same material - a sphere and a plane and a light source.
The Problem
As you can see the shader is working as expected on the sphere.
I enabled wireframe for this shot to show that the vertex shader (perlin noise) is working beautifully on the plane.
But when I turn the wireframe off you can see that the fragment shader seems to be receiving the same level of light uniformly across the entire plane creating this...
Rotating the plane to face the light source will change the color of the material but again the color is applied uniformly over the entire surface of the plane.
The Fragment Shader
In all it's script kid glory lol.
uniform vec3 uMaterialColor;
uniform vec3 uDirLightPos;
uniform vec3 uDirLightColor;
uniform float uKd;
uniform float uBorder;
varying vec3 vNormal;
varying vec3 vViewPosition;
void main() {
vec4 color;
// compute direction to light
vec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 );
vec3 lVector = normalize( lDirection.xyz );
// N * L. Normal must be normalized, since it's interpolated.
vec3 normal = normalize( vNormal );
// check the diffuse dot product against uBorder and adjust
// this diffuse value accordingly.
float diffuse = max( dot( normal, lVector ), 0.0);
if (diffuse > 0.95)
color = vec4(1.0,0.0,0.0,1.0);
else if (diffuse > 0.85)
color = vec4(0.9,0.0,0.0,1.0);
else if (diffuse > 0.75)
color = vec4(0.8,0.0,0.0,1.0);
else if (diffuse > 0.65)
color = vec4(0.7,0.0,0.0,1.0);
else if (diffuse > 0.55)
color = vec4(0.6,0.0,0.0,1.0);
else if (diffuse > 0.45)
color = vec4(0.5,0.0,0.0,1.0);
else if (diffuse > 0.35)
color = vec4(0.4,0.0,0.0,1.0);
else if (diffuse > 0.25)
color = vec4(0.3,0.0,0.0,1.0);
else if (diffuse > 0.15)
color = vec4(0.2,0.0,0.0,1.0);
else if (diffuse > 0.05)
color = vec4(0.1,0.0,0.0,1.0);
else
color = vec4(0.05,0.0,0.0,1.0);
gl_FragColor = color;
The Vertex Shader
vec3 mod289(vec3 x)
{
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x)
{
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x)
{
return mod289(((x*34.0)+1.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
vec3 fade(vec3 t) {
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
// Classic Perlin noise
float cnoise(vec3 P)
{
vec3 Pi0 = floor(P); // Integer part for indexing
vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
Pi0 = mod289(Pi0);
Pi1 = mod289(Pi1);
vec3 Pf0 = fract(P); // Fractional part for interpolation
vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
vec4 iy = vec4(Pi0.yy, Pi1.yy);
vec4 iz0 = Pi0.zzzz;
vec4 iz1 = Pi1.zzzz;
vec4 ixy = permute(permute(ix) + iy);
vec4 ixy0 = permute(ixy + iz0);
vec4 ixy1 = permute(ixy + iz1);
vec4 gx0 = ixy0 * (1.0 / 7.0);
vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
gx0 = fract(gx0);
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
vec4 sz0 = step(gz0, vec4(0.0));
gx0 -= sz0 * (step(0.0, gx0) - 0.5);
gy0 -= sz0 * (step(0.0, gy0) - 0.5);
vec4 gx1 = ixy1 * (1.0 / 7.0);
vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
gx1 = fract(gx1);
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
vec4 sz1 = step(gz1, vec4(0.0));
gx1 -= sz1 * (step(0.0, gx1) - 0.5);
gy1 -= sz1 * (step(0.0, gy1) - 0.5);
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
g000 *= norm0.x;
g010 *= norm0.y;
g100 *= norm0.z;
g110 *= norm0.w;
vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
g001 *= norm1.x;
g011 *= norm1.y;
g101 *= norm1.z;
g111 *= norm1.w;
float n000 = dot(g000, Pf0);
float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
float n111 = dot(g111, Pf1);
vec3 fade_xyz = fade(Pf0);
vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
return 2.2 * n_xyz;
}
// Classic Perlin noise, periodic variant
float pnoise(vec3 P, vec3 rep)
{
vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period
vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period
Pi0 = mod289(Pi0);
Pi1 = mod289(Pi1);
vec3 Pf0 = fract(P); // Fractional part for interpolation
vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
vec4 iy = vec4(Pi0.yy, Pi1.yy);
vec4 iz0 = Pi0.zzzz;
vec4 iz1 = Pi1.zzzz;
vec4 ixy = permute(permute(ix) + iy);
vec4 ixy0 = permute(ixy + iz0);
vec4 ixy1 = permute(ixy + iz1);
vec4 gx0 = ixy0 * (1.0 / 7.0);
vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
gx0 = fract(gx0);
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
vec4 sz0 = step(gz0, vec4(0.0));
gx0 -= sz0 * (step(0.0, gx0) - 0.5);
gy0 -= sz0 * (step(0.0, gy0) - 0.5);
vec4 gx1 = ixy1 * (1.0 / 7.0);
vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
gx1 = fract(gx1);
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
vec4 sz1 = step(gz1, vec4(0.0));
gx1 -= sz1 * (step(0.0, gx1) - 0.5);
gy1 -= sz1 * (step(0.0, gy1) - 0.5);
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
g000 *= norm0.x;
g010 *= norm0.y;
g100 *= norm0.z;
g110 *= norm0.w;
vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
g001 *= norm1.x;
g011 *= norm1.y;
g101 *= norm1.z;
g111 *= norm1.w;
float n000 = dot(g000, Pf0);
float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
float n111 = dot(g111, Pf1);
vec3 fade_xyz = fade(Pf0);
vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
return 2.2 * n_xyz;
}
varying vec2 vUv;
varying float noise;
uniform float time;
// for the cell shader
varying vec3 vNormal;
varying vec3 vViewPosition;
float turbulence( vec3 p ) {
float w = 100.0;
float t = -.5;
for (float f = 1.0 ; f <= 10.0 ; f++ ){
float power = pow( 2.0, f );
t += abs( pnoise( vec3( power * p ), vec3( 10.0, 10.0, 10.0 ) ) / power );
}
return t;
}
varying vec3 vertexWorldPos;
void main() {
vUv = uv;
// add time to the noise parameters so it's animated
noise = 10.0 * -.10 * turbulence( .5 * normal + time );
float b = 25.0 * pnoise( 0.05 * position + vec3( 2.0 * time ), vec3( 100.0 ) );
float displacement = - 10. - noise + b;
vec3 newPosition = position + normal * displacement;
gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
// for the cell shader effect
vNormal = normalize( normalMatrix * normal );
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
vViewPosition = -mvPosition.xyz;
}
Worth Mention
I am using the Three.js library
My light source is an instance of THREE.SpotLight
First of all, shadows are completely different. Your problem here is a lack of change in the per-vertex normal after displacement. Correcting this is not going to get you shadows, but your lighting will at least vary across your displaced geometry.
If you have access to partial derivatives, you can do this in the fragment shader. Otherwise, you are kind of out of luck in GL ES, due to a lack of vertex adjacency information. You could also compute per-face normals with a Geometry Shader, but that is not an option in WebGL.
This should be all of the necessary changes to implement this, note that it requires partial derivative support (optional extension in OpenGL ES 2.0).
Vertex Shader:
varying vec3 vertexViewPos; // NEW
void main() {
...
vec3 newPosition = position + normal * displacement;
vertexViewPos = (modelViewMatrix * vec4 (newPosition, 1.0)).xyz; // NEW
...
}
Fragment Shader:
#extension GL_OES_standard_derivatives : require
uniform vec3 uMaterialColor;
uniform vec3 uDirLightPos;
uniform vec3 uDirLightColor;
uniform float uKd;
uniform float uBorder;
varying vec3 vNormal;
varying vec3 vViewPosition;
varying vec3 vertexViewPos; // NEW
void main() {
vec4 color;
// compute direction to light
vec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 );
vec3 lVector = normalize( lDirection.xyz );
// N * L. Normal must be normalized, since it's interpolated.
vec3 normal = normalize(cross (dFdx (vertexViewPos), dFdy (vertexViewPos))); // UPDATED
...
}
To enable partial derivative support in WebGL you need to check the extension like this:
var ext = gl.getExtension("OES_standard_derivatives");
if (!ext) {
alert("OES_standard_derivatives does not exist on this machine");
return;
}
// proceed with the shaders above.

Resources