Custom Shader Joyplotstlye - three.js

Maybe you could help me out here please: I'm trying to visualize a digital elevation model with a mesh and draw lines on it joyplot-style. This is done with a custom shader and currently it looks like the picture.
The black lines look blurry, but I want them to look crisp (no grays, only black and white). Can someone tell me how to change the fragment shader to achieve this effect?
This is the current shader:
function vertexShader() {
return
varying vec3 vUv;
void main() {
vUv = position;
vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewPosition;
}
}
function fragmentShader() {
return
uniform vec3 colorA;
uniform vec3 colorB;
varying vec3 vUv;
void main() {
gl_FragColor = vec4(mix(colorA, colorB, vUv.y - (5.0 * floor(vUv.y / 5.0))), 1.0);
}
}

If you only want black and white pixels, you just need to write colorA or colorB in gl_FragColor. Replace mix with the ternary (?:) operator:
gl_FragColor = vec4(mix(colorA, colorB, vUv.y - (5.0 * floor(vUv.y / 5.0))), 1.0);
float w = vUv.y - (5.0 * floor(vUv.y / 5.0));
gl_FragColor = vec4(w < 0.5 ? colorA : colorB, 1.0);
mix linearly interpolate between two values, but the ternary operator only selects one of the values.
Alternatively, you can use step, which generates a step function by comparing two values and returns either 0.0 or 1.0:
float w = step(0.5, vUv.y - (5.0 * floor(vUv.y / 5.0)));
gl_FragColor = vec4(mix(colorA, colorB, w), 1.0);

Related

How to convert a square texture into a trapezoid texture with progressive distortion in GLSL

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;
}

cast shadow from partly transparent plane

