当前位置: 首页 > article >正文

Three.js 实现云状特效

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

在这里插入图片描述

在本文中,我们将从一个基于 GLSL 的云状特效 shader 出发,讲解 shader 的实现过程和所涉及的数学原理。

一、Shader 实现细节解析

1. 宏定义与时间控制

我们在 shader 开始部分定义了两个常用的数学常量:

#define PI2 6.28318530718
#define PI 3.1416
  • 同时,iTime 作为 uniform 变量传入,使得图案能随时间动态变化,从而实现动画效果。

2. 坐标变换与周期函数

在 vorocloud 函数中,首先通过下面这行代码对输入坐标 p 进行周期性处理:

vec2 pp = cos(vec2(p.x * 14.0, (16.0 * p.y + cos(floor(p.x * 30.0)) + iTime * PI2)));
  • 周期性函数:利用 cos 函数生成周期性变化,调整水平方向和垂直方向上的频率。
  • 离散扰动:通过 floor 与 cos 的组合,引入了不规则性,使得最终效果更接近自然云彩的随机分布。
  • 时间因子:引入 iTime,使图案随时间平滑过渡,产生动画效果。

3. 非线性空间扭曲

接下来对坐标 p 进行非线性扭曲:

p = cos(p * 12.1 + pp * 10.0 + 0.5 * cos(pp.x * 10.0));

这种多重余弦变换使得原始坐标发生复杂扰动,从而获得更为自然和丰富的纹理变化。

4. 计算距离并确定最小值

我们预设了 4 个参考点:

vec2 pts[4];
pts[0] = vec2(0.5, 0.6);
pts[1] = vec2(-0.4, 0.4);
pts[2] = vec2(0.2, -0.7);
pts[3] = vec2(-0.3, -0.4);

通过遍历这些点,并根据当前计算得到的 pp 坐标计算欧几里得距离:


float d = 5.0;
for(int i = 0; i < 4; i++){
pts[i].x += 0.03 _ cos(float(i)) + p.x;
pts[i].y += 0.03 _ sin(float(i)) + p.y;
d = min(d, distance(pts[i], pp));
}

数学上,我们使用的欧几里得距离公式为:

  • 这里,d 表示当前点与各参考点中最接近的距离。

5. 指数衰减与量化

在获得最小距离后,通过以下公式计算最终的亮度因子:

f = 2.0 * pow(1.0 - 0.3 * d, 13.0);
f = min(f, 1.0);

数学公式推导如下:

  • 线性衰减:首先计算 1.0 - 0.3 * d,使得距离越大,值越小。

  • 非线性提升:对上述值取 13 次幂,公式为:

这种处理使得靠近目标区域的值迅速趋向于 1,而远离区域迅速衰减到 0。

放缩与截断:最终乘以 2.0 并用 min 限制最大值为 1.0,确保亮度值始终在 [0, 1] 区间内。

6. 最终图像构建

在 scene 函数中,我们通过两次调用 vorocloud 函数,在不同尺度上生成图案,并根据返回值对颜色通道进行混合:

vec4 col = vec4(0.0);
col.g += 0.02;

float v = vorocloud(p);
v = 0.2 _ floor(v _ 5.0);

col.r += 0.1 _ v;
col.g += 0.6 _ v;
col.b += 0.5 \* pow(v, 5.0);

v = vorocloud(p _ 2.0);
v = 0.2 _ floor(v \* 5.0);

col.r += 0.1 _ v;
col.g += 0.2 _ v;
col.b += 0.01 \* pow(v, 5.0);
  • 量化效果:利用 floor 函数将连续值离散化,产生分级的视觉效果。
  • 多层混合:在不同尺度下叠加效果,增强了图像的层次感和细节。

二、数学原理总结

1. 周期函数与坐标扭曲:

利用 cos 与 sin 函数,使得图案在各个方向上呈现出周期性和非线性变形效果。数学上,这是通过将线性坐标映射到周期函数上实现的,从而引入无限延展的波动性。

2. 欧几里得距离计算

使用基本的欧几里得距离公式确定当前像素与各参考点的最小距离,从而在空间中划分不同的影响区域。

3. 指数衰减与非线性变换

采用指数函数 𝑓=2×(1−0.3𝑑)13f=2×(1−0.3d)13 使得靠近参考点的区域迅速达到高亮,而远离区域则快速衰减到 0,为图像添加了锐利的边缘效果。

3 量化处理

通过对连续亮度值进行离散化处理,模拟出一种复古或艺术化的调色板效果,使得整体视觉更具层次和风格。

三、完整代码

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

// 创建场景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000,
);
camera.position.z = 1.5;

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

const uniforms = {
  iTime: { value: 0.0 },
  iResolution: {
    value: new THREE.Vector2(window.innerWidth, window.innerHeight),
  },
};

const material = new THREE.ShaderMaterial({
  uniforms,
  vertexShader: `
        varying vec2 vUv;
        void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    `,
  fragmentShader: `
        #define PI2 6.28318530718
        #define PI 3.1416
        
        uniform float iTime;
        uniform vec2 iResolution;
        
        varying vec2 vUv;
        
        float vorocloud(vec2 p){
            float f = 0.0;
            vec2 pp = cos(vec2(p.x * 14.0, (16.0 * p.y + cos(floor(p.x * 30.0)) + iTime * PI2)));
            p = cos(p * 12.1 + pp * 10.0 + 0.5 * cos(pp.x * 10.0));
            
            vec2 pts[4];
            pts[0] = vec2(0.5, 0.6);
            pts[1] = vec2(-0.4, 0.4);
            pts[2] = vec2(0.2, -0.7);
            pts[3] = vec2(-0.3, -0.4);
            
            float d = 5.0;
            
            for(int i = 0; i < 4; i++){
                pts[i].x += 0.03 * cos(float(i)) + p.x;
                pts[i].y += 0.03 * sin(float(i)) + p.y;
                d = min(d, distance(pts[i], pp));
            }
            
            f = 2.0 * pow(1.0 - 0.3 * d, 13.0);
            f = min(f, 1.0);
            
            return f;
        }
        
        void main() {
            vec2 UV = vUv;
            vec2 p = UV - vec2(0.5);
            
            vec4 col = vec4(0.0);
            col.g += 0.02;
            
            float v = vorocloud(p);
            v = 0.2 * floor(v * 5.0);
            
            col.r += 0.1 * v;
            col.g += 0.6 * v;
            col.b += 0.5 * pow(v, 5.0);
            
            v = vorocloud(p * 2.0);
            v = 0.2 * floor(v * 5.0);
            
            col.r += 0.1 * v;
            col.g += 0.2 * v;
            col.b += 0.01 * pow(v, 5.0);
            
            col.a = 1.0;
            
            gl_FragColor = col;
        }
    `,
});

const plane = new THREE.PlaneGeometry(2, 2);
const quad = new THREE.Mesh(plane, material);
scene.add(quad);

const controls = new OrbitControls(camera, renderer.domElement);

function animate() {
  requestAnimationFrame(animate);
  uniforms.iTime.value += 0.01;
  renderer.render(scene, camera);
}

animate();

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

总结

本文详细解析了如何利用数学工具(如周期函数、欧几里得距离、指数函数)构造出具有丰富层次和动态效果的云状特效。通过将 shader 实现我们可以轻松地将复杂的图形效果应用于 WebGL 项目中,同时也加深了对基于数学函数生成复杂图形原理的理解。


http://www.kler.cn/a/586832.html

相关文章:

  • Global Mapper 多功能的GIS软件
  • Python 本地翻译库 googletrans
  • 华为机试牛客刷题之HJ5 进制转换
  • 「为爱发电」的硬核打开方式,涂鸦智能用AIoT引领智慧能源变革
  • The Rust Programming Language 学习 (五)
  • 人工智能技术与应用演讲(61页PPT)(文末有下载方式)
  • [已解决]UserWarning: __floordiv__ is deprecated
  • memcpy,memmove,strstr的模拟实现
  • 微店商品详情页的常见结构及爬虫解析方法
  • PCIe信号传输的幕后:HCSL与LP-HCSL深度解析
  • Canoe Panel基础功能介绍
  • ERROR: There are no scenarios; must have at least one.
  • 2024华东师范大学计算机复试上机真题
  • OpenCV基础【图像和视频的加载与显示】
  • 蓝光三维扫描技术:手机闪光灯模块全尺寸3D检测的精准解决方案
  • 【数据结构初阶第十九节】八大排序系列(下篇)—[详细动态图解+代码解析]
  • nodejs42 (UI相关) : svg的viewBox属性
  • Mamba组件:状态空间模型简介
  • MySQL开发陷阱与最佳实践:第1章:MySQL开发基础概述-1.2 MySQL开发环境搭建
  • HarmonyOS NEXT - 网络请求问题(http)