电影动画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 …...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...
Java并发编程实战 Day 11:并发设计模式
【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天,今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案,它们不仅提供了优雅的设计思路,还能显著提升系统的性能…...
标注工具核心架构分析——主窗口的图像显示
🏗️ 标注工具核心架构分析 📋 系统概述 主要有两个核心类,采用经典的 Scene-View 架构模式: 🎯 核心类结构 1. AnnotationScene (QGraphicsScene子类) 主要负责标注场景的管理和交互 🔧 关键函数&…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...
运动控制--BLDC电机
一、电机的分类 按照供电电源 1.直流电机 1.1 有刷直流电机(BDC) 通过电刷与换向器实现电流方向切换,典型应用于电动工具、玩具等 1.2 无刷直流电机(BLDC) 电子换向替代机械电刷,具有高可靠性,常用于无人机、高端家电…...
