How to have distortion effect on image using Javascript? - javascript

I'm trying to build a simple image distortion effect on mouse hover same as in this link.
I am expecting to have same output as this, the output is working, but there are 2 issues:
I want to have idle animation, but I could not find the way to
implement it in documentation.
I want to change depth of mouse when hover over image.
EDIT:
I tried to re-implement same effect using Javascript library:
body {
margin: 0;
background: #fff;
padding: 0;
}
.container {
position: relative;
}
/* canvas */
#canvas {
position: fixed;
top: 0;
right: 0;
left: 0;
height: 100vh;
z-index: 10;
}
#flowmap {
width: 100%;
height: 100vh;
}
#flowmap img {
display: none;
}
<div class="container">
<div id="canvas"></div>
<div id="flowmap">
<img src="https://i.imgur.com/wATUVc1.jpeg" alt="" data-sampler="planeTexture" />
</div>
</div>
<script>
// flowmap shaders
const flowmapVs = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
// default mandatory variables
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
// custom variables
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
void main() {
vec3 vertexPosition = aVertexPosition;
gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0);
// varyings
vTextureCoord = aTextureCoord;
vVertexPosition = vertexPosition;
}
`;
const flowmapFs = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
uniform sampler2D uFlowMap;
uniform vec2 uMousePosition;
uniform float uFalloff;
uniform float uAlpha;
uniform float uDissipation;
uniform float uCursorGrow;
uniform vec2 uVelocity;
uniform float uAspect;
void main() {
vec2 textCoords = vTextureCoord;
/*** comment this whole block for a regular mouse flow effect ***/
// convert to -1 -> 1
textCoords = textCoords * 2.0 - 1.0;
// make the cursor grow with time
textCoords /= uCursorGrow;
// adjust cursor position based on its growth
textCoords += uCursorGrow * uMousePosition / (1.0 / (uCursorGrow - 1.0) * pow(uCursorGrow, 2.0));
// convert back to 0 -> 1
textCoords = (textCoords + 1.0) / 2.0;
/*** end of whole block commenting for a regular mouse flow effect ***/
vec4 color = texture2D(uFlowMap, textCoords) * uDissipation;
//vec4 color = vec4(0.0, 0.0, 0.0, 1.0) * uDissipation;
vec2 mouseTexPos = (uMousePosition + 1.0) * 0.5;
vec2 cursor = vTextureCoord - mouseTexPos;
cursor.x *= uAspect;
vec3 stamp = vec3(uVelocity * vec2(1.0, -1.0), 1.0 - pow(1.0 - min(1.0, length(uVelocity)), 3.0));
float falloff = smoothstep(uFalloff, 0.0, length(cursor)) * uAlpha;
color.rgb = mix(color.rgb, stamp, vec3(falloff));
// handle premultiply alpha
color.rgb = color.rgb * color.a;
gl_FragColor = color;
}
`;
const displacementVs = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
// default mandatory variables
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 planeTextureMatrix;
// custom variables
varying vec3 vVertexPosition;
varying vec2 vPlaneTextureCoord;
varying vec2 vTextureCoord;
void main() {
vec3 vertexPosition = aVertexPosition;
gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0);
// varyings
vTextureCoord = aTextureCoord;
vPlaneTextureCoord = (planeTextureMatrix * vec4(aTextureCoord, 0.0, 1.0)).xy;
vVertexPosition = vertexPosition;
}
`;
const displacementFs = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
varying vec3 vVertexPosition;
varying vec2 vPlaneTextureCoord;
varying vec2 vTextureCoord;
uniform sampler2D planeTexture;
uniform sampler2D uFlowTexture;
// the uniform we declared inside our javascript
uniform float uTime;
void main() {
// our flowmap
vec4 flowTexture = texture2D(uFlowTexture, vTextureCoord);
// distort our image texture based on the flowmap values
vec2 distortedCoords = vPlaneTextureCoord;
distortedCoords -= (flowTexture.xy * 0.1);
// get our final texture based on the displaced coords
vec4 texture = texture2D(planeTexture, distortedCoords);
// get a B&W version of our image texture
vec4 textureBW = vec4(1.4);
textureBW.rgb = vec3(texture.r * 0.3 + texture.g * 0.59 + texture.b * 0.11);
// mix the BW image and the colored one based on our flowmap color values
float mixValue = clamp((abs(flowTexture.r) + abs(flowTexture.g) + abs(flowTexture.b)) * 1.5, 0.0, 0.0);
texture = mix(texture, textureBW, mixValue);
// switch between this 2 lines to see what we have drawn onto our flowmap
//gl_FragColor = flowTexture;
gl_FragColor = texture;
}
`;
</script>
<script type="module">
// add cdn of curtainsjs
import {
Curtains,
Plane,
Vec2,
PingPongPlane,
} from "https://cdn.jsdelivr.net/npm/curtainsjs#7.2.0/src/index.mjs";
window.addEventListener("load", () => {
// set up webGl contetxt or set up curtiansjs
const curtains = new Curtains({
container: "canvas",
pixelRatio: Math.min(1.5, window.devicePixelRatio),
});
// mouse/touch move
const ww = window.innerWidth;
const wh = window.innerHeight;
const mouse = new Vec2(ww / 5, wh / 5);
const lastMouse = mouse.clone();
const velocity = new Vec2();
function onMouseMove(e) {
lastMouse.copy(mouse);
// touch event
if (e.targetTouches) {
mouse.set(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
} else {
mouse.set(e.clientX, e.clientY);
}
velocity.set((mouse.x - lastMouse.x) / 16, (mouse.y - lastMouse.y) / 16);
// update the velocity
updateVelocity = true;
}
window.addEventListener("mousemove", onMouseMove);
window.addEventListener("touchmove", onMouseMove, {
passive: true,
});
// create planeElement
const planeElement = document.getElementById("flowmap");
// parameters
const flowMapParams = {
sampler: "uFlowMap",
vertexShader: flowmapVs,
fragmentShader: flowmapFs,
texturesOptions: {
floatingPoint: "half-float",
},
uniforms: {
mousePosition: {
name: "uMousePosition",
type: "2f",
value: mouse,
},
fallOff: {
name: "uFalloff",
type: "1f",
value: ww > wh ? ww / 10000 : wh / 10000,
// we can change what i want here
},
time: {
name: "uTime", // uniform name that will be passed to our shaders
type: "1f", // this means our uniform is a float
value: 0,
},
cursorGrow: {
name: "uCursorGrow",
type: "1f",
value: 1.15,
},
// alpha of the cursor
alpha: {
name: "uAlpha",
type: "1f",
value: 1.14,
},
dissipation: {
name: "uDissipation",
type: "1f",
value: 0.925,
},
velocity: {
name: "uVelocity",
type: "2f",
value: velocity,
},
aspect: {
name: "uAspect",
type: "1f",
value: ww / wh,
},
},
};
// create ping pong plane
const flowMap = new PingPongPlane(curtains, planeElement, flowMapParams);
flowMap.onRender(() => {
flowMap.uniforms.time.value++;
// update mouse position
flowMap.uniforms.mousePosition.value = flowMap.mouseToPlaneCoords(mouse);
flowMap.uniforms.velocity.value = new Vec2(
curtains.lerp(velocity.x, 0.5, 1.5),
curtains.lerp(velocity.y, 0.5, 1.5)
);
});
// add displacements shader
const params = {
vertexShader: displacementVs,
fragmentShader: displacementFs,
};
// create plane
const plane = new Plane(curtains, planeElement, params);
// create a texture that will hold our flowmap
const flowTexture = plane.createTexture({
sampler: "uFlowTexture",
fromTexture: flowMap.getTexture(), // set it based on our PingPongPlane flowmap plane's texture
});
});
</script>

Related

THREE JS OcclusionComposer issues "Cannot read properties of undefined (reading 'x')"

I am trying to recreate the Volumetric Lighting example from JMSWRNR ( https://codesandbox.io/s/github/jmswrnr/website-examples/tree/master/3d-header/volumetric-lighting?from-embed=&file=/src/index.js ) but I am facing some issues with Occlusion Composer.
I don't really know GLSL so debugging is very hard especially for a beginner like me.
Here is the GitHub Repo: https://github.com/RolandTeslaru/roland-teslaru-portfolio/tree/not-working
Main Code is in the components/HomeCanvas/Scene.ts
My guess its that the VolumetricLightCylinder.js shader is causing the issue because when I change
worldPosition.xyz;
to
worldPosition
the error disappears.
import * as THREE from 'three'
export default () => ({
lights: true,
transparent: true,
depthWrite: false,
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib['lights'],
{
attenuation: { value: 25.0 },
anglePower: { value: 10.0 },
spotPosition: { value: new THREE.Vector3(0, 0, 0) },
},
]),
vertexShader: /* glsl */ `
#include <common>
struct PointLight {
vec3 position;
vec3 color;
float distance;
float decay;
int shadow;
float shadowBias;
float shadowRadius;
vec2 shadowMapSize;
float shadowCameraNear;
float shadowCameraFar;
};
uniform PointLight pointLights[NUM_POINT_LIGHTS];
uniform float viewMix;
varying vec4 vColor;
varying vec3 vNormal;
varying vec3 vWorldPosition;
float _punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
if( decayExponent > 0.0 ) {
return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );
}
return 1.0;
}
void main() {
vNormal = normalize(normalMatrix * normal);
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewPosition;
vec4 addedLights = vec4(0.1, 0.1, 0.1, 1.0);
for(int l = 0; l < NUM_POINT_LIGHTS; l++) {
float dist = distance(pointLights[l].position, worldPosition.xyz);
addedLights.rgb += pointLights[l].color *
_punctualLightIntensityToIrradianceFactor(
dist,
pointLights[l].distance,
pointLights[l].decay
);
}
vWorldPosition = worldPosition.xyz;
vColor = addedLights;
}
`,
fragmentShader: /* glsl */`
varying vec3 vNormal;
varying vec3 vWorldPosition;
uniform vec3 spotPosition;
uniform float attenuation;
uniform float anglePower;
varying vec4 vColor;
void main() {
float intensity;
intensity = distance(vWorldPosition, spotPosition)/attenuation;
intensity = 1.0 - clamp(intensity, 0.0, 1.0);
vec3 normal = vec3(vNormal.x, vNormal.y, abs(vNormal.z));
float angleIntensity = pow(dot(normal, vec3(0.0, 0.0, 1.0)), anglePower);
intensity = intensity * angleIntensity;
gl_FragColor = vec4(vColor.rgb, intensity);
}
`,
})

