【Threejs进阶教程-着色器篇】5. 2D SDF(二)圆形波纹效果

2D SDF 圆形波纹效果

  • 关于本Shader教程
  • 前四篇地址,请按顺序学习
  • 本博客使用模板代码中的Shader模板
  • 绘制第一圈波纹
  • 绘制多圈波纹
    • fract函数
  • 让光波动起来
  • 使用uniform控制最终效果
    • 追加uniform,以及lil.gui控制器
    • 修改片元着色器
    • 最终效果
  • 完整源码

关于本Shader教程

  1. 本教程着重讲解Shadertoy的shader和Threejs的Shader,与原生WebGLShader略有不同,如果需要学习原生WebGL的shader,请参考《WebGL编程指南》
  2. 本人的shader水平也比较基础,文章中所写代码,不一定是最佳的代码,思路也不一定是最好的思路,所以一切本人的Shader教程下,所有的代码及思路以及学习建议均仅供参考,且目前本教程可能不适用于WebGPU,如果有大佬路过看到本人文章,觉得有可以指点之处,可以在下面留言,我们一起进步
  3. 数学水平不行的人,尤其是高中数学都及格不了的,不建议入坑Shader
  4. 本教程会在讲解片元着色器时,使用Shadertoy来编写demo,所以教程中会出现一部分Shadertoy的代码
  5. 本段内容将会出现在本人所有的【进阶教程-着色器篇】的文章中

前四篇地址,请按顺序学习

【Threejs进阶教程-着色器篇】1. Shader入门(ShadertoyShader和ThreejsShader入门)
【Threejs进阶教程-着色器篇】2. Uniform的基本用法与Uniform的调试
【Threejs进阶教程-着色器篇】3. Uniform的基本用法2与基本地球昼夜效果
【Threejs进阶教程-着色器篇】4. 2D SDF(一) SDF的基本用法

本博客使用模板代码中的Shader模板

请各位自取【模板代码】用于编写Threejs Demo的模板代码

绘制第一圈波纹

上一篇中我们讲到了圆形的sdf,我们依然在片元着色器中引入sdCircle这个函数

<script type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;float sdCircle( vec2 p, float r ){return length(p) - r;}void main(){gl_FragColor = vec4(1.0,0.0,0.0,1.0);}
</script>

一般来说,咱们看到的波纹效果,应该是这样的
在这里插入图片描述
我们先考虑绘制第一圈波纹,可以看出,越靠近边缘的地方,就越亮,越靠近中心的地方就越暗,因为在项目上,波纹基本上都是透明的,所以本次我们以操作透明度的方式来开发这个效果

<script type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;float sdCircle( vec2 p, float r ){return length(p) - r;}void main(){vec2 aUv = vUv - 0.5;vec3 color = vec3(1.0,0.0,0.0);float alp = sdCircle(aUv, 0.5);gl_FragColor = vec4(color,alp);}
</script>

在这里插入图片描述
首先我们先分析上面的代码
先对vUv - 0.5,将坐标系平移到中心
然后创建一个三维向量,作为颜色来使用
然后我们使用sdf函数,计算透明度,计算结果如上图所示

在这里插入图片描述
根据被移动后的坐标系,我们可以推测出,越接近圆心的,length的取值会越小,再-0.1的时候,就会越小,透明度小于0时会显示为0,所以一直到边缘处,u=0.5,v=0.5的时候,最后计算下来的透明度约为 sqrt( 0.5 * 0.5 * 2 ) ≈ 0.707

绘制多圈波纹

那么,我们可以此时,通过把uv * 4.0,来扩大坐标系,达到调整边缘的效果

		//vec2 aUv = vUv - 0.5; //旧代码vec2 aUv = (vUv - 0.5) * 4.0;//新代码

在这里插入图片描述

但是这样我们又有了新问题,此时坐标系变成了 uv的最大值 = 0.5 * 4 = 2.0,那么,在uv都为2.0的地方,透明度是多少呢? 计算一下就知道了 sqrt(2 * 2 * 2) = 2.82,透明度最大值是1,超过1了按照1来显示,那么,我们可以考虑只取小数部分

fract函数

