当前位置: 首页 > news >正文

Godot实现蝴蝶飞舞Shader

前言

我一直在探索在游戏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粒子的方式实现。

相关文章:

  • 网站制作的主要流程/seo推广一个月见效
  • wordpress 图片下加文字/国内优秀个人网站欣赏
  • 西安市住房和城乡建设局网站/网站推广的基本方法有
  • 网站 流量攻击/线下推广都有什么方式
  • id怎么转wordpress/怎样做网络销售平台
  • 找手工活带回家做的找工作哪个网站最靠谱/今日新闻头条
  • 汽车网络技术概述
  • 物理主机telenet登录ensp虚拟网络设备并显示配置python脚本
  • kubernetes Ingress
  • 基于深度学习方法的车道线检测综述(2022)
  • java List的stream()方法解析
  • Linux 命令(246)—— mii-tool 命令
  • 安卓包在真机上安装时的“风险提示”问题
  • Elasticsearch高级查询—— 关键字精确查询文档
  • APP攻防—— jadx反编译frida编写js
  • 装修--避坑--瓷砖
  • 手撕RTSP协议中 从零开始学习RTSP协议 持续更新中....
  • 协程应用——aiohttp异步爬虫实战