My objective is to utilize curtainjs to apply a distortion and scale effect along the Y axis to a plane and its texture, with the added specification of incorporating rounded edges on the plane. However, I am not sure of how to execute this modification without disrupting the rest of the script, and thus, preventing the successful execution of the intended distortion and scaling effects, so it can distort and scale properly.
- Presented below is the original script of curtainjs, which implements a scale effect along the Y axis on both the plane and its texture :
import {Curtains, Plane, ShaderPass, Vec2, Vec3} from 'curtainsjs';
window.addEventListener("load", () => {
let planeDrawn = 0;
const debugElement = document.getElementById("debug-value");
const curtains = new Curtains({
container: "canvas",
antialias: false,
pixelRatio: Math.min(1.5, window.devicePixelRatio)
});
curtains.onRender(() => {
scrollEffect = curtains.lerp(scrollEffect, 0, 0.05);
debugElement.innerText = planeDrawn;
}).onScroll(() => {
const delta = curtains.getScrollDeltas();
delta.y = -delta.y;
if(delta.y > 60) {
delta.y = 60;
}
else if(delta.y < -60) {
delta.y = -60;
}
if(Math.abs(delta.y) > Math.abs(scrollEffect)) {
scrollEffect = curtains.lerp(scrollEffect, delta.y, 0.5);
}
for(let i = 0; i < planes.length; i++) {
applyPlanesParallax(i);
}
}).onError(() => {
document.body.classList.add("no-curtains", "planes-loaded");
}).onContextLost(() => {
curtains.restoreContext();
});
const planes = [];
let scrollEffect = 0;
const planeElements = document.getElementsByClassName("plane");
const vs = `
precision mediump float;
// default mandatory variables
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
attribute vec3 aVertexNormal; // HERE CHANGES
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 planeTextureMatrix;
// custom variables
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
varying vec3 vVertexNormal; // HERE CHANGES
void main() {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
// varyings
vVertexPosition = aVertexPosition;
vVertexNormal = aVertexNormal; // HERE CHANGES
vTextureCoord = (planeTextureMatrix * vec4(aTextureCoord, 0.0, 1.0)).xy;
}
`;
const fs = `
precision mediump float;
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
varying vec3 vVertexNormal; // HERE CHANGES
uniform sampler2D planeTexture;
void main() {
// just display our texture
float edge = smoothstep(0.4, 0.5, length(vVertexNormal)); // HERE CHANGES
gl_FragColor = texture2D(planeTexture, vTextureCoord);
}
`;
for(let i = 0; i < planeElements.length; i++) {
const plane = new Plane(curtains, planeElements[i], {
vertexShader: vs,
fragmentShader: fs,
});
planes.push(plane);
handlePlanes(i);
}
function handlePlanes(index) {
const plane = planes[index];
plane.onReady(() => {
planeDrawn++;
applyPlanesParallax(index);
if(index === planes.length - 1) {
document.body.classList.add("planes-loaded");
}
}).onAfterResize(() => {
applyPlanesParallax(index);
}).onRender(() => {
plane.scale.y = 1 + Math.abs(scrollEffect) / 300;
plane.textures[0].scale.y = 1 + Math.abs(scrollEffect) / 150;
}).onReEnterView(() => {
planeDrawn++;
}).onLeaveView(() => {
planeDrawn--;
});
}
function applyPlanesParallax(index) {
const windowHeight = curtains.getBoundingRect().height / 1.2;
const planeBoundingRect = planes[index].getBoundingRect();
const planeOffsetTop = planeBoundingRect.top + planeBoundingRect.height / 2;
const parallaxEffect = (planeOffsetTop - windowHeight / 2) / windowHeight;
planes[index].relativeTranslation.y = parallaxEffect * windowHeight / 4;
}
// post processing
const firstFs = `
precision mediump float;
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
uniform sampler2D uRenderTexture;
uniform float uScrollEffect;
void main() {
// invert colors
vec4 scene = texture2D(uRenderTexture, vTextureCoord);
vec4 invertedColors = texture2D(uRenderTexture, vTextureCoord);
if(
vTextureCoord.x > 0.625 && vTextureCoord.x < 0.875 && vTextureCoord.y > 0.625 && vTextureCoord.y < 0.875
|| vTextureCoord.x > 0.125 && vTextureCoord.x < 0.375 && vTextureCoord.y > 0.125 && vTextureCoord.y < 0.375
) {
invertedColors.rgb = vec3(1.0 - invertedColors.rgb);
}
vec4 finalColor = mix(scene, invertedColors, abs(uScrollEffect) / 60.0);
gl_FragColor = finalColor;
}
`;
const firstShaderPassParams = {
fragmentShader: firstFs,
uniforms: {
scrollEffect: {
name: "uScrollEffect",
type: "1f",
value: 0,
},
},
};
const firstShaderPass = new ShaderPass(curtains, firstShaderPassParams);
firstShaderPass.onRender(() => {
firstShaderPass.uniforms.scrollEffect.value = scrollEffect;
});
const secondFs = `
#ifdef GL_ES
precision mediump float;
#endif
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
uniform sampler2D uRenderTexture;
uniform float uScrollEffect;
void main() {
vec2 textureCoords = vTextureCoord;
vec2 texCenter = vec2(0.5, 0.5);
// distort around scene center
textureCoords += vec2(texCenter - textureCoords).xy * sin(distance(texCenter, textureCoords)) * uScrollEffect / 175.0;
gl_FragColor = texture2D(uRenderTexture, textureCoords);
}
`;
const secondShaderPassParams = {
fragmentShader: secondFs,
uniforms: {
scrollEffect: {
name: "uScrollEffect",
type: "1f",
value: 0,
},
},
};
const secondShaderPass = new ShaderPass(curtains, secondShaderPassParams);
secondShaderPass.onRender(() => {
secondShaderPass.uniforms.scrollEffect.value = scrollEffect;
});
});
Simple explanation of what the script is doing :
The script starts by importing the necessary modules from the Curtains.js library, including Curtains, Plane, ShaderPass, Vec2, and Vec3. Then it adds an event listener to the window object that triggers a function on the load event.
Inside the load event function, the script initializes the Curtains object by passing a configuration object to it, which includes a reference to the container element where the WebGL canvas should be created, as well as some optional parameters like antialias and pixelRatio.
After that, the script defines several event handlers that are used to handle rendering, scrolling, errors, and context loss events.
Next, the script creates several Plane objects using the Plane constructor of the Curtains library, passing a configuration object to it that includes various options for the plane's appearance and behavior, including vertex and fragment shaders, geometry, width and height segments, and uniforms.
The script also defines two custom shaders, one for the vertex shader and one for the fragment shader. These shaders are written in GLSL, a language used to write graphics shaders, and define how the vertices and fragments of the 3D objects should be processed.
Finally, the script defines several functions that are used to handle various events and apply parallax and distortion scrolling effects to the planes.
- To make the plane has rounded edges tried to replace the plane with an other geometry using a simple function like this one :
const w = 16; // width
const h = 9; // height
const r = 2; // radius corner
const s = 18; // smoothness
// helper const's
const wi = w / 2 - r;
const hi = h / 2 - r;
const w2 = w / 2;
const h2 = h / 2;
const ul = r / w;
const ur = ( w - r ) / w;
const vl = r / h;
const vh = ( h - r ) / h;
let positions = [
-wi, -h2, 0, wi, -h2, 0, wi, h2, 0,
-wi, -h2, 0, wi, h2, 0, -wi, h2, 0,
-w2, -hi, 0, -wi, -hi, 0, -wi, hi, 0,
-w2, -hi, 0, -wi, hi, 0, -w2, hi, 0,
wi, -hi, 0, w2, -hi, 0, w2, hi, 0,
wi, -hi, 0, w2, hi, 0, wi, hi, 0
];
let uvs = [
ul, 0, ur, 0, ur, 1,
ul, 0, ur, 1, ul, 1,
0, vl, ul, vl, ul, vh,
0, vl, ul, vh, 0, vh,
ur, vl, 1, vl, 1, vh,
ur, vl, 1, vh, ur, vh
];
let phia = 0;
let phib, xc, yc, uc, vc;
for ( let i = 0; i < s * 4; i ++ ) {
phib = Math.PI * 2 * ( i + 1 ) / ( 4 * s );
xc = i < s || i >= 3 * s ? wi : - wi;
yc = i < 2 * s ? hi : -hi;
positions.push( xc, yc, 0, xc + r * Math.cos( phia ), yc + r * Math.sin( phia ), 0, xc + r * Math.cos( phib ), yc + r * Math.sin( phib ), 0 );
uc = xc = i < s || i >= 3 * s ? ur : ul;
vc = i < 2 * s ? vh : vl;
uvs.push( uc, vc, uc + ul * Math.cos( phia ), vc + vl * Math.sin( phia ), uc + ul * Math.cos( phib ), vc + vl * Math.sin( phib ) );
phia = phib;
}
const geometry = new THREE.BufferGeometry( );
geometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) );
geometry.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
However, upon replacing the original plane with the rounded edges geometry, the script failed to execute and the intended effect was not achieved.
So, does anyone know how can i make the edges of the plane rounded while still keeping the distortion and scale effects of curtainjs working ?