How do I pass the canvas as a texture to a shader?

I want to manipulate the canvas with a shader, but I am stuck with how to pass the current canvas to the shader? I assume I use sampler2D.
Why doesn't this code invert the canvas? Instead it's just a blank white screen.
image(cnvs) works perfectly fine.
script.js
let cnvs;
let shdr;
function preload() {
shdr = loadShader("shaders/shader.vert", "shaders/shader.frag");
}
function setup() {
cnvs = createCanvas(500, 500, WEBGL);
}
function draw() {
fill(255, 0, 0);
square(0, 0, width / 2);
shdr.setUniform("u_resolution", [width, height]);
shdr.setUniform("u_texture", cnvs);
shader(shdr);
fill(0);
rect(0, 0, width, height);
resetShader();
noLoop();
}
shader.frag
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform sampler2D u_texture;
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
st.y = 1.0 - st.y;
vec4 tex = texture2D(u_texture, st);
gl_FragColor = tex;
}
shader.vert
#ifdef GL_ES
precision mediump float;
#endif
attribute vec3 aPosition;
void main() {
vec4 positionVec4 = vec4(aPosition, 1.0); // Copy the position data into a vec4, adding 1.0 as the w parameter
positionVec4.xy = positionVec4.xy * 2.0 - 1.0; // Scale to make the output fit the canvas.
gl_Position = positionVec4;
}

