Godot实现蝴蝶飞舞Shader

news2024/11/16 2:43:10

前言

我一直在探索在游戏UI中实现特效的方法,如LOL,王者荣耀那种华丽的UI特效。

经过总结有一些方法

1、AE做特效渲染成序列帧供游戏引擎播放

优点:节省资源,适合定制特殊需求
缺点:太大占地方,不好修改

2、游戏引擎本身的材质系统 + 粒子系统

优点:方法通用灵活,比较推荐

缺点:3D粒子渲染到UI容易不清晰,2D粒子还好其实不过没有3D效果。

分为2D粒子和3D粒子,由于Godot本身的粒子系统差强人意,所以难度其实也不低。

粒子Shder暴露出来的参数太少,并且很多功能都需要自己写算法实现。

不能实现过去复杂的需求。

3、纯Shader

优点:清晰,好修改

缺点:太耗GPU,很容易怼到90%多

开端

https://www.shadertoy.com/view/ld23z3

我的shader是修改于这个案例,但实际使用以后发现太耗性能。

即使我优化了很多if判断。

1、新建ColorRect并且在属性栏里面添加ShaderMaterial

 

 下面这些属性是Shader代码里面暴露出来的。

2、写脚本,类似我这样

onready var texture_rect = $TextureRect
onready var color_rect = $ColorRect
onready var color_rect_2 = $ColorRect2
onready var viewport = $Viewport
onready var texture_rect_3 = $TextureRect3


func _ready():
	var tween = Tween.new()
	self.add_child(tween)
	tween.interpolate_property(color_rect, "material:shader_param/iTime", 5, 100, 800, Tween.TRANS_LINEAR, Tween.EASE_IN)
	tween.start()
	texture_rect_3.texture = viewport.get_texture()

func _process(delta):
	var mouse_pos = color_rect.get_global_mouse_position()
	
	color_rect.material.set_shader_param("iMouse",Color(-mouse_pos.x,-mouse_pos.y,0,0))
	color_rect_2.material.set_shader_param("iMouse",Color(-mouse_pos.x,-mouse_pos.y,0,0))

func _on_Control_resized():
	get_node("ColorRect2").set_size(get_node(".").get_rect().size)
	get_node("ColorRect2").material.set_shader_param("iResolution",get_node(".").get_rect().size)

通过脚本可以设置Shader中的参数了。

代码分享

shader_type canvas_item;

uniform float     iTime;                 // 着色器回放时间(单位:秒)
uniform vec3      iResolution;           // 视口分辨率(像素)

uniform int NUM_BUTTERFLIES = 3;//数量

// Noise functions from IQ.
float hash( float n ) { return fract(sin(n)*43758.5453123); }
float noise( in vec2 x )
{
    vec2 p = floor(x);
    vec2 f = fract(x);
    f = f*f*(3.0-2.0*f);
	
    float n = p.x + p.y*157.0;
    return mix(mix( hash(n+  0.0), hash(n+  1.0),f.x),
                   mix( hash(n+157.0), hash(n+158.0),f.x),f.y);
}

float fbm2(vec2 p)
{
   float f = 0.0, x;
   for(int i = 1; i <= 9; ++i)
   {
      x = exp2(float(i));
      f += (noise(p * x) - 0.5) / x;
   }
   return f;
}


float sq(float x)
{
	return x*x;
}

vec2 rotate(float a,vec2 v)
{
	return vec2(cos(a)*v.x+sin(a)*v.y, cos(a)*v.y-sin(a)*v.x);
}

mat3 rotateXMat(float a)
{
	return mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, cos(a), -sin(a)), vec3(0.0, sin(a), cos(a)));
}

mat3 rotateYMat(float a)
{
	return mat3(vec3(cos(a), 0.0, -sin(a)), vec3(0.0, 1.0, 0.0), vec3(sin(a), 0.0, cos(a)));
}

vec3 wing0Node(int i)
{
	return vec3(-0.1,-0.1,1.0);
}

vec3 wing1Node(int i)
{
	return vec3(0.5,-0.2,1.0);
}

vec3 wing0NodeTransformed(int i)
{
	return (wing0Node(i)+vec3(-0.7,-0.05,0.0))*vec3(vec2(1.2,1.0)*0.7,1.0);
}

vec3 wing1NodeTransformed(int i)
{
	return (wing1Node(i)+vec3(-0.7,-0.05,0.0))*vec3(vec2(1.2,1.0)*0.7,1.0);
}

vec3 wing0Tex(vec2 p)
{
	p=rotate(-0.7,p+vec2(0.3,0.0));
	
	float a=1e3;
	float b=1e3;
	
	int cn=0;
	float cnd=1e3;
	for(int i=0;i<8;i+=1)
	{
		float d=distance(p,wing0NodeTransformed(i).xy);
		cnd=d;
		cn=i;
	}
	
	float s=0.04+pow(max(0.0,-p.y*0.4),1.3)+pow(max(0.0,-p.x-1.0),1.3)*0.1;
	
	s+=0.2*(1.0-smoothstep(0.0,0.4,distance(p,vec2(-1.2,0.2)))) +
		0.2*(1.0-smoothstep(0.0,0.3,distance(p,vec2(-1.0,0.5))));
	
	float c=0.0;
	for(int j=0;j<8;j+=1)
	{		
		vec3 n0=wing0NodeTransformed(cn);
		vec3 n1=wing0NodeTransformed(j);
		vec2 nd=n1.xy-n0.xy;
		float d=dot(p-(n0.xy+nd*0.5),normalize(nd))+s*n0.z;
		c+=sq(max(0.0,d));
	}
	
	float p0=sq(max(0.0,dot(p-vec2(-0.5,0.0),normalize(vec2(1.0,-0.9)))));
	
	c+=sq(max(0.0,(distance(p+vec2(0.6,1.45),vec2(0.0))-2.0+s))) + p0 +
		sq(max(0.0,dot(p-vec2(-0.6,-0.2),normalize(vec2(-0.3,-0.9)))));
	
	float c2=sq(max(0.0,(distance(p+vec2(0.6,1.55),vec2(0.0))-2.0))) + p0 +
		sq(max(0.0,dot(p-vec2(-0.6,-0.2),normalize(vec2(-0.3,-0.9)))-0.1));
	
	vec2 xa=vec2(-1.7,-0.0),xb=vec2(-0.8,-0.3);
	vec2 xs=vec2(0.6,1.0);

	vec2 u=mix(xa,xb,floor(clamp(dot(p-xa,xb-xa)/dot(xb-xa,xb-xa),0.0,1.0)*5.0+0.5)/5.0);
	
 	float x=max(1.0-smoothstep(0.06,0.07,distance(p,vec2(-1.2,0.3))),
				1.0-smoothstep(0.02,0.025,length((p-u)*xs)));
	
	return vec3(1.0-smoothstep(s-0.015,s-0.015+0.006,sqrt(c)),1.0-smoothstep(0.1,0.106,sqrt(c2)-0.03),x);
}

vec3 wing1Tex(vec2 p)
{
	p=p+vec2(0.0,0.16);
	
	float a=1e3;
	float b=1e3;
	
	int cn=0;
	float cnd=1e3;
	for(int i=0;i<7;i+=1)
	{
		float d=distance(p,wing1NodeTransformed(i).xy);
		cnd=d;
		cn=i;
	}
	
	float s=0.04+pow(max(0.0,-p.y*0.4),1.3)+pow(max(0.0,-p.x-1.0),1.3)*0.1;
	
	float c=0.0;
	for(int j=0;j<7;j+=1)
	{	
		vec3 n0=wing1NodeTransformed(cn);
		vec3 n1=wing1NodeTransformed(j);
		vec2 nd=n1.xy-n0.xy;
		float d=dot(p-(n0.xy+nd*0.5),normalize(nd))+s*n0.z;
		c+=sq(max(0.0,d));
	}
	
	float p0=sq(max(0.0,dot(p-vec2(-0.5,-0.4),normalize(vec2(1.0,-0.7)))));
	float p1=sq(max(0.0,dot(p-vec2(-0.3,0.3),normalize(-vec2(0.1,-0.9)))));
	
	c+=sq(max(0.0,(distance(p+vec2(0.52,-0.1),vec2(0.0))-0.5))) + p0 + p1;
	
	float c2=sq(max(0.0,(distance(p+vec2(0.5,-0.0),vec2(0.0))-0.53))) + p0 + p1;
	
	float xr=0.7;
	vec2 xa=vec2(-0.4,0.05);
	
	vec2 pd=rotate(-0.2,p-xa);
	float ang=mix(-3.1,-1.8,floor((clamp(atan(pd.y,pd.x),-3.1,-1.8)+3.1)/1.299*6.0+0.5)/6.0);
	
	float x=1.0-smoothstep(0.02,0.025,distance(pd,vec2(cos(ang),sin(ang))*xr));

	return vec3(1.0-smoothstep(s-0.015,s-0.015+0.006,sqrt(c)),1.0-smoothstep(0.1,0.106,sqrt(c2)-0.03),x);
}

vec4 wing(vec2 p)
{
	p+=fbm2(p*4.0)*0.02;

	vec3 wc=mix(vec3(1.0,0.5,0.15),vec3(2.0,0.5,0.15)*0.3,
				fbm2(p*vec2(1.0,16.0))*0.26+pow(clamp((p.y*4.0-abs(p.x)*2.0)/3.0,0.0,1.0),2.0))*0.8;
	
	wc=pow(wc,vec3(1.5));
	
	vec3 c0=wing0Tex(p);
	vec3 c1=wing1Tex(p);
	
	vec3 col=vec3(0.0);
	
	col.rgb=mix(mix(vec3(0.0),c0.x*wc,c0.y),c1.x*wc,c1.y);
	col.rgb=mix(col.rgb,vec3(1.0),c0.z);
	col.rgb=mix(col.rgb,vec3(1.0),c1.z);
	
	return vec4(col,max(c0.y,c1.y));
}

vec3 traceButterflyWing(vec3 ro,vec3 rd,vec3 bo,vec3 bd,float flap)
{
	vec3 up=vec3(0.0,1.0,0.0);
	vec3 c=cross(bd,up);
	float flapangle=mix(radians(20.0),radians(150.0),flap);
	vec3 w=cos(flapangle)*c+sin(flapangle)*up;
	float t=-dot(ro,w)/dot(rd,w);
	vec3 s=cross(w,bd);
	vec3 rp=ro+rd*t;
	return vec3(dot(rp,s),dot(rp,bd),t);
}

vec4 traceButterfly(vec3 ro,vec3 rd,vec3 bo,vec3 bd,float flap)
{
	flap=pow(flap,0.75);
	bo.y-=flap*0.5;
	ro-=bo;
	vec3 up=vec3(0.0,1.0,0.0);
	vec3 c=cross(bd,up);
	
	vec3 w0=traceButterflyWing(ro,rd,bo,bd,flap);
	
	ro-=dot(ro,c)*2.0*c;
	rd-=dot(rd,c)*2.0*c;
	
	vec3 w1=traceButterflyWing(ro,rd,bo,bd,flap);

	//if ( max(abs(w0.x),abs(w0.y)) > 2.0 && max(abs(w1.x),abs(w1.y)) > 2.0 )
	//	return vec4(0,0,0,1e4);
	
	vec4 c0=wing(w0.xy);
	vec4 c1=wing(w1.xy);
	
	bool u0=c0.a>0.0 && w0.z>0.0;
	bool u1=c1.a>0.0 && w1.z>0.0;

	float a1 = step(c0.a,0.0);
	a1 = a1 + step(w0.z,0.0);
	float a2 = step(c1.a,0.0);
	a2 = a2 + step(w1.z,0.0);
	a1 = a1 * a2;

	return mix(vec4(0.0,0.0,0.0,1e4),mix(vec4(c0.rgb,w0.z),vec4(c1.rgb,w1.z),step(w1.z,w0.z)), 1.-a1);
	/*
	if(!u0 && !u1)
		return vec4(0.0,0.0,0.0,1e4);
	else if(u0 && !u1)
		return vec4(c0.rgb,w0.z);
	else if(!u0 && u1)
		return vec4(c1.rgb,w1.z);
	else
		return mix(vec4(c0.rgb,w0.z),vec4(c1.rgb,w1.z),step(w1.z,w0.z));
	*/
}

vec3 butterflyPath(float t)
{
	return vec3(cos(t),cos(t*0.22)*1.0+sin(t*4.0)*0.1,sin(t*1.3))*4.0;
}

vec4 mainImage(in vec4 fragCoord)
{
	vec2 uv = fragCoord.xy / iResolution.xy;
	vec2 q=uv;
	uv=uv*2.0-vec2(1.0);
	uv.x*=iResolution.x/iResolution.y;
	mat3 m=rotateYMat(TIME*0.2)*rotateXMat(cos(TIME*0.12)*0.7);
	//这里乘数可以控制蝴蝶群所在的位置
	vec3 ro=m*vec3(0.0,0.0,25.0),rd=m*normalize(vec3(uv,-2.0));//越接近0越小
	
	vec3 c=vec3(0.0);
	float d=1e3;
	
	for(int i=0;i<NUM_BUTTERFLIES;i+=1)
	{
		float t=TIME+float(i)*10.2;
		vec3 bo=butterflyPath(t);
		vec4 b=traceButterfly(ro,rd,bo,vec3(normalize(butterflyPath(t+1e-2).xz-bo.xz),0.0).xzy,0.5+0.5*cos(t*9.0));
		c=mix(c,b.rgb,step(b.a,d));
		d=min(d,b.a);
	}
	vec4 fragColor = vec4(1.0);
    fragColor.rgb=mix(vec3(0.0),mix(c,vec3(0.0,0.67,1.0),1.0+0.2*dot(c,vec3(1.0/3.0))),step(d,1e2));
	fragColor.rgb=sqrt(fragColor.rgb);
	fragColor *= vec4(0.0,step(d,1e2),0.0,0.5);
	fragColor.rgb = vec3(fragColor.g);
	// IQ's vignet.
	//fragColor.rgb *= pow( 16.0*q.x*q.y*(1.0-q.x)*(1.0-q.y), 0.1 );
    return fragColor;
}


void fragment(){
	vec4 output = mainImage(FRAGCOORD);
    vec2 _output = step(output.xy,vec2(0.0,0.0));
    _output = 1. - _output;
	output = output * vec4(1.0,1.0,_output);
    COLOR = output * vec4(0.0,0.67,1.0,1.0);
}

由于非常复杂,我也一知半解就不误导大家了,初学者可以跟原版代码比对学习。

 

总结

学习到了一些基础的Shader知识,明白了纯Shader只能处理一些简单的操作,想这种比较复杂的3D效果最好采用3D粒子的方式实现。

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

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

相关文章

【H5小游戏】-使用js复刻经典小游戏【接鸡蛋】,快来帮助鸡妈妈找回蛋宝宝吧

接鸡蛋游戏需求&#x1f447;核心玩法&#x1f447;&#x1f447;界面原型&#x1f447;&#x1f447;成品演示&#x1f447;1.游戏演示2.暂停演示游戏开发1.游戏素材准备2.代码实现1.创建index.html页面复刻经典小游戏【接鸡蛋】&#xff0c;快来帮助鸡妈妈找回它的蛋宝宝吧 …

用友BIP与旺店通·企业奇门对接集成采购订单列表查询=>创建采购单(采购订单=>采购单)

用友BIP与旺店通企业奇门对接集成采购订单列表查询>创建采购单(采购订单>采购单-p)来源系统:用友BIP用友BIP助力行业龙头企业、产业链核心企业以及平台型企业建设产业互联网运营平台&#xff0c;通过运营、管理、协同配置和共享资源&#xff0c;实现产业链上下游连接与协…

基于 CartPole-v0 环境的强化学习算法实现(附完整代码)

1.1 CartPole-v0Cart Pole 在 OpenAI 的 gym 模拟器里面是相对比较简单的一个游戏。游戏里面有一个小车&#xff0c;上有 一根杆子。小车需要左右移动来保持杆子竖直。如果杆子倾斜的角度大于 15&#xff0c;那么游戏结束。小车也不 能移动出一个范围&#xff08;中间到两边各 …

03 利用栈进行中缀表达式计算

运算符优先级 ​​​​ 栈内运算符加减乘除取模优先级比栈外优先级大1&#xff0c;例如当23-5时我们往往从左到右计算&#xff0c;即先算再算-&#xff0c;使用中缀表达式两个栈计算就是栈外-优先级低于栈顶&#xff0c;故会弹出运算符和两个操作数进行计算。 中缀表达式计算…

Redis应用技术(3)List和Set在Feed流中的选择

在数据推送的时候,我们使用Feed流 Feed流有三种推送数据的方式(以微博订阅为例) 1.推模式(Push) 将数据在发出后直接推到每个收件箱中.这样会造成发送方的内存占用很大 2.拉模式 用户每次登录后主动的将数据从收件箱中拉去到,会造成用户的负载增加(如果关注的人很多,甚至会导…

SpringCloud——config远程连接github 踩坑

说明 我们现在使用spring cloud config 远程连接github会存在网络跟不上问题&#xff0c;以及本地git密钥问题。 1.github更改了密钥策略原来生成的rsa会导致报错&#xff0c;需要使用命令重新生成ed25519密钥2.配置需要按照上述配置 force-pull: true #这个参数不加可能会有…

Linux系统的启动与关闭

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java案例分…

vue-循环引用感想

vue-循环引用感想开场-什么是循环引用探讨-怎么用后话如果我们要在主组件中传方法到子组件呢普通的porps传参依赖注入开场-什么是循环引用 刚开始看着这个官方文档上的循环引用十分的不解&#xff0c;意思是什么呢&#xff1f;看着官网的意思&#xff0c;大概举了个文件管理器…

Exceeded limit on max bytes to buffer : 262144

springboot版本 2.7.3springcloud版本 2021.0.3POST请求的body是1M多&#xff0c;请求网关提示报错。org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144at org.springframework.core.io.buffer.LimitedDataBuffer…

使用 Burpsuite 测试的常用操作(一)

大家好啊&#xff0c;我是大田。今天分享一下 Burpsuite 在工作中常用操作&#xff0c;本文先说说其中两个操作。一、了解一下 Burpsuite 做什么1、Burpsuite 是一个黑客工具、安全测试工具、半自动化抓包、篡改信息。2、他能做&#xff1a;代理工具 Proxy 、爬虫 Spider、暴力…

python与excel

一、安装anaconda(阿里云镜像库) pip config list -v#pip在哪里寻找pip.conf文件 阿里云镜像&#xff1a; 1、安装完成&#xff0c;命令行输入&#xff1a;conda config生成.condarc文件&#xff08;运行期配置文件&#xff09; 2、如果原本的源中的源地址是 https&#xff0…

手撕Pytorch源码#2.Dataset类 part2

写在前面手撕Pytorch源码系列目的&#xff1a;通过手撕源码复习了解高级python语法熟悉对pytorch框架的掌握在每一类完成源码分析后&#xff0c;会与常规深度学习训练脚本进行对照本系列预计先手撕python层源码&#xff0c;再进一步手撕c源码版本信息python&#xff1a;3.6.13p…

十四.文件操作

目录 一.为什么使用文件 二.什么是文件 1.程序文件和数据文件 2.文件名 三.文件的打开和关闭 1.文件指针 2.fopen函数和fclose函数 四.文件的顺序读写 1.顺序读写函数一览表 2.主要输入输出函数介绍 &#xff08;1&#xff09;字符输出函数futc &#xff08;2&…

Python采集*瓣电影影评并实现可视化分析

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 环境使用: Python 3.8 解释器 Pycharm 编辑器 模块使用 import parsel >>> pip install parsel import requests >>> pip install requests import csv 安装python第三方…

LeetCode 1825 求出MK平均值【Set 队列】 HERODING的LeetCode之路

解题思路&#xff1a; 好久没更新力扣困难题的题解了&#xff0c;今天这道困难题有点意思&#xff0c;读罢题目一目了然&#xff0c;解题思路清晰明了&#xff0c;就是解题过程细节满满。这是一个数据流场景的问题&#xff0c;保留最后m个元素&#xff0c;但是要去除k个最大&am…

设计模式—工厂方法模式

工厂方法模式 文章目录工厂方法模式工厂方法模式是什么理解工厂方法模式代码实例运行截图工厂方法的优点工厂方法的不足工厂方法模式是什么 工厂方法模式属于创建型模式&#xff0c;也叫抽象构造模式&#xff0c; 工厂方法模式将工厂抽象化&#xff0c;并定义一个创建对象的接…

高级语言(C语言)、汇编语言、机器语言区别?编译器如何将高级语言编译成机器语言?

⾼级语⾔&#xff1a; 是相对于汇编语⾔⽽⾔的&#xff0c;是⾼度封装了的编程语⾔&#xff0c;与低级语⾔相对。它是以⼈类的⽇常语⾔为基础的⼀种编程语⾔&#xff0c;使⽤⼀般⼈易于接受的⽂字来表⽰&#xff08;例如汉字、不规则英⽂或其他外语&#xff09;&#xff0c;从…

(二十四)List系列集合

目录 前言: 一、List集合的特有方法 二、List集合的遍历方式有几种&#xff1f; 三、Arraylist集合底层原理 四、LinkedList的特点 前言: List集合包括JavaList接口以及List接口的所有实现类。List集合中的元素允许重复&#xff0c;各元素的顺序放是对象插入的顺序&#xff…

C生万物 | C语言文件操作指南汇总【内附文件外排序源码】

&#x1f451;作者主页&#xff1a;Fire_Cloud_1 &#x1f3e0;学习社区&#xff1a;烈火神盾 &#x1f517;专栏链接&#xff1a;万物之源——C 文章目录一、为什么使用文件&#xff1f;二、什么是文件&#xff1f;1、程序文件2、数据文件3、文件名三、文件的打开和关闭1、文件…

自动化测试【软件测试】

自动化测试 什么是自动化 有效减少人力的消耗&#xff0c;同时提高生活的质量 通过自动化测试有效减少人力的投入&#xff0c;同时提高了测试的质量和效率 由于回归测试&#xff0c;版本越来越多&#xff0c;版本回归的压力越来越大&#xff0c;仅仅通过人工测试来回归所有版本…