电影动画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;

    // 创建自定义 ShaderMaterial
    const 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);
            }
    
            // 球体 SDF
            float 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;

关注我们

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/950433.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用ML.NET进行对象检测

1、前言 ML.NET 是面向 .NET 开发人员的开源跨平台机器学习框架&#xff0c;支持将自定义机器学习模型集成到 .NET 应用程序中。 它包含一个 API&#xff0c;其中包含不同的 NuGet 包、名为 模型生成器的 Visual Studio 扩展&#xff0c;以及作为 .NET 工具安装的 命令行接口。…

年会抽奖Html

在这里插入图片描述 <!-- <video id"backgroundMusic" src"file:///D:/background.mp3" loop autoplay></video> --> <divstyle"width: 290px; height: 580px; margin-left: 20px; margin-top: 20px; background: url(D:/nianhu…

vue -关于浏览器localstorge数据定期清除的实现

1.实现背景 用户登录时的信息存在了localstorge中&#xff0c;但它会一直存在。一般来说&#xff0c;我们希望这个数据能够定期被清除掉&#xff0c;以下一个定时清除的实现。 2.实现原理 在用户登录时&#xff0c;将用户信息存入localstorge的同时&#xff0c;将当前时间作…

LabVIEW水轮发电机组振动摆度故障诊断

本文介绍了基于LabVIEW的水轮发电机组振动摆度故障诊断系统的设计与实施过程。系统在通过高效的故障诊断功能&#xff0c;实现水轮发电机组的振动、温度等关键指标的实时监控与智能分析&#xff0c;从而提高电力设备的可靠性和安全性。 ​ 项目背景 随着电力行业对设备稳定性…

Collaborate with AI -- Write a modern C++ singleton factory

translate my blog <<与AI合作 -- 写一个modern c单例工厂>> to English. NOTE: It was written at 2024.01, maybe the AI is not smart as now. Preface In this article, readers can learn about a hybrid of the modern C singleton pattern and factory pat…

【轻松学C:编程小白的大冒险】--- C语言简介 02

在编程的艺术世界里&#xff0c;代码和灵感需要寻找到最佳的交融点&#xff0c;才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里&#xff0c;我们将共同追寻这种完美结合&#xff0c;为未来的世界留下属于我们的独特印记。 【轻松学C&#xff1a;编程小白的大冒险】…

下载b站高清视频

需要使用的edge上的一个扩展插件&#xff0c;所以选择使用edge浏览器。 1、在edge浏览器上下载 强力视频下载合并 扩展插件 2、在edge上打开b站&#xff0c;登录自己账号&#xff08;登录后才能下载到高清&#xff01;&#xff01;&#xff09;。打开一个视频&#xff0c;选择自…

oxml中创建CT_Document类

概述 本文基于python-docx源码&#xff0c;详细记录CT_Document类创建的过程&#xff0c;以此来加深对Python中元类、以及CT_Document元素类的认识。 元类简介 元类&#xff08;MetaClass&#xff09;是Python中的高级特性。元类是什么呢&#xff1f;Python是面向对象编程…

Tableau数据可视化与仪表盘搭建-基础图表制作

目录 对比分析&#xff1a;比大小 柱状图 条形图 数据钻取 筛选器 热力图 气泡图 变化分析&#xff1a;看趋势 折线图 预测 面积图 关系分布&#xff1a;看位置 散点图 直方图 地图 构成分析&#xff1a;看占比 饼图 树地图 堆积图 对比分析&#xff1a;比大…

QML学习(八) Quick中的基础组件:Item,Rectangle,MouseArea说明及使用场景和使用方法

上一篇中我们从设计器里可以看到Qt Quick-Base中有几大基础组件&#xff0c;如下图&#xff0c;这篇文章先介绍下Item&#xff0c;Rectangle&#xff0c;MouseArea这三个的说明及使用场景和使用方法 Item Item 是 QML 中所有可视元素的基类&#xff0c;是一个非常基础和通用的…

万界星空科技质量管理QMS系统具体功能介绍

一、什么是QMS系统&#xff0c;有什么价值&#xff1f; 1、QMS 系统即质量管理系统&#xff08;Quality Management System&#xff09;。 它是一套用于管理和控制企业产品或服务质量的集成化体系。 2、QMS 系统的价值主要体现在以下几个方面&#xff1a; 确保产品质量一致性…

字符串哈希stl解决

题目如下 STL的unordered-set STL的map 谢谢观看&#xff01;&#xff01;&#xff01;

JAVA I/O流练习1

往D盘中的JAVA复习文件夹中写数据&#xff1a; 数据改了一下哈&#xff1a; import java.io.*; import java.util.Scanner; public class Test {public static void main(String[] args) throws IOException {String fileName"D:JAVA复习\\grade.txt";FileWriter w…

英伟达Project Digits赋能医疗大模型:创新应用与未来展望

英伟达Project Digits赋能医疗大模型&#xff1a;创新应用与未来展望 一、引言 1.1 研究背景与意义 在当今数字化时代&#xff0c;医疗行业作为关乎国计民生的关键领域&#xff0c;正面临着前所未有的挑战与机遇。一方面&#xff0c;传统医疗模式在应对海量医疗数据的处理、复…

OpenAI 故障复盘 - 阿里云容器服务与可观测产品如何保障大规模 K8s 集群稳定性

本文作者&#xff1a; 容器服务团队&#xff1a;刘佳旭、冯诗淳 可观测团队&#xff1a;竺夏栋、麻嘉豪、隋吉智 一、前言 Kubernetes(K8s)架构已经是当今 IT 架构的主流与事实标准&#xff08;CNCF Survey[1]&#xff09;。随着承接的业务规模越来越大&#xff0c;用户也在使…

移动电商的崛起与革新:以开源AI智能名片2+1链动模式S2B2C商城小程序为例的深度剖析

摘要&#xff1a;本文旨在探讨移动电商的崛起背景、特点及其对传统电商模式的革新影响&#xff0c;并以开源AI智能名片21链动模式S2B2C商城小程序为具体案例&#xff0c;深入分析其在移动电商领域的创新实践。随着移动互联网技术的飞速发展&#xff0c;移动电商已成为电商行业的…

el-table 合并单元格

参考文章&#xff1a;vue3.0 el-table 动态合并单元格 - flyComeOn - 博客园 <el-table :data"tableData" border empty-text"暂无数据" :header-cell-style"{ background: #f5f7fa }" class"parent-table" :span-method"obj…

C/C++进阶-函数

C/C入门-函数起始 函数引用与指针函数参数 指针写法 和 数组写法数组的引用右值引用概念&#xff1a;**反汇编&#xff1a;**总结用结构体的示例再理解一遍 函数的本质栈分析栈溢出攻击 函数重载函数重载 进阶 思考函数重载补充 函数模板&#xff08;1&#xff09;&#xff08;…

通俗易懂之线性回归时序预测PyTorch实践

线性回归&#xff08;Linear Regression&#xff09;是机器学习中最基本且广泛应用的算法之一。它不仅作为入门学习的经典案例&#xff0c;也是许多复杂模型的基础。本文将全面介绍线性回归的原理、应用&#xff0c;并通过一段PyTorch代码进行实践演示&#xff0c;帮助读者深入…

分布式主键ID生成方式-snowflake雪花算法

这里写自定义目录标题 一、业务场景二、技术选型1、UUID方案2、Leaf方案-美团&#xff08;基于数据库自增id&#xff09;3、Snowflake雪花算法方案 总结 一、业务场景 大量的业务数据需要保存到数据库中&#xff0c;原来的单库单表的方式扛不住大数据量、高并发&#xff0c;需…