THREE.js vertexShader color mix based on height

How do I set the color to the mesh only when the height is zero?
As for now, i just mixed the colors:
The problem is that this kind on mixing is not precise. I just want the color blue only when the height is zero (so only inside that red path I made with paint).
I created a custom material for the mesh, like so:
material = new THREE.ShaderMaterial({
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib['lights'],
{
lightIntensity: {type: 'f', value: 1.0},
diffuse: {type: 'c', value: new THREE.Color(0x0000ff)},
color0: {
value: new THREE.Color("blue")
},
color1: {
value: new THREE.Color("green")
},
color2: {
value: new THREE.Color("brown")
},
color3: {
value: new THREE.Color("black")
},
bboxMin: {
value: geom.boundingBox.min
},
bboxMax: {
value: geom.boundingBox.max
}
}
]),
vertexShader: `
uniform vec3 bboxMin;
uniform vec3 bboxMax;
varying vec2 vUv;
varying vec3 vPos;
varying vec3 vNormal;
void main() {
vPos = (modelMatrix * vec4(position, 1.0 )).xyz;
vNormal = normalMatrix * normal;
vUv.y = (position.y - bboxMin.y) / (bboxMax.y - bboxMin.y);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`,
fragmentShader: `
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
uniform vec3 color0;
varying vec2 vUv;
uniform vec3 diffuse;
varying vec3 vPos;
varying vec3 vNormal;
struct PointLight {
vec3 position;
vec3 color;
};
uniform PointLight pointLights[ NUM_POINT_LIGHTS ];
void main() {
vec4 addedLights = vec4(0.1, 0.1, 0.1, 1.0);
for(int l = 0; l < NUM_POINT_LIGHTS; l++) {
vec3 adjustedLight = pointLights[l].position + cameraPosition;
vec3 lightDirection = normalize(vPos - adjustedLight);
addedLights.rgb += clamp(dot(-lightDirection, vNormal), 0.0, 1.0) * pointLights[l].color;
}
gl_FragColor = mix(vec4(mix(mix(mix(color0, color1, vUv.y), color1, vUv.y), mix(color1, color2, vUv.y), vUv.y), 1.0),addedLights, addedLights);
}
`,
lights: true
});
Try using the step() function. Here's a definition to help you understand it. Here's how it works:
float step(float edge, float x)
It takes in a constant to declare the edge, and x, which is your variable.
If x is below the edge, you get 0, and if x is above the edge, you get 1.
Here's a simplified use of it. When height is below 0.2, you'll get blue, and when height is above 0.2, you'll get green.
vec3 green = vec3(0.0, 1.0, 0.0);
vec3 blue = vec3(0.0, 0.0, 1.0);
float edge = 0.2;
float colorMix = step(edge, height);
vec3 finalColor = mix(blue, green, colorMix);
I picked 0.2 to give the blue band some thickness, otherwise it wouldn't be visible.

