更多有趣示例 尽在小红砖社区
<a href="https://github.com/vaneenige/three.phenomenon" target="_blank">THREE.Phenomenona>
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
letter-spacing: 0;
font-style: normal;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
canvas {
position: fixed;
width: 100%;
height: 100%;
image-rendering: pixelated;
}
a {
position: fixed;
margin: 16px;
font-size: 14px;
font-weight: 500;
text-align: center;
text-decoration: none;
text-transform: uppercase;
border-radius: 0;
cursor: pointer;
outline: none;
padding: 8px 20px;
border: 2px solid #E2A9E5;
color: #E2A9E5;
z-index: 1;
font-weight: bold;
}
a:hover,
a:focus {
color: white;
background-color: #FFD700;
}
function getArrayWithNoise(array, noise) {
return array.map(item => item + getRandomBetween(noise));
}
function getRandomBetween(value) {
const floor = -value;
return floor + Math.random() * value * 2;
}
let instance;
function createInstance() {
const duration = 0.7;
const geometry = new THREE.IcosahedronGeometry(0.8, 0);
// const multiplier = 50;
const material = new THREE.MeshPhongMaterial({
color: "#E2A9E5",
emissive: "#632C65",
flatShading: true,
shininess: 100
});
const castShadow = true;
const S = 20;
const positions = [];
for (let z = -S; z < S; z += 1) {
for (let x = -S; x < S; x += 1) {
positions.push(x, z);
}
}
const multiplier = positions.length / 2;
const attributes = [
{
name: "aPositionStart",
size: 3,
data: i => [positions[i * 2], 0, positions[i * 2 + 1]]
},
{
name: "aControlPointOne",
data: i => [positions[i * 2], 0, positions[i * 2 + 1]],
size: 3
},
{
name: "aControlPointTwo",
data: i => [positions[i * 2], 0, positions[i * 2 + 1]],
size: 3
},
{
name: "aPositionEnd",
size: 3,
data: i => [positions[i * 2], 0, positions[i * 2 + 1]]
},
{
name: "aOffset",
data: i => [i * ((1 - duration) / (multiplier - 1))],
size: 1
}
];
const uniforms = {
time: {
value: 0.25
}
};
const vertex = `
attribute vec3 aPositionStart;
attribute vec3 aControlPointOne;
attribute vec3 aControlPointTwo;
attribute vec3 aPositionEnd;
attribute float aOffset;
uniform float time;
float easeInOutSin(float t){
return (1.0 + sin(${Math.PI} * t - ${Math.PI} / 2.0)) / 2.0;
}
vec4 quatFromAxisAngle(vec3 axis, float angle) {
float halfAngle = angle * 0.5;
return vec4(axis.xyz * sin(halfAngle), cos(halfAngle));
}
vec3 rotateVector(vec4 q, vec3 v) {
return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
}
vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3 d, float t) {
return mix(mix(mix(a, b, t), mix(b, c, t), t), mix(mix(b, c, t), mix(c, d, t), t), t);
}
void main(){
float tProgress = easeInOutSin(min(1.0, max(0.0, (time - aOffset)) / ${duration}));
vec4 quatX = quatFromAxisAngle(vec3(1.0, 0.0, 0.0), -10.0 * tProgress);
vec4 quatY = quatFromAxisAngle(vec3(0.0, 0.0, 0.0), -5.0 * tProgress);
vec3 basePosition = rotateVector(quatX, rotateVector(quatY, position));
vec3 newPosition = bezier4(aPositionStart, aControlPointOne, aControlPointTwo, aPositionEnd, tProgress);
float scale = tProgress * 2.0 - 1.0;
scale = 1.0 - scale * scale;
basePosition *= scale;
gl_Position = basePosition + newPosition;
}
`;
// vNormal = rotateVector(quatX, vNormal);
instance = new THREE.Phenomenon({
geometry,
multiplier,
material,
castShadow,
attributes,
uniforms,
vertex
});
scene.add(instance.mesh);
}
// Base structure --
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setClearColor(0xFFD700, 0);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(1);
document.querySelector("body").appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
40,
window.innerWidth / window.innerHeight,
0.1,
10000
);
camera.position.set(0, 20 * 1, 35 * 1);
camera.lookAt(scene.position);
scene.add(camera);
const ambientLight = new THREE.AmbientLight("#FFD700", 0.1);
scene.add(ambientLight);
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(1000, 1000),
new THREE.MeshPhongMaterial({
emissive: "#4B384C"
})
);
plane.receiveShadow = true;
plane.position.y = -15;
plane.rotation.x = Math.PI * -0.5;
scene.add(plane);
const light = new THREE.SpotLight(0xffffff, 3, 80, Math.PI * 0.25, 1, 2);
light.position.set(0, 40, 0);
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 31;
scene.add(light);
function animate() {
requestAnimationFrame(animate);
if (instance.uniforms.time.value >= 1) {
instance.uniforms.time.value = 0;
}
instance.uniforms.time.value += 1 / (6 * 60);
renderer.render(scene, camera);
}
createInstance();
animate();
window.addEventListener(
"resize",
() => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
},
false
);