Is there a possibility to cast shadow from a plane for which the texture plays a video with a chromakey shader ? My trial seams to answer by NO but I guess my shader is not adapted. The object is a simple PlabeBufferGeometry and the shader is :
vertexShader is :
varying vec2 vUv;
void main() {vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition;
}
fragmentShader is :
uniform sampler2D vidtexture;
uniform vec3 color;
varying vec2 vUv;
void main()
{ vec3 tColor = texture2D( vidtexture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
gl_FragColor = vec4(tColor, a);}
Have you tried using the discard keyword? If your frag shader encounters that keyword, it won't render that fragment, it's as if it didn't exist. You could use this to create shadow outlines defined by your chromakey instead of always geting a square shadow.
void main(){
vec3 tColor = texture2D( vidtexture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
// Do not render pixels that are less than 10% opaque
if (a < 0.1) discard;
gl_FragColor = vec4(tColor, a);
}
This is the same approach Three.js uses for Material.alphaTest in all their built-in materials. You can see the GLSL source code for that command here.

Change color in GLSL shaders

I am trying to modify the color of a 3d model (three.js) which uses GLSL shaders (frag and vert files). To be honest I am not experienced at all with shader language.
.frag file
precision highp float;
uniform sampler2D uTexture;
varying vec2 vPUv;
varying vec2 vUv;
void main() {
vec4 color = vec4(0.0);
vec2 uv = vUv;
vec2 puv = vPUv;
// pixel color
vec4 colA = texture2D(uTexture, puv);
// greyscale
float grey = colA.r * 0.31 + colA.g * 0.71 + colA.b * 0.07;
vec4 colB = vec4(grey, grey, grey, 1.0);
// circle
float border = 0.3;
float radius = 0.5;
float dist = radius - distance(uv, vec2(0.5));
float t = smoothstep(0.0, border, dist);
// final color
color = colB;
color.a = t;
gl_FragColor = color;
}
.vert file
precision highp float;
attribute float pindex;
attribute vec3 position;
attribute vec3 offset;
attribute vec2 uv;
attribute float angle;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform float uTime;
uniform float uRandom;
uniform float uDepth;
uniform float uSize;
uniform vec2 uTextureSize;
uniform sampler2D uTexture;
uniform sampler2D uTouch;
varying vec2 vPUv;
varying vec2 vUv;
#pragma glslify: snoise2 = require(glsl-noise/simplex/2d)
float random(float n) {
return fract(sin(n) * 43758.5453123);
}
void main() {
vUv = uv;
// particle uv
vec2 puv = offset.xy / uTextureSize;
vPUv = puv;
// pixel color
vec4 colA = texture2D(uTexture, puv);
float grey = colA.r * 0.21 + colA.g * 0.71 + colA.b * 0.07;
// displacement
vec3 displaced = offset;
// randomise
displaced.xy += vec2(random(pindex) - 0.5, random(offset.x + pindex) - 0.5) * uRandom;
float rndz = (random(pindex) + snoise_1_2(vec2(pindex * 0.1, uTime * 0.1)));
displaced.z += rndz * (random(pindex) * 2.0 * uDepth);
// center
displaced.xy -= uTextureSize * 0.5;
// touch
float t = texture2D(uTouch, puv).r;
displaced.z += t * 20.0 * rndz;
displaced.x += cos(angle) * t * 20.0 * rndz;
displaced.y += sin(angle) * t * 20.0 * rndz;
// particle size
float psize = (snoise_1_2(vec2(uTime, pindex) * 0.5) + 2.0);
psize *= max(grey, 0.2);
psize *= uSize;
// final position
vec4 mvPosition = modelViewMatrix * vec4(displaced, 1.0);
mvPosition.xyz += position * psize;
vec4 finalPosition = projectionMatrix * mvPosition;
gl_Position = finalPosition;
}
This creates particles from a very dark grey tone up to white like you see in the example. I would like to change only the color of the very dark tones to match the background color. I've played with some color values I've found here but unfortunately the results are not what I expected.
Maybe somebody has a quick hint for me?
I would like to change only the color of the very dark tones to match the background color.
Actually you create a gray scale color. But what you actually want is that the "dark tones to match the background" and the light tones are white. Hence you want a gradient from the background to white.
Either blend a white color with the back ground dependent on the grayscale:
void main()
{
vec4 color = vec4(0.0);
// [...]
// final color
color.rgb = vec3(1.0);
color.a = t * grey;
gl_FragColor = color;
}
You have to mix the back ground color and white dependent on the gray scale. For this you have to know the background color in the fragment shader:
void main()
{
vec4 color = vec4(0.0);
vec3 backgroundColor = vec3(42.0, 67.0, 101.0) / 255.0;
// [...]
// final color
color.rgb = mix(backgroundColor.rgb, vec3(1.0), gray);
color.a = t;
gl_FragColor = color;
}

How to implement MeshNormalMaterial in THREE.js by GLSL?

I want to implement a shader like MeshNormalMaterial, but I have no idea how to convert normal to color.
In THREE.js:
My test1:
varying vec3 vNormal;
void main(void) {
vNormal = abs(normal);
gl_Position = matrix_viewProjection * matrix_model * vec4(position, 1.0);
}
varying vec3 vNormal;
void main(void) {
gl_FragColor = vec4(vNormal, 1.0);
}
My test2:
varying vec3 vNormal;
void main(void) {
vNormal = normalize(normal) * 0.5 + 0.5;
gl_Position = matrix_viewProjection * matrix_model * vec4(position, 1.0);
}
varying vec3 vNormal;
void main(void) {
gl_FragColor = vec4(vNormal, 1.0);
}
These are just test, I can't find any resources about how to calculate the color...
Can anyone help me ?
Thanks.
If you want to see the normal vector in view space, the you have to transform the normal vector from the model space to the world space and from the world space to the view space. This can be done in one step by transforming the normal vector with the normalMatrix.
varying vec3 vNormal;
void main(void)
{
vNormal = normalMatrix * normalize(normal);
gl_Position = matrix_viewProjection * matrix_model * vec4(position, 1.0);
}
Since a varying variable is interpolated when it is passed from the vertex shader to the fragment shader, according to its Barycentric coordinates, the transformation to the color should be done in the fragment shader. Note, after the interpolation the normal vector has to be normalized again
varying vec3 vNormal;
void main(void)
{
vec3 view_nv = normalize(vNormal);
vec3 nv_color = view_nv * 0.5 + 0.5;
gl_FragColor = vec4(nv_color, 1.0);
}
Since the normal vector is normalized, its component are in the range [-1.0, 1.0]. How to represent it as a color is up to you.
If you use the abs value, then a positive and negative value with the same size have the same color representation. The intensity of the color increases with the grad of the value.
With the formula normal * 0.5 + 0.5 the intensity increases from 0 to 1.
In common the x component is represented red, the y component is green and the z component is blue.
The colors can be saturated, by dividing with the maximum value of its components:
varying vec3 vNormal;
void main(void)
{
vec3 view_nv = normalize(vNormal);
vec3 nv_color = abs(view_nv);
nv_color /= max(nv_color.x, max(nv_color.y, nv_color.z));
gl_FragColor = vec4(nv_color, 1.0);
}

Shader Z space perspective ShaderMaterial BufferGeometry

I'm changing the z coordinate vertices on my geometry but find that the Mesh Stays the same size, and I'm expecting it to get smaller. Tweening between vertex positions works as expected in X,Y space however.
This is how I'm calculating my gl_Position by tweening the amplitude uniform in my render function:
<script type="x-shader/x-vertex" id="vertexshader">
uniform float amplitude;
uniform float direction;
uniform vec3 cameraPos;
uniform float time;
attribute vec3 tweenPosition;
varying vec2 vUv;
void main() {
vec3 pos = position;
vec3 morphed = vec3( 0.0, 0.0, 0.0 );
morphed += ( tweenPosition - position ) * amplitude;
morphed += pos;
vec4 mvPosition = modelViewMatrix * vec4( morphed * vec3(1, -1, 0), 1.0 );
vUv = uv;
gl_Position = projectionMatrix * mvPosition;
}
</script>
I also tried something like this from calculating perspective on webglfundamentals:
vec4 newPos = projectionMatrix * mvPosition;
float zToDivideBy = 1.0 + newPos.z * 1.0;
gl_Position = vec4(newPos.xyz, zToDivideBy);
This is my loop to calculate another vertex set that I'm tweening between:
for (var i = 0; i < positions.length; i++) {
if ((i+1) % 3 === 0) {
// subtracting from z coord of each vertex
tweenPositions[i] = positions[i]- (Math.random() * 2000);
} else {
tweenPositions[i] = positions[i]
}
}
I get the same results with this -- objects further away in Z-Space do not scale / attenuate / do anything different. What gives?
morphed * vec3(1, -1, 0)
z is always zero in your code.
[x,y,z] * [1,-1,0] = [x,-y,0]

Resources