Hi Three Commu !
I try to achieve a scene in Webflow with Threejs. On one hand I made a sphere with a liquid distortion effect using fragment shader material.
<!-- Include Three.js -->
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script>
<!-- Include OrbitControls -->
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<script>
// Create a scene
const scene = new THREE.Scene();
// Create a camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 2;
// Create a renderer with alpha enabled for transparency and add it to the DOM
const renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Add OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // enables smooth controls
controls.dampingFactor = 0.25;
controls.screenSpacePanning = false;
//controls.minDistance = 3; // minimum distance to zoom
//controls.maxDistance = 3; // maximum distance to zoom
// Limit vertical rotation to ±30 degrees
const verticalLimit = Math.PI / 6; // 30 degrees in radians
controls.minPolarAngle = Math.PI / 2 - verticalLimit; // 30 degrees from top
controls.maxPolarAngle = Math.PI / 2 + verticalLimit; // 30 degrees from bottom
// Create the icosahedron geometry
const ico = new THREE.IcosahedronGeometry(1, 256);
// Load a texture (image)
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('https://myportfolio-webflow.s3.eu-west-3.amazonaws.com/Scene/wall_stickers.jpg');
// Custom shader material
const customShaderMaterial = new THREE.ShaderMaterial({
uniforms: {
'texture1': { value: texture },
'tSize': { value: new THREE.Vector2(256, 256) },
'center': { value: new THREE.Vector2(0.5, 0.5) },
'angle': { value: 0.0},
'scale': { value: 1.0 },
'time': { value: 0.0 },
'progress': { value: 0.0 },
'metalness': { value: 1.0 }, // Added metalness uniform
'roughness': { value: 0.0 } // Added roughness uniform
},
vertexShader: `
varying vec2 vUv;
varying vec3 vNormal;
void main() {
vUv = uv;
vNormal = normal;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec2 center;
uniform float angle;
uniform float scale;
uniform vec2 tSize;
uniform sampler2D texture1;
uniform float time;
uniform float progress;
uniform float metalness;
uniform float roughness;
varying vec2 vUv;
varying vec3 vNormal;
float pattern() {
float s = sin(angle + time), c = cos(angle + time);
vec2 tex = vUv * tSize - center;
vec2 point = vec2(c * tex.x - s * tex.y, s * tex.x + c * tex.y) * scale;
return (sin(point.x) * sin(point.y)) * 4.0;
}
void main() {
vec2 newUV = vUv;
vec3 p = vNormal;
p += 0.1 * cos(scale * 3.0 * p.yzx + time + vec3(1.2, 3.4, 2.1));
p += 0.1 * cos(scale * 3.7 * p.yzx + 1.4 * time + vec3(2.2, 3.4, 1.7));
p += 0.1 * cos(scale * 5.0 * p.yzx + 2.6 * time + vec3(4.2, 1.4, 3.1));
p += 0.3 * cos(scale * 7.0 * p.yzx + 3.6 * time + vec3(10.2, 3.4, 8.1));
newUV.x = mix(vUv.x, length(p), progress);
newUV.y = mix(vUv.y, 0.0, progress);
vec4 color = texture2D(texture1, newUV);
// Simulate metalness effect
vec3 metalColor = mix(vec3(0.04), color.rgb, metalness);
vec3 finalColor = mix(color.rgb, metalColor, metalness);
gl_FragColor = color;
}
`
});
// Create a mesh with the geometry and custom shader material
const mesh = new THREE.Mesh(ico, customShaderMaterial);
scene.add(mesh);
// Animation loop
function animate(time) {
requestAnimationFrame(animate);
// Update uniforms
customShaderMaterial.uniforms['time'].value = time * 0.0002; // Convert time to seconds
customShaderMaterial.uniforms['progress'].value = 0.2; // Oscillate progress between 0 and 1
// Update controls
controls.update();
// Render scene
renderer.render(scene, camera);
}
animate();
// Handle window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
On the other hand I found a morphing ‘sphere to plane’ script in codepen.
<div id="threejs-container" style="width: 100%; height: 100vh;"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x404040);
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('threejs-container').appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var planeGeom = new THREE.PlaneBufferGeometry(Math.PI * 5, Math.PI * 2.5, 36, 18);
planeGeom.morphAttributes.position = [];
var sphereFormation = [];
var uvs = planeGeom.attributes.uv;
var uv = new THREE.Vector2();
var t = new THREE.Vector3();
for (let i = 0; i < uvs.count; i++) {
uv.fromBufferAttribute(uvs, i);
t.setFromSphericalCoords(2.5, Math.PI * (1 - uv.y), Math.PI * (uv.x - 0.5) * 2);
sphereFormation.push(t.x, t.y, t.z);
}
planeGeom.morphAttributes.position[0] = new THREE.Float32BufferAttribute(sphereFormation, 3);
var planeMat = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('https://images.unsplash.com/photo-1596263576925-d90d63691097?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3422&q=80'),
morphTargets: true,
side: THREE.DoubleSide
});
var spherePlane = new THREE.Mesh(planeGeom, planeMat);
scene.add(spherePlane);
spherePlane.morphTargetInfluences[0] = 0;
var clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
spherePlane.morphTargetInfluences[0] = Math.sin(clock.getElapsedTime()) * 0.5 + 0.5;
renderer.render(scene, camera);
});
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
The goal is to combine the 2 scripts using fragment shaders to have the liquid sphere unbending to plane on click similar to that :
Thanks in advance for your help