Three.js 中实现自定义光圈 Shader 效果

news/2025/2/1 10:59:11 标签: 图形渲染, 线性代数

目录

前言

Three.js 与自定义着色器的基础知识

效果展示代码概览

顶点着色器的作用

Uniforms 的作用

 动画实现


 

前言

Three.js 是一个功能强大的 WebGL 库,它让开发者能够轻松地创建复杂的 3D 场景、动画和交互效果。然而,有时候内置的材质和效果无法满足项目的特定需求。在这种情况下,我们可以通过使用自定义着色器来实现独特的视觉效果。 

Three.js 与自定义着色器的基础知识

在 Three.js 中,自定义着色器是通过 ShaderMaterial 实现的。ShaderMaterial 允许你完全控制顶点着色器和片段着色器的行为。以下是自定义着色器的一些关键组件:

  1. 顶点着色器(Vertex Shader):负责处理每个顶点的位置。
  2. 片段着色器(Fragment Shader):负责为每个像素计算颜色。
  3. Uniforms:在 JavaScript 和着色器之间传递的全局变量,用于动态更新效果。

效果展示代码概览

我们先来看一个完整的代码示例,后续会逐步拆解每个部分。

import * as THREE from 'three';

// 顶点着色器
const vertexShader = `
varying vec2 vUv;
void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;

// 片段着色器
const fragmentShader = `
uniform vec2 iResolution;
uniform float iTime;

varying vec2 vUv;

// SDF 圆形函数
float sdCircle(vec2 p, float r) {
    return length(p) - r;
}

// 效果一:波纹效果
vec4 effect_1(vec2 uv) {
    float c = length(uv);
    c = abs(sin(c * 6.0 - iTime) / 6.0);
    c = smoothstep(0.0, 0.125, c);

    return vec4(vec3(1.0 - c), 1.0);
}

// 效果二:辐射效果
vec4 effect_2(vec2 uv) {
    vec3 color = vec3(1.0, 2.0, 4.0);

    float c = length(uv);
    c = abs(sin(c * 2.0 - iTime) / 4.0);
    c = 0.0125 / c;

    color *= c;

    return vec4(color, 1.0);
}