How to use GLSL scale and position together with VideoContext?

I have an interesting problem with the VideoContext library.
I am trying to apply glsl Scale to media, which works ok, but when I then subsequently use a Position effect, I find that the media is then "cut off" at the top, revealing the black background color, making a sort of "black bar" at the top of the media. Please see the problem area circled in red here:
This seems like strange behavior to me, since I had already scaled up the media.
I have made a CodePen demonstrating the problem here:
https://codepen.io/kingpalethe/pen/xeVYLr?editors=0010
Here is all the JS of this codepen:
const exampleMediaFile = {
url: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/2831288/cars-4T1c8aZiOWDDZ-1N8KxDX49.mp4",
width: 1280,
height: 720,
}
const canvasDescription = {
width: 1280,
height: 720,
}
// scale effect
const scaleDescription = {
title: "AAF Video Scale Effect",
description: "A scale effect based on the AAF spec.",
vertexShader: `
attribute vec2 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = vec4(vec2(2.0,2.0)*a_position-vec2(1.0, 1.0), 0.0, 1.0);
v_texCoord = a_texCoord;
}`,
fragmentShader: `
precision mediump float;
uniform sampler2D u_image;
uniform float scaleX;
uniform float scaleY;
varying vec2 v_texCoord;
varying float v_progress;
void main(){
vec2 pos = vec2(v_texCoord[0]*1.0/scaleX - (1.0/scaleX/2.0 -0.5), v_texCoord[1]*1.0/scaleY - (1.0/scaleY/2.0 -0.5));
vec4 color = texture2D(u_image, pos);
if (pos[0] < 0.0 || pos[0] > 1.0 || pos[1] < 0.0 || pos[1] > 1.0){
color = vec4(0.0,0.0,0.0,0.0);
}
gl_FragColor = color;
}`,
properties: {
scaleX: { type: "uniform", value: 1.0 },
scaleY: { type: "uniform", value: 1.0 }
},
inputs: ["u_image"]
};
/// position effect
const positionDescription = {
title: "AAF Video Position Effect",
description: "A position effect based on the AAF spec.",
vertexShader: `
attribute vec2 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = vec4(vec2(2.0,2.0)*a_position-vec2(1.0, 1.0), 0.0, 1.0);
v_texCoord = a_texCoord;
}`,
fragmentShader: `
precision mediump float;
uniform sampler2D u_image;
uniform float positionOffsetX;
uniform float positionOffsetY;
varying vec2 v_texCoord;
varying float v_progress;
void main(){
vec2 pos = vec2(v_texCoord[0] - positionOffsetX/2.0, v_texCoord[1] - positionOffsetY/2.0);
vec4 color = texture2D(u_image, pos);
if (pos[0] < 0.0 || pos[0] > 1.0 || pos[1] < 0.0 || pos[1] > 1.0){
color = vec4(0.0,0.0,0.0,0.0);
}
gl_FragColor = color;
}`,
properties: {
positionOffsetX: { type: "uniform", value: 0.0 },
positionOffsetY: { type: "uniform", value: 0.0 }
},
inputs: ["u_image"]
};
//Setup the video context.
var canvas = document.getElementById("canvas");
var ctx = new VideoContext(canvas);
//Create a video node
var videoNode = ctx.video(exampleMediaFile.url);
videoNode.start(0);
videoNode.stop(10);
/// ------------ SCALE
const scaleEffect = ctx.effect(scaleDescription);
scaleEffect.scaleX = 2 // this is 200% scale
scaleEffect.scaleY = 2 // this is 200% scale
/// ------------ POSITION
const positionEffect = ctx.effect(positionDescription);
positionEffect.positionOffsetX = 0
positionEffect.positionOffsetY = -0.1 // this nudges the position slightly down
videoNode.connect(scaleEffect)
scaleEffect.connect(positionEffect)
positionEffect.connect(ctx.destination);
// you could play it if you wanted
// ctx.play();

Can't manage to make a shader running on three.js from shaderfrom

I would like to import that shader on my project:
https://shaderfrog.com/app/view/2447
This is my fragment shader:
<script id="fragmentShader" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
uniform sampler2D texture1;
// Example varyings passed from the vertex shader
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
vec3 color = vec3(texture2D(texture1, vUv));
vec3 outcolor=color;
float v = u_time + (vUv.x*0.5 + vUv.y*0.5);
vec2 Uv2 = vec2(color.r+color.b+v,color.g+color.b+v);
outcolor = vec3(texture2D(texture1, Uv2));
gl_FragColor = vec4( outcolor, 1.0 );
}
</script>
And this is my vertex shader:
<script id="vertexShader" type="x-shader/x-vertex">
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
vNormal = normal;
vUv = uv;
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
My shader is compiling, but I don't have the same result, I only have a highly frozen contrasted image. Plus, I do increment my uniform u_time value on a requestAnimationFrame function.
I can't see what I'm doing wrong?
I've simplified your code and made it work with the latest revision (r96).
Pay attention to the settings of the texture.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 2);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var tex = new THREE.TextureLoader().load("https://threejs.org/examples/textures/UV_Grid_Sm.jpg");
tex.wrapS = THREE.RepeatWrapping;
tex.wrapT = THREE.RepeatWrapping;
var geo = new THREE.PlaneBufferGeometry(2, 2);
var mat = new THREE.ShaderMaterial({
uniforms:{
time: {value: 0},
texture1: {value: tex}
},
vertexShader:`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`,
fragmentShader: `
uniform float time;
uniform sampler2D texture1;
varying vec2 vUv;
void main() {
vec3 c = vec3(texture2D(texture1, vUv));
float v = time + (vUv.x*0.5 + vUv.y*0.5);
vec2 Uv2 = vec2(c.r + c.b+v, c.g + c.b + v);
vec3 outcolor = vec3(texture2D(texture1, Uv2));
gl_FragColor = vec4( outcolor, 1.0 );
}
`
});
var plane = new THREE.Mesh(geo, mat);
scene.add(plane);
var clock = new THREE.Clock();
var time = 0;
render();
function render(){
requestAnimationFrame(render);
time += clock.getDelta();
mat.uniforms.time.value = time;
renderer.render(scene, camera);
}
body{
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>

Categories

Resources