fract只取小数部分,可以作为周期函数来使用
在这里插入图片描述
fract函数的图像大致如图所示,如果要做类似波纹的效果,且不是圆滑的变化规则,可以使用fract函数来做
在这里插入图片描述

我们接下来用fract函数来处理我们的效果

<!-- 片元着色器内容 -->
<script type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;float sdCircle( vec2 p, float r ){return length(p) - r;}void main(){vec2 aUv = (vUv - 0.5) * 4.0;vec3 color = vec3(1.0,0.0,0.0);float alp = fract(sdCircle(aUv, 0.5));gl_FragColor = vec4(color,alp);}
</script>

在这里插入图片描述
我们其实可以再对uv放大一点,比如说放大到10
在这里插入图片描述

让光波动起来

然后我们还需要让它动起来,因为fracrt本身也是周期性函数,那么我们可以通过追加iTime的方式,让fract函数的计算结果发生改变

我们对几何体和材质也稍微做一下修改

let uniforms = {iTime:{value:0}}function addMesh() {//常规情况下,planeGeometry,circleGeometry是与z轴垂直的,改成与y轴垂直,只需要沿着x轴让几何体旋转-90度即可//let geometry = new THREE.PlaneGeometry(10,10).rotateX(-Math.PI/2);//为了让效果更适用于圆形,我们从方形的planeGeometry换成CircleGeometrylet geometry = new THREE.CircleGeometry(5,32).rotateX(-Math.PI/2);let material = new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById('vertexShader').textContent,fragmentShader:document.getElementById('fragmentShader').textContent,transparent:true})let mesh = new THREE.Mesh(geometry,material);scene.add(mesh);}function render() {uniforms.iTime.value += 0.01;renderer.render(scene,camera);orbit.update();requestAnimationFrame(render);}

修改片元着色器响应动态变化

<script type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;uniform float iTime;float sdCircle( vec2 p, float r ){return length(p) - r;}void main(){vec2 aUv = (vUv - 0.5) * 10.0;vec3 color = vec3(1.0,0.0,0.0);//有fract控制,最终数值只会取小数部分float alp = fract(sdCircle(aUv, 0.5) - iTime);gl_FragColor = vec4(color,alp);}
</script>

在这里插入图片描述

使用uniform控制最终效果

首先,我们分析一下现在代码中的常量,一般来说常量都可以单独拎出来做uniform
我们光波的圈数由 aUv后面乘的10.0来控制
我们光波的颜色由vec3 color来控制
我们光波的扩散速度,可以给iTime乘一个常量来控制
我们的光波宽度,可以通过对alp做pow计算来控制
那么,我们接下来添加这四个uniform,并且添加GUI控制

如果看不懂接下来的代码,请移步前三篇,全部的代码在前三篇都有解释

追加uniform,以及lil.gui控制器

    let uniforms = {iTime:{value:0},iFreq:{value:10.0},//频率,光波圈数iColor:{value:new THREE.Color('#ff0000')},//光波颜色iSpeed:{value:1},//扩散速度iPower:{value:2},//光波强度}function addMesh() {//常规情况下,planeGeometry,circleGeometry是与z轴垂直的,改成与y轴垂直,只需要沿着x轴让几何体旋转-90度即可//let geometry = new THREE.PlaneGeometry(10,10).rotateX(-Math.PI/2);//为了让效果更适用于圆形,我们从方形的planeGeometry换成CircleGeometrylet geometry = new THREE.CircleGeometry(5,32).rotateX(-Math.PI/2);let material = new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById('vertexShader').textContent,fragmentShader:document.getElementById('fragmentShader').textContent,transparent:true})let mesh = new THREE.Mesh(geometry,material);scene.add(mesh);let param = {color:"#ff0000" //lil.gui读取threejs的颜色比较麻烦,个人习惯在这里单独写一个来控制};let gui = new GUI();gui.add(uniforms.iFreq,'value',0,50,0.01).name('光波圈数');gui.add(uniforms.iPower,'value',0,50,0.01).name('光波强度');gui.add(uniforms.iSpeed,'value',0,50,0.01).name('扩散速度');gui.addColor(param,'color').name('光波颜色').onChange(v=>{uniforms.iColor.value = new THREE.Color(v);})}function render() {uniforms.iTime.value += 0.01;renderer.render(scene,camera);orbit.update();requestAnimationFrame(render);}

修改片元着色器

<script type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;uniform float iTime;uniform float iFreq;uniform vec3 iColor;uniform float iSpeed;uniform float iPower;float sdCircle( vec2 p, float r ){return length(p) - r;}void main(){vec2 aUv = (vUv - 0.5) * iFreq;float alp = fract(sdCircle(aUv, 0.5) - iTime * iSpeed);alp = pow(alp,iPower);//对扩散的光波pow可以减少光波有颜色的部分的宽度gl_FragColor = vec4(iColor,alp);}
</script>

最终效果

在这里插入图片描述

完整源码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>body{width:100vw;height: 100vh;overflow: hidden;margin: 0;padding: 0;border: 0;}</style>
</head>
<body><script type="importmap">{"imports": {"three": "../three/build/three.module.js","three/addons/": "../three/examples/jsm/"}}</script><script type="x-shader/x-vertex" id="vertexShader">varying vec2 vUv;void main(){vUv = vec2(uv.x,uv.y);vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );gl_Position = projectionMatrix * mvPosition;gl_Position = projectionMatrix * modelMatrix * viewMatrix * vec4( position, 1.0 );}</script>
<script type="x-shader/x-fragment" id="fragmentShader">varying vec2 vUv;uniform float iTime;uniform float iFreq;uniform vec3 iColor;uniform float iSpeed;uniform float iPower;float sdCircle( vec2 p, float r ){return length(p) - r;}void main(){vec2 aUv = (vUv - 0.5) * iFreq;float alp = fract(sdCircle(aUv, 0.5) - iTime * iSpeed);alp = pow(alp,iPower);//对扩散的光波pow可以减少光波有颜色的部分的宽度gl_FragColor = vec4(iColor,alp);}
</script><script type="module">import * as THREE from "../three/build/three.module.js";import {OrbitControls} from "../three/examples/jsm/controls/OrbitControls.js";import {GUI} from "../three/examples/jsm/libs/lil-gui.module.min.js";window.addEventListener('load',e=>{init();addMesh();render();})let scene,renderer,camera;let orbit;function init(){scene = new THREE.Scene();renderer = new THREE.WebGLRenderer({alpha:true,antialias:true});renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement);camera = new THREE.PerspectiveCamera(50,window.innerWidth/window.innerHeight,0.1,2000);camera.add(new THREE.PointLight());camera.position.set(15,15,15);scene.add(camera);orbit = new OrbitControls(camera,renderer.domElement);orbit.enableDamping = true;scene.add(new THREE.GridHelper(10,10));}let uniforms = {iTime:{value:0},iFreq:{value:10.0},//频率,光波圈数iColor:{value:new THREE.Color('#ff0000')},//光波颜色iSpeed:{value:1},//扩散速度iPower:{value:2},//光波强度}function addMesh() {//常规情况下,planeGeometry,circleGeometry是与z轴垂直的,改成与y轴垂直,只需要沿着x轴让几何体旋转-90度即可//let geometry = new THREE.PlaneGeometry(10,10).rotateX(-Math.PI/2);//为了让效果更适用于圆形,我们从方形的planeGeometry换成CircleGeometrylet geometry = new THREE.CircleGeometry(5,32).rotateX(-Math.PI/2);let material = new THREE.ShaderMaterial({uniforms,vertexShader:document.getElementById('vertexShader').textContent,fragmentShader:document.getElementById('fragmentShader').textContent,transparent:true})let mesh = new THREE.Mesh(geometry,material);scene.add(mesh);let param = {color:"#ff0000" //lil.gui读取threejs的颜色比较麻烦,个人习惯在这里单独写一个来控制};let gui = new GUI();gui.add(uniforms.iFreq,'value',0,50,0.01).name('光波圈数');gui.add(uniforms.iPower,'value',0,50,0.01).name('光波强度');gui.add(uniforms.iSpeed,'value',0,50,0.01).name('扩散速度');gui.addColor(param,'color').name('光波颜色').onChange(v=>{uniforms.iColor.value = new THREE.Color(v);})}function render() {uniforms.iTime.value += 0.01;renderer.render(scene,camera);orbit.update();requestAnimationFrame(render);}</script>
</body>
</html>

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode日记(63)颜色分类

感觉就是排序问题&#xff1f;我使用的是时间复杂度比较高的简单粗暴排序法&#xff0c;时间复杂度O&#xff08;n^2&#xff09;。 class Solution { public:void sortColors(vector<int>& nums) {int nnums.size();for(int i0;i<n;i){for(int ji1;j<n;j){if…

鸿蒙应用框架开发【选择并查看文档与媒体文件】 本地数据与文件

选择并查看文档与媒体文件 介绍 应用使用ohos.file.picker、ohos.file.fs等接口&#xff0c;实现了picker拉起文档编辑保存、拉起系统相册图片查看、拉起视频并播放的功能。 效果预览 使用说明&#xff1a; 在首页&#xff0c;应用显示查看最近打开文件功能的跳转按钮&…

APP自动化测试 ------ 滑动和拖拽事件操作!

前言 Appium自动化测试中的常见模拟操作涵盖了多种用户交互行为&#xff0c;这些操作对于自动化测试框架来说至关重要&#xff0c;因为它们能够模拟真实用户的使用场景&#xff0c;从而验证应用程序的功能和稳定性。 今天讲解滑动和拖拽事件操作&#xff1a; 1、swipe滑动事…

java-数据结构与算法-02-数据结构-07-优先队列

1. 概念 队列是一种先进先出的结构&#xff0c;但是有些时候&#xff0c;要操作的数据带有优先级&#xff0c;一般出队时&#xff0c;优先级较高的元素先出队&#xff0c;这种数据结构就叫做优先级队列。 比如&#xff1a;你在打音游的时候&#xff0c;你的朋友给你打了个电话…

springboot短视频推荐系统-计算机毕业设计源码21503

摘 要 本论文基于协同过滤算法&#xff0c;旨在设计并实现一种基于SpringBoot框架的短视频推荐系统。该系统主要分为平台用户和管理员两类角色&#xff0c;用户可以注册、登录、浏览短视频内容&#xff0c;并根据个人兴趣收藏喜爱的视频。管理员则可以管理系统数据、用户和内容…

nginx反向代理和负载均衡+安装jdk-22.0.2

ps -aux|grep nginx //查看进程 nginx 代理 nginx代理是负载均衡的基础 主机&#xff1a;192.168.118.60 这台主机只发布了web服务&#xff0c;没有做代理的任何操作 修改一下index.html中的内容 echo "this is java web server" > /usr/local/nginx/htm…

JVM性能调优全指南:高流量电商系统的最佳实践

1.G1(Garbage-First) 官网: G1 Garbage Collection G1收集器是Java 7中引入的垃圾收集器,用于替代CMS(Concurrent Mark-Sweep)收集器。它主要针对大内存、多核CPU环境下的应用场景,具有以下特点: 分代收集:G1仍然保留了分代的概念,但新生代和老年代不再是物理隔离的,…

线程的同步互斥

互斥 互斥保证了在一个时间内只有一个线程访问一个资源。 先看一段代码&#xff1a;三个线程同时对全局变量val进行--&#xff0c;同时val每自减一次其线程局部存储的全局变量 #include <iostream> #include <thread> #include <vector> #include <uni…

Java之Java基础十六(反射)

一、什么是反射 一般情况下&#xff0c;我们在使用某个类之前已经确定它到底是个什么类了&#xff0c;拿到手就直接可以使用 new 关键字来调用构造方法进行初始化&#xff0c;之后使用这个类的对象来进行操作。 Writer writer new Writer(); writer.setName("aaa"…

WPF的MVVM架构:如何通过数据绑定简化UI逻辑

WPF的MVVM架构&#xff1a;如何通过数据绑定简化UI逻辑 目录 MVVM模式概述数据绑定在MVVM中的作用实现MVVM模式的步骤MVVM模式中的常见问题与解决方案实践示例总结 MVVM模式概述 MVVM&#xff08;Model-View-ViewModel&#xff09;是一种设计模式&#xff0c;用于WPF应用程序…

超声波传感器 - 从零开始认识各种传感器【第十九期】

超声波传感器|从零开始认识各种传感器 1、什么是超声波传感器 超声波传感器是一种利用超声波来进行距离测量和目标检测的传感器。它通过发送&#xff0c;超声波脉冲&#xff0c;并测量超声波从传感器发射到目标物体并返回的时间来计算目标物体与传感器之间的距离。 超声波传感…

echarts无数据的展示内容,用graphic属性配置

echarts无数据的展示内容&#xff0c;用graphic属性配置 当echarts无数据的时候&#xff0c;图表展示的是个空白部分&#xff0c;感觉会有点丑&#xff0c;影响页面美观&#xff0c;这时候翻阅了echarts的官网&#xff0c;让我找到个配置项&#xff0c;试试发现还可以&#xf…

Notion支持直接绑定自己的域名,有何工具可替代为公开网站自定义域名?

Notion最近大招频出&#xff0c;推出新功能——自定义域名。只需简单几步&#xff0c;xxx.notion.site秒变你的专属域名&#xff08;月费仅需10美金&#xff09;。推特上的独立内容创作者/初创公司&#xff0c;用它来打造品牌、分享资料模板&#xff0c;甚至实现盈利。 Notion的…

你还在为PDF转Word烦恼?试试这四款免费工具吧!

悄咪咪问一句&#xff0c;大家在平时上班时最头疼的事情有哪些&#xff1f;我想会有很多朋友也有pdf如何在线转换word文档的免费方式&#xff0c;毕竟这些办公文档是非常常见的问题了&#xff0c;所以今天就专门准备这么一篇文章来分享我个人喜欢的四款好用工具&#xff1a; 第…

做知识付费项目还能做吗?知识付费副业项目如何做?能挣多少钱?

hello,我是阿磊&#xff0c;一个20年的码农&#xff0c;6年前代码写不动了&#xff0c;转型专职做副业项目研究&#xff0c;为劳苦大众深度挖掘互联网副业项目&#xff0c;共同富裕。 现在做知识付费项目还能做吗&#xff1f; 互联网虚拟资源项目我一直在做&#xff0c;做了有…

【单片机毕业设计选题24088】-基于STM32的智能家居控制系统

系统功能: 系统操作说明&#xff1a; 上电后OLED显示 “欢迎使用智能家居系统请稍后”&#xff0c;两秒后显示Connecting...表示 正在连接阿里云&#xff0c;正常连接阿里云后显示第一页面&#xff0c;如长时间显示Connecting...请 检查WiFi网络是否正确。 第一页面第一行…

使用runlink通过容器打印出容器的启动命令

1、Runlike简介 Runlike:通过容器打印出容器的启动命令&#xff0c;然后发现自己需要手动重新运行一些容器的人来说&#xff0c;这是一个真正的节省时间。 2、Docker镜像安装 2.1 构建Runlike容器 [rootlocalhost ~]# docker run --rm -v /var/run/docker.sock:/var/run/do…

嵌入式Linux:符号链接(软链接)和硬链接

目录 1、符号链接&#xff08;软链接&#xff09; 2、硬链接 3、link()函数 4、symlink()函数 5、readlink()函数 在 Linux 系统中&#xff0c;符号链接&#xff08;软链接&#xff09;和硬链接是两种创建文件链接的方法。理解它们的区别和使用场景对于文件系统的管理非常…

Spring核心机制Ioc和Aop

Spring全家桶 WEB&#xff1a;SpringMvc、Spring Web Flux 持久层&#xff1a;Spring Data、Spring Data Redis、Spring Data MongoDB 安全校验&#xff1a;spring Security 构建工程脚手架&#xff1a;SpringBoot 微服务&#xff1a;SpringCloud 所有的Spring框架集成&#xf…

轻松入门Linux—CentOS,直接拿捏 —/— <1>

一、什么是Linux Linux是一个开源的操作系统&#xff0c;目前是市面上占有率极高的服务器操作系统&#xff0c;目前其分支有很多。是一个基于 POSIX 和 UNIX 的多用户、多任务、支持多线程和多 CPU 的操作系统 Linux能运行主要的UNIX工具软件、应用程序和网络协议 Linux支持 32…