void main() {
    vec2 uv = vUv;
    float ratio = iResolution.x / iResolution.y;

    vec2 center = vec2(0.5, 0.5); // 中心位置

    uv -= center; // 居中 UV 坐标
    uv *= 2.0;    // 归一化
    uv.x *= ratio;

    // 选择效果(effect_1 或 effect_2)
    gl_FragColor = effect_2(uv);
}
`;

// Uniforms 设置
const uniforms = {
    iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
    iTime: { value: 0 },
};

// Shader 材质
const material = new THREE.ShaderMaterial({
    vertexShader,
    fragmentShader,
    uniforms,
});

// 创建一个平面来应用 Shader
const geometry = new THREE.PlaneGeometry(2, 2);
const mesh = new THREE.Mesh(geometry, material);

// 场景与相机设置
const scene = new THREE.Scene();
scene.add(mesh);
const camera = new THREE.Camera();

// 渲染器设置
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 动画循环
function animate() {
    uniforms.iTime.value += 0.05; // 更新时间
    renderer.render(scene, camera);
    requestAnimationFrame(animate);
}

animate();

顶点着色器的作用

顶点着色器定义了如何将三维点映射到二维屏幕上。在我们的代码中,顶点着色器非常简单,只是将 UV 坐标传递到片段着色器中:

varying vec2 vUv;
void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

vUv 是 UV 坐标,它描述了纹理如何映射到几何体表面。gl_Position 则是顶点的最终位置。

片段着色器解析 片段着色器是实现视觉效果的核心。在这个示例中,我们定义了两个效果函数:

效果一:波纹效果 波纹效果利用了 SDF(有符号距离函数)的特性,通过 sin 函数和 smoothstep 产生渐变波纹。

vec4 effect_1(vec2 uv) {
    float c = length(uv);
    c = abs(sin(c * 6.0 - iTime) / 6.0);
    c = smoothstep(0.0, 0.125, c);

    return vec4(vec3(1.0 - c), 1.0);
}

 效果二:辐射效果 辐射效果通过缩放颜色强度实现,创造出一种动态发光的视觉效果。

vec4 effect_2(vec2 uv) {
    vec3 color = vec3(1.0, 2.0, 4.0);

    float c = length(uv);
    c = abs(sin(c * 2.0 - iTime) / 4.0);
    c = 0.0125 / c;

    color *= c;

    return vec4(color, 1.0);
}

Uniforms 的作用

uniform 是一种全局变量,可以从 JavaScript 中传递给 GLSL。这里的 iResolution 和 iTime 是两个关键变量:

iResolution:屏幕分辨率,用于调整 UV 坐标比例。 iTime:时间变量,用于为动画效果提供动态输入。 将着色器应用到平面 为了将着色器可视化,我们将其应用到一个 2D 平面上。PlaneGeometry 用于创建一个平面,而 ShaderMaterial 则绑定了我们的着色器。

 动画实现

通过在每一帧更新 iTime,我们可以为效果注入时间维度,从而实现动态动画: 

function animate() {
    uniforms.iTime.value += 0.05; // 更新时间
    renderer.render(scene, camera);
    requestAnimationFrame(animate);
}

 


http://www.niftyadmin.cn/n/5839319.html

相关文章

SG算法解析

Savitzky-Golay 滤波器的核心代码主要集中在计算投影矩阵B并使用这个矩阵对输入信号进行滤波。这部分核心代码包括计算B矩阵、处理边界效应和进行实际滤波操作。以下是对核心代码的一点解释: ① 计算 Savitzky-Golay 投影矩阵B B sgolay(order, framelen, weight…

动态规划每日一练(四)

一、day1——最长数对链 题目链接&#xff1a; 646. 最长数对链 - 力扣&#xff08;LeetCode&#xff09;646. 最长数对链 - 给你一个由 n 个数对组成的数对数组 pairs &#xff0c;其中 pairs[i] [lefti, righti] 且 lefti < righti 。现在&#xff0c;我们定义一种 跟随…

【Rust】18.2. 可辩驳性:模式是否会无法匹配

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 18.2.1. 模式的两种形式 模式有两种形式&#xff1a; 可辩驳的&#xff08;可失败的&…

MySQL知识点总结(十六)

请说明在复制拓扑中&#xff0c;中继日志集和从属服务器状态日志的作用。 中继日志用来保存从主服务器接受的二进制日志&#xff0c;与二进制日志相同的格式存储&#xff0c;由服务器自动管理&#xff0c;在其全部内容重放后会自动删除。 从属服务器状态日志存储关于如何连接…

前端八股CSS:盒模型、CSS权重、+与~选择器、z-index、水平垂直居中、左侧固定,右侧自适应、三栏均分布局

一、盒模型 题目&#xff1a;简述CSS的盒模型 答&#xff1a;盒模型有两种类型&#xff0c;可以通过box-sizing设置 1.标准盒模型&#xff08;content-box&#xff09;:默认值&#xff0c;宽度和高度只包含内容区域&#xff0c;不包含内边距、边框和外边距。 2.边框盒模型&a…

Python3 【装饰器】项目实战:5个新颖的学习案例

Python3 【装饰器】项目实战&#xff1a;5个新颖的学习案例 以下是 5 个使用 Python 装饰器的综合应用项目&#xff0c;这些项目具有新颖性、前瞻性和实用性。每个项目都包含完整的代码、解释说明、测试案例和执行结果。 项目 1&#xff1a;API 请求限流器 描述&#xff1a;实…

第十一章 F - H 开头的术语

文章目录 第十一章 F - H 开头的术语文件流 (file stream)最终类 (final class)最终方法 (final method)最终属性 (final property)外键 (foreign key)基础 (foundation) 以 G 开头的术语全局 (global)全局数据库 (globals database)全局目录 (global directory)全局唯一标识符…

CSS 中调整元素大小的全面指南

CSS 中调整元素大小的全面指南 1. 原始尺寸&#xff08;固有尺寸&#xff09;示例代码&#xff1a;图像的固有尺寸 2. 设置具体的尺寸示例代码&#xff1a;设置固定宽度和高度 3. 使用百分比示例代码&#xff1a;使用百分比设置宽度 4. 使用百分比作为外边距和内边距示例代码&a…