自学内容网 自学内容网

使用 Three.js 实现动态爱心效果

大家好!我是 [数擎 AI],一位热爱探索新技术的前端开发者,在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步!
开发领域:前端开发 | AI 应用 | Web3D | 元宇宙
技术栈:JavaScript、React、ThreeJs、WebGL、Go
经验经验:6 年+ 前端开发经验,专注于图形渲染和 AI 技术
经验经验演示地址
开源项目AI 智简未来晓智元宇宙数字孪生引擎 源码地址

在本文中,我们将深入解析如何将 GLSL 片段着色器代码嵌入到 Three.js 项目中,从而创建动态渲染效果。

代码分解与实现过程

以下是片段着色器的实现过程分步解析:

1. 定义 Uniforms

uniform vec3 iResolution;
uniform float iTime;
  • iResolution:提供屏幕分辨率,用于将像素坐标标准化。
  • iTime:表示动画运行的时间(秒),用于驱动动态效果。

2. 屏幕坐标标准化

vec2 p = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y, iResolution.x);
  • 将当前像素位置标准化为以屏幕中心为原点的坐标 p,范围通常为 [-1, 1]。

3. 背景颜色计算

vec3 bcol = vec3(1.0, 0.8, 0.7 - 0.07 * p.y) * (1.0 - 0.25 * length(p));

使用线性渐变和距离计算生成动态背景颜色,靠近中心颜色较亮,远离中心逐渐变暗。

4. 动画参数计算

float tt = mod(iTime, 1.5) / 1.5;
float ss = pow(tt, 0.2) * 0.5 + 0.5;
ss = 1.0 + ss * 0.5 * sin(tt * 6.2831 * 3.0 + p.y * 0.5) * exp(-tt * 4.0);
p *= vec2(0.5, 1.5) + ss * vec2(0.5, -0.5);

  • tt:归一化时间,用于生成循环动画。
  • ss:通过指数和平滑函数生成动画效果。
  • 坐标系 p 随时间动态缩放和偏移,呈现周期性变化。
  1. 动态形状定义
p.y -= 0.25;
float a = atan(p.x, p.y) / 3.141593;
float r = length(p);
float h = abs(a);
float d = (13.0 * h - 22.0 * h * h + 10.0 * h * h * h) / (6.0 - 5.0 * h);

通过极坐标定义复杂曲线形状:

  • a:点的角度,归一化到 [−1, 1]。
  • r:点到原点的距离。
  • d:形状边界的数学描述,使用多项式函数控制曲线。
  1. 前景颜色与混合
float s = 0.75 + 0.75 * p.x;
s *= 1.0 - 0.4 * r;
s = 0.3 + 0.7 * s;
s *= 0.5 + 0.5 * pow(1.0 - clamp(r / d, 0.0, 1.0), 0.1);
vec3 hcol = vec3(1.0, 0.4 * r, 0.3) * s;

vec3 col = mix(bcol, hcol, smoothstep(-0.01, 0.01, d - r));

整合与实现

import * as THREE from 'three';
// Create the scene, camera, and renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 2;

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Define the shader material
const shaderMaterial = new THREE.ShaderMaterial({
  uniforms: {
    iResolution: { value: new THREE.Vector3(window.innerWidth, window.innerHeight, 1) },
    iTime: { value: 0.0 }
  },
  fragmentShader: `
    uniform vec3 iResolution;
    uniform float iTime;

    void mainImage(out vec4 fragColor, in vec2 fragCoord) {
        vec2 p = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y, iResolution.x);

        // background color
        vec3 bcol = vec3(1.0, 0.8, 0.7 - 0.07 * p.y) * (1.0 - 0.25 * length(p));

        // animate
        float tt = mod(iTime, 1.5) / 1.5;
        float ss = pow(tt, 0.2) * 0.5 + 0.5;
        ss = 1.0 + ss * 0.5 * sin(tt * 6.2831 * 3.0 + p.y * 0.5) * exp(-tt * 4.0);
        p *= vec2(0.5, 1.5) + ss * vec2(0.5, -0.5);

        // shape
        p.y -= 0.25;
        float a = atan(p.x, p.y) / 3.141593;
        float r = length(p);
        float h = abs(a);
        float d = (13.0 * h - 22.0 * h * h + 10.0 * h * h * h) / (6.0 - 5.0 * h);

        // color
        float s = 0.75 + 0.75 * p.x;
        s *= 1.0 - 0.4 * r;
        s = 0.3 + 0.7 * s;
        s *= 0.5 + 0.5 * pow(1.0 - clamp(r / d, 0.0, 1.0), 0.1);
        vec3 hcol = vec3(1.0, 0.4 * r, 0.3) * s;

        vec3 col = mix(bcol, hcol, smoothstep(-0.01, 0.01, d - r));

        fragColor = vec4(col, 1.0);
    }

    void main() {
        vec4 color;
        mainImage(color, gl_FragCoord.xy);
        gl_FragColor = color;
    }
  `,
});

// Create a plane geometry and add it to the scene
const geometry = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight);
const plane = new THREE.Mesh(geometry, shaderMaterial);
scene.add(plane);

// Resize handler
window.addEventListener('resize', () => {
  renderer.setSize(window.innerWidth, window.innerHeight);
  shaderMaterial.uniforms.iResolution.value.set(window.innerWidth, window.innerHeight, 1);
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
});

// Animation loop
const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);

  // Update the time uniform
  shaderMaterial.uniforms.iTime.value = clock.getElapsedTime();

  renderer.render(scene, camera);
}

animate();


总结

这篇文章展示了如何将复杂的 GLSL 代码转化为 Three.js 的动态渲染效果。通过这种方式,你可以将艺术创意与数学结合,为项目增添独特的视觉效果。
在这里插入图片描述


原文地址:https://blog.csdn.net/qq_34820371/article/details/145172032

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!