电影动画shader解析与实现
着色器代码解析
大家好!我是 [数擎AI],一位热爱探索新技术的前端开发者,在这里分享前端和Web3D、AI技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步!
开发领域:前端开发 | AI 应用 | Web3D | 元宇宙
技术栈:JavaScript、React、ThreeJs、WebGL、Go
经验经验:6 年+ 前端开发经验,专注于图形渲染和 AI 技术
经验经验:演示地址
开源项目:AI智简未来、晓智元宇宙、数字孪生引擎 源码地址
提供的 GLSL 着色器代码通过光线行进算法(Ray Marching)生成一个动态的三维场景。以下是代码的核心功能概述:
1. 平滑联合(Smooth Union)
opSmoothUnion 函数用于平滑地融合两个几何体,使它们看起来像一个整体。
公式如下:
float opSmoothUnion( float d1, float d2, float k ) {float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );return mix( d2, d1, h ) - k*h*(1.0-h);
}
其中:
- d1 和 𝑑2 表示两个几何体的距离场值。
- k:控制平滑程度的参数。
- mix 函数用于插值,clamp 函数限制范围。
2. 符号距离函数(SDF)
sdSphere 函数定义了一个点到球体表面的距离:
float sdSphere( vec3 p, float s ) {return length(p) - s;
}
- 输入 p 是点的三维坐标,s 是球体的半径。
- 返回值为点到球表面的最短距离。
3. 场景组合
map 函数动态地组合多个球体,通过时间参数让它们产生动画:
float map(vec3 p) {float d = 2.0;for (int i = 0; i < 16; i++) {float fi = float(i);float time = iTime * (fract(fi * 412.531 + 0.513) - 0.5) * 2.0;d = opSmoothUnion(sdSphere(p + sin(time + fi * vec3(52.5126, 64.62744, 632.25)) * vec3(2.0, 2.0, 0.8),mix(0.5, 1.0, fract(fi * 412.531 + 0.5124))),d,0.4);}return d;
}
- 通过循环,生成 16 个动态移动的球体,并用 opSmoothUnion 进行平滑组合。
- 球体的位置随时间(iTime)变化,实现动画效果。
4. 光线行进
在 mainImage 中实现了光线行进算法,通过迭代寻找光线与几何体的交点:
vec3 rayOri = vec3(...); // 光线起点
vec3 rayDir = vec3(0.0, 0.0, -1.0); // 光线方向
for (int i = 0; i < 64; i++) { p = rayOri + rayDir * depth; float dist = map(p); depth += dist; if (dist < 1e-6) break;
}
- depth 表示光线当前行进的深度。
- map§ 计算光线与场景的最近距离。
完整代码
import * as THREE from 'three';
import React, { useEffect, useRef } from 'react';const CineShader: React.FC = () => {const cineShaderRef = useRef<any>();useEffect(() => {// 初始化场景、相机和渲染器const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000,);const renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 设置相机位置camera.position.z = 5;// 创建自定义 ShaderMaterialconst shaderMaterial = new THREE.ShaderMaterial({uniforms: {iResolution: {value: new THREE.Vector2(window.innerWidth, window.innerHeight),},iTime: { value: 0.0 },},vertexShader: `varying vec2 vUv;void main() {vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}`,fragmentShader: `uniform vec2 iResolution;uniform float iTime;varying vec2 vUv;// 平滑联合float opSmoothUnion(float d1, float d2, float k) {float h = clamp(0.5 + 0.5 * (d2 - d1) / k, 0.0, 1.0);return mix(d2, d1, h) - k * h * (1.0 - h);}// 球体 SDFfloat sdSphere(vec3 p, float s) {return length(p) - s;}// 场景组合float map(vec3 p) {float d = 2.0;for (int i = 0; i < 16; i++) {float fi = float(i);float time = iTime * (fract(fi * 412.531 + 0.513) - 0.5) * 2.0;d = opSmoothUnion(sdSphere(p + sin(time + fi * vec3(52.5126, 64.62744, 632.25)) * vec3(2.0, 2.0, 0.8), mix(0.5, 1.0, fract(fi * 412.531 + 0.5124))),d,0.4);}return d;}// 计算法线vec3 calcNormal(vec3 p) {const float h = 1e-5;const vec2 k = vec2(1, -1);return normalize(k.xyy * map(p + k.xyy * h) +k.yyx * map(p + k.yyx * h) +k.yxy * map(p + k.yxy * h) +k.xxx * map(p + k.xxx * h));}void main() {vec2 uv = gl_FragCoord.xy / iResolution;uv = uv * 2.0 - 1.0; // 将坐标转换为 [-1, 1]// 设置光线起点和方向vec3 rayOri = vec3(uv * vec2(iResolution.x / iResolution.y, 1.0) * 6.0, 3.0);vec3 rayDir = vec3(0.0, 0.0, -1.0);// 光线行进float depth = 0.0;vec3 p;for (int i = 0; i < 64; i++) {p = rayOri + rayDir * depth;float dist = map(p);depth += dist;if (dist < 1e-6) {break;}}depth = min(6.0, depth); // 限制最大深度// 计算颜色vec3 n = calcNormal(p);float b = max(0.0, dot(n, vec3(0.577))); // 简单光照vec3 col = (0.5 + 0.5 * cos((b + iTime * 3.0) + uv.xyx * 2.0 + vec3(0, 2, 4))) * (0.85 + b * 0.35);col *= exp(-depth * 0.15); // 添加深度雾效gl_FragColor = vec4(col, 1.0);}`,});// 创建平面并添加到场景const geometry = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight);const plane = new THREE.Mesh(geometry, shaderMaterial);scene.add(plane);// 动画渲染循环function animate() {requestAnimationFrame(animate);// 更新时间shaderMaterial.uniforms.iTime.value += 0.1;renderer.render(scene, camera);}animate();// 响应窗口大小调整window.addEventListener('resize', () => {renderer.setSize(window.innerWidth, window.innerHeight);shaderMaterial.uniforms.iResolution.value.set(window.innerWidth,window.innerHeight,);camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();});}, []);return <div ref={cineShaderRef}/>;
};export default CineShader;
相关文章:
电影动画shader解析与实现
着色器代码解析 大家好!我是 [数擎AI],一位热爱探索新技术的前端开发者,在这里分享前端和Web3D、AI技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步! 开发领域:…...
蓝桥杯 第十五届 研究生组 B题 召唤数学精灵
问题描述: 数学家们发现了两种用于召唤强大的数学精灵的仪式,这两种仪式分别被称为累加法仪式 A(n) 和累乘法仪式 B(n)。累加法仪式 A(n) 是将从 1 到 n 的所有数字进行累加求和,即:A(n)12⋯n累乘法仪式 B(n) 则是将从 1 到 n 的所…...
在 Go 应用中 如何像 FastAPI 一样优雅地构建控制器
文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons:JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram,自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 ? 5 IDEA必装的插件&…...
用户界面的UML建模11
然而,在用户界面方面,重要的是要了解《boundary》类是如何与这个异常分层结构进行关联的。 《exception》类的对象可以作为《control》类的对象。因此,《exception》类能够聚合《boundary》类。 参见图12,《exception》Database…...
历代iPhone运行内存大小和电池容量信息
系列设备名称充电端口标配充电线PD快充无线充电 (W)标配充电器电池容量 (mAh)发布时间RAM运存iPhone 16iPhone 16 Pro MaxUSB Type-CUSB-C to USB-C支持25无47472024/9/108GB LPDDR5XiPhone 16 ProUSB Type-CUSB-C to USB-C支持25无35772024/9/108GB LPDDR5XiPhone 16 PlusUSB …...
计算机网络之---物理层设备
什么是物理层设备 物理层设备是指负责数据在物理媒介上传输的硬件设备,它们主要处理数据的转换、信号的传输与接收,而不涉及数据的内容或意义。常见的物理层设备包括网卡、集线器、光纤收发器、调制解调器等。 物理层设备有哪些 1、网卡(N…...
57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景
57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景 实现效果 该案例实现了使用Three.js创建一个带有聚光灯和旋转立方体的3D场景。 知识点 WebGLRenderer(WebGL渲染器) THREE.WebGLRenderer 是 Three.js 中用于将场景渲染为 WebGL 内容的核…...
第八讲 一元函数积分学的概念和性质
不定积分 1.原函数与不定积分 需知道:F(X)可导必连续 2.原函数(不定积分)存在定理 (1)连续函数f(x)必有原函数F(x) (2)含有第一类间断点和无穷间断点的函数f(x)在包含该间断点的区间内必没有原函数F(x). 速记:只有震荡可能有…...
ADMM原理及应用
文章目录 1. ADMM原理1.1. 数学形式1.2. 传统“乘子法”和它的不足1.3. ADMM 的核心思想:分步做1.4. Scaled Form of ADMM1.5. 迭代过程中主要检查的两大残差1.6. 怎么设置停止准则(Stopping Criteria)?1.7. 自适应调整罚参数 ρ \rho ρ(又…...
mysql之sql的优化方案(重点)
1、全字段匹配是最棒的 假如一个Staffs 这个表,将 name,age ,pos 组合成了一个联合索引,在where条件下,能够使用到的索引越多越好。 EXPLAIN SELECT * FROM staffs WHERE NAME July; EXPLAIN SELECT * FROM staffs WHERE NAME July AND age…...
【LeetCode】303. 区域和检索 - 数组不可变
目录 描述Python1. 前缀和 描述 给定一个整数数组nums,处理以下类型的多个查询:计算索引left和right(包含left和right)之间的nums元素的 和 ,其中left < right 实现NumArray类: NumArray(int[] nums)&a…...
前端开发 vue 中如何实现 u-form 多个form表单同时校验
在 Vue 项目中使用 UView UI 的 u-form 组件时,多个表单同时校验的需求非常常见。例如,当我们有多个表单需要在同一个页面中进行校验并提交时,我们需要确保每个表单都能进行单独验证,同时可以在同一时刻进行批量验证。 接下来&am…...
【网络】什么是速率 (Rate)带宽 (Bandwidth)吞吐量 (Throughput)?
注意单位: 在 kbps、Mbps、Gbps 中,前面的 k、M、G 是 国际单位制(SI) 的前缀,表示不同的数量级: k(千/kilo): (10^3 1,000) kbps(kilobits per second): 每秒 1,000 位(…...
(leetcode算法题)769. 最多能完成排序的块
Q1. 是否能用贪心算法?为什么? 先预设一个策略,每当当前的nums[i]满足可以 "成块",就直接让这个数成块,也就是说之后的遍历过程中不会将这个数在考虑到自己的块内, "成块" 是指只要只…...
高光谱相机的特点
光谱特性 高光谱分辨率:能将光谱范围分割成极窄的波段,光谱分辨率通常达到纳米级甚至亚纳米级,可精确捕捉到不同物质在细微光谱差异上的特征,比如可以区分不同种类的植被因叶绿素含量等差异而在光谱上的细微变化。 多波段探测&a…...
《Spring Framework实战》8:4.1.3.Bean 概述
欢迎观看《Spring Framework实战》视频教程 Spring IoC 容器管理一个或多个 bean。这些 bean 是使用 您提供给容器的配置元数据(例如,以 XML <bean/>定义的形式)。 在容器本身中,这些 bean 定义表示为BeanDefinition对象&a…...
BGP的local_preference本地优先级属性
一、BGP的local preference属性简介 1、local preference公认任意属性 当一条BGP路由器中存在多条去往同一目标网络的BGP路由时,BGP协议会对这些BGP路由属性进行比较,从而筛选出最佳到达目标网络的通达路径。本地优先属性,只在IBGP对等体之间…...
IP地址与端口号
ip地址与端口号 IP地址和端口号是网络通信中的两个重要概念,它们共同构成了网络通信的基础。 IP地址:网络世界的门牌号 定义:IP地址(Internet Protocol Address)是分配给网络设备的数字标签,用于在计算机网…...
Fastapi + vue3 自动化测试平台(2)--日志中间件
FastAPI Vue3 自动化测试平台(2)-- 日志中间件 前言 在开发和运行自动化测试平台时,日志功能是至关重要的一部分。日志不仅能帮助我们快速定位和解决问题,还能作为平台运行的记录依据,为后续分析和优化提供参考。 …...
iOS - AutoreleasePool
1. 基本数据结构 // AutoreleasePool 的基本结构 struct AutoreleasePoolPage {static pthread_key_t const key AUTORELEASE_POOL_KEY;magic_t const magic;id *next; // 指向下一个可存放对象的地址pthread_t const thread; // 所属线程AutoreleasePoolPage …...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
