Im in a Three.js project and Im trying to convert a square with a square texture inside into a trapezoid.
I manage to create the shape but the texture inside, although it fits/cover the shape it do it with an undesired distorsiĆ³n.
Im using a PlaneBufferGeometry with ShaderMaterial and im trying to obtain this distorsion in the shader part (although it would be ok if it is done in the threejs geometry part).
This is my vertex:
uniform sampler2D uTexture;
varying vec2 vUv;
void main(){
float scaleTOP = 0.5;
float scaleBOTTOM = 1.0;
float scaleLEFT = 1.0;
float scaleRIGHT = 1.0;
float scaleX = mix(scaleBOTTOM, scaleTOP, uv.y);
float posX = position.x*scaleX;
float scaleY = mix(scaleLEFT, scaleRIGHT, uv.x);
float posY = position.y*scaleY;
vec3 finalPosition = vec3(posX, posY);
gl_Position = projectionMatrix * modelViewMatrix * vec4( finalPosition, 1.0 );
// Varyings:
vUv = uv;
}
And this is my fragment:
uniform sampler2D uTexture;
varying vec2 vUv;
void main() {
vec4 tex = texture2D ( uTexture, vUv );
gl_FragColor = vec4(tex.r, tex.g, tex.b, 1.0);
}
Unfortunately I manage to distort the square into the trapezoid but the texture is not distorted in the way I want. See figure to see the intended result:
Figure:
My vertex and fragment were ok.
The problem was that the Threejs geometry I was using had only 2 polygons. I was using:
this.bg_geometry = new THREE.PlaneBufferGeometry(width, height, 1, 1)
Thats it... with only one division which only created two triangles which actually can be seen in the figure I posted.
I changed the geometry to:
this.bg_geometry = new THREE.PlaneBufferGeometry(width, height, 100, 100)
...and now the texture is distorted as desired.
Anyway many thanks to #prisoner849 as he put me in the track to pass 4 points as uniforms uPoints in this order: TL,TR,BL,BR to set the shape of the plane.
My vertex shader looks now like this:
uniform vec3 uPoints[4];
varying vec2 vUv;
void main(){
vec3 baselineBottom = (uPoints[3] - uPoints[2]) * uv.x + uPoints[2];
vec3 baselineTop = (uPoints[1] - uPoints[0]) * uv.x + uPoints[0];
vec3 finalPosition = (baselineTop - baselineBottom) * uv.y + baselineBottom;
gl_Position = projectionMatrix * modelViewMatrix * vec4( finalPosition, 1.0 );
vUv = uv;
}
How can you change the position of instanced vertexes from a square to a circle?
I'm attempting to control the positioning of the vertices, specifically to constrain them into the shape of a circle rather than a square. I can't use the fragment shader (which I'm more comfortable with moulding shapes in) because I'm instancing the vertexes.
//UV for texture
vUv = uv;
vec3 pos;
vec3 globalPos;
vec3 tile;
globalPos.x = offset.x-posX*delta;
globalPos.z = offset.z-posZ*delta;
tile.x = floor((globalPos.x + 0.5 * width) / width);
tile.z = floor((globalPos.z + 0.5 * width) / width);
pos.x = globalPos.x - tile.x * width;
pos.z = globalPos.z - tile.z * width;
pos.y = max(0.0, placeOnSphere(pos)) - radius;
pos.y += getYPosition(vec2(pos.x+delta*posX, pos.z+delta*posZ));
//Position of the blade in the visible patch [0->1]
vec2 fractionalPos = 0.5 + offset.xz / width;
Can anyone point me in the right direction for how I would do this?
Picture for illustration purposes:
The entire vertex shader:
precision mediump float;
attribute vec3 position;
attribute vec3 normal;
attribute vec3 offset;
attribute vec2 uv;
attribute vec2 halfRootAngle;
attribute float scale;
attribute float index;
uniform float time;
uniform float delta;
uniform float posX;
uniform float posZ;
uniform float radius;
uniform float width;
uniform float bladeHeight;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;
varying float frc;
varying float idx;
const float PI = 3.1415;
const float TWO_PI = 2.0 * PI;
//https://www.geeks3d.com/20141201/how-to-rotate-a-vertex-by-a-quaternion-in-glsl/
vec3 rotateVectorByQuaternion(vec3 v, vec4 q){
return 2.0 * cross(q.xyz, v * q.w + cross(q.xyz, v)) + v;
}
float placeOnSphere(vec3 v){
float theta = acos(v.z/radius);
float phi = acos(v.x/(radius * sin(theta)));
float sV = radius * sin(theta) * sin(phi);
//If undefined, set to default value
if(sV != sV){
sV = v.y;
}
return sV;
}
void main() {
//Vertex height in blade geometry
frc = position.y / float(bladeHeight);
//Scale vertices
vec3 vPosition = position;
vPosition.y *= scale;
//Invert scaling for normals
vNormal = normal;
vNormal.y /= scale;
//Rotate blade around Y axis
vec4 direction = vec4(0.0, halfRootAngle.x, 0.0, halfRootAngle.y);
vPosition = rotateVectorByQuaternion(vPosition, direction);
vNormal = rotateVectorByQuaternion(vNormal, direction);
//UV for texture
vUv = uv;
vec3 pos;
vec3 globalPos;
vec3 tile;
globalPos.x = offset.x-posX*delta;
globalPos.z = offset.z-posZ*delta;
tile.x = floor((globalPos.x + 0.5 * width) / width);
tile.z = floor((globalPos.z + 0.5 * width) / width);
pos.x = globalPos.x - tile.x * width;
pos.z = globalPos.z - tile.z * width;
pos.y = max(0.0, placeOnSphere(pos)) - radius;
pos.y += getYPosition(vec2(pos.x+delta*posX, pos.z+delta*posZ));
//Position of the blade in the visible patch [0->1]
vec2 fractionalPos = 0.5 + offset.xz / width;
//To make it seamless, make it a multiple of 2*PI
fractionalPos *= TWO_PI;
//Wind is sine waves in time.
float noise = sin(fractionalPos.x + time);
float halfAngle = noise * 0.1;
noise = 0.5 + 0.5 * cos(fractionalPos.y + 0.25 * time);
halfAngle -= noise * 0.2;
direction = normalize(vec4(sin(halfAngle), 0.0, -sin(halfAngle), cos(halfAngle)));
//Rotate blade and normals according to the wind
vPosition = rotateVectorByQuaternion(vPosition, direction);
vNormal = rotateVectorByQuaternion(vNormal, direction);
//Move vertex to global location
vPosition += pos;
//Index of instance for varying colour in fragment shader
idx = index;
gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0);
}
I'm building 2D Graph structure based on Three.js, all elements of the graph (nodes, edges, triangles for arrows) calculated in shaders. I was able to reach a good level of antialiasing for nodes (circles) but stuck with same task for lines and triangles.
I was able to reach a good antialiasing results for nodes (circles) with and without stroke following this question: How can I add a uniform width outline to WebGL shader drawn circles/ellipses (drawn using edge/distance antialiasing) , my code, responsible for antialiasing alpha:
`float strokeWidth = 0.09;
float outerEdgeCenter = 0.5 - strokeWidth;
float d = distance(vUV, vec2(.5, .5));
float delta = fwidth(d);
float alpha = 1.0 - smoothstep(0.45 - delta, 0.45, d);
float stroke = 1.0 - smoothstep(outerEdgeCenter - delta,
outerEdgeCenter + delta, d);`
But now I'm completely stack with edges and triangles to do same stuff.
Here is an example of shapes images that I have now (on non retina displays):
To reduce under-sampling artifacts I want to do similar algorithms (as for circles) directly in shaders by manipulating alpha and already find some materials related to this topic:
https://thebookofshaders.com/glossary/?search=smoothstep - seems to be the closest solution but unfortunately I wasn't able to implement it properly and figure out how to set up y equation for segmented lines.
https://discourse.threejs.org/t/shader-to-create-an-offset-inward-growing-stroke/6060/12 - last answer, looks promising but not give me proper result.
https://www.shadertoy.com/view/4dcfW8 - also do not give proper result.
Here is an examples of my shaders for lines and triangles:
Line VertexShader (is a slightly adapted version of WestLangley's LineMaterial shader):
`precision highp float;
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
uniform float linewidth;
uniform vec2 resolution;
attribute vec3 instanceStart;
attribute vec3 instanceEnd;
attribute vec3 instanceColorStart;
attribute vec3 instanceColorEnd;
attribute float alphaStart;
attribute float alphaEnd;
attribute float widthStart;
attribute float widthEnd;
varying vec2 vUv;
varying float alphaTest;
void trimSegment( const in vec4 start, inout vec4 end ) {
// trim end segment so it terminates between the camera plane and the near plane
// conservative estimate of the near plane
float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column
float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column
float nearEstimate = - 0.5 * b / a;
float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );
end.xyz = mix( start.xyz, end.xyz, alpha );
}
void main() {
#ifdef USE_COLOR
vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;
alphaTest = ( position.y < 0.5 ) ? alphaStart : alphaEnd;
#endif
float aspect = resolution.x / resolution.y;
vUv = uv;
// camera space
vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
// but we need to perform ndc-space calculations in the shader, so we must address this issue directly
// perhaps there is a more elegant solution -- WestLangley
bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column
if (perspective) {
if (start.z < 0.0 && end.z >= 0.0) {
trimSegment( start, end );
} else if (end.z < 0.0 && start.z >= 0.0) {
trimSegment( end, start );
}
}
// clip space
vec4 clipStart = projectionMatrix * start;
vec4 clipEnd = projectionMatrix * end;
// ndc space
vec2 ndcStart = clipStart.xy / clipStart.w;
vec2 ndcEnd = clipEnd.xy / clipEnd.w;
// direction
vec2 dir = ndcEnd - ndcStart;
// account for clip-space aspect ratio
dir.x *= aspect;
dir = normalize( dir );
// perpendicular to dir
vec2 offset = vec2( dir.y, - dir.x );
// undo aspect ratio adjustment
dir.x /= aspect;
offset.x /= aspect;
// sign flip
if ( position.x < 0.0 ) offset *= - 1.0;
// endcaps, to round line corners
if ( position.y < 0.0 ) {
// offset += - dir;
} else if ( position.y > 1.0 ) {
// offset += dir;
}
// adjust for linewidth
offset *= (linewidth * widthStart);
// adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...
offset /= resolution.y;
// select end
vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;
// back to clip space
offset *= clip.w;
clip.xy += offset;
gl_Position = clip;
vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
#include <fog_vertex>
}`
Line FragmentShader:
`precision highp float;
#include <common>
#include <color_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
uniform vec3 diffuse;
uniform float opacity;
varying vec2 vUv;
varying float alphaTest;
void main() {
if ( abs( vUv.y ) > 1.0 ) {
float a = vUv.x;
float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
float len2 = a * a + b * b;
if ( len2 > 1.0 ) discard;
}
vec4 diffuseColor = vec4( diffuse, alphaTest );
#include <logdepthbuf_fragment>
#include <color_fragment>
gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a );
#include <premultiplied_alpha_fragment>
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
}`
Triangle vertex shader:
`precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform float zoomLevel;
attribute vec3 position;
attribute vec3 vertexPos;
attribute vec3 color;
attribute float alpha;
attribute float xAngle;
attribute float yAngle;
attribute float xScale;
attribute float yScale;
varying vec4 vColor;
// transforms the 'positions' geometry with instance attributes
vec3 transform( inout vec3 position, vec3 T) {
position.x *= xScale;
position.y *= yScale;
// Rotate the position
vec3 rotatedPosition = vec3(
position.x * yAngle + position.y * xAngle,
position.y * yAngle - position.x * xAngle, 0);
position = rotatedPosition + T;
// return the transformed position
return position;
}
void main() {
vec3 pos = position;
vColor = vec4(color, alpha);
// transform it
transform(pos, vertexPos);
gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );
}`
Triangle FragmentShader:
`precision highp float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}`
Will really appreciate any help on how to do it or suggestion of right direction for further investigations. Thank you!
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.