【ShaderLab实例笔记】Overwatch Shield - 守望先锋护盾特效制作笔记

本文详细记录了在Unity中使用Shader重现《守望先锋》中Reinhardt护盾效果的过程,包括半透明护盾、蜂窝纹理搏动、电流脉冲扩散、边缘效果以及自发光等关键步骤。通过调整时间、纹理采样和数学计算,实现了从护盾中心向外扩散的菱形电流效果,以及与周围环境交互的高亮边缘。同时,文章还探讨了如何利用线性空间提高颜色精度,并对Shader进行了优化,如将多张纹理合并为一张,减少资源占用。
摘要由CSDN通过智能技术生成

教程链接:Overwatch Shield
项目链接:OverwatchShieldTemplate
Pipeline & Shader:Built-in,Unlit

本文是对 Overwatch Shield 学习过程的记录和总结,不是完全的翻译,更多的细节和图文建议跳转原博

效果分析

使用守望先锋中Reinhardt的护盾作为参考

整体观察
在这里插入图片描述
可以看出护盾是半透明的,带有一层基色,可以微微照亮周边区域
绕着护盾走可以发现,护盾的背面也可以显示(关闭Cull),并且有明显的厚度

蜂窝纹理
在这里插入图片描述
蜂窝会从中心沿x轴方向搏动,仔细观察还可以发现蜂窝并不是同时搏动的

蜂窝状电流
在这里插入图片描述
电流沿着蜂窝状边缘向外扩散,不能难发现扩散的形状一个菱形
所以这个效果其实是一个从护盾中心,从点变大的菱形框点亮蜂窝边缘的过程

固定边缘
在这里插入图片描述
仔细看就会发现边缘效果由两部分组成:固定边缘、相交边缘

相交边缘
在这里插入图片描述
相交边缘的效果基本和固定边缘一样

效果总结

  1. 基础效果:整体透明,带有基色,Cull Off
  2. 蜂窝纹理:蜂窝纹理从护盾中心沿x轴搏动,带有一定先后顺序
  3. 电流脉冲:蜂窝电流从护盾中心按菱形扩散
  4. 固定边缘:越边缘颜色越实
  5. 相交边缘:与其他物体交叉处有高亮
  6. 自发光:护盾可以微微照亮周边

项目设置

场景方面,模拟了视频(图片)中的物体:
护盾与地面相交,有物体穿过护盾,方便测试效果
在这里插入图片描述

另外项目的色彩空间使用了线性空间,因为线性空间的精度更高
在这里插入图片描述
更多关于颜色空间的内容,可以查看GAMMA AND LINEAR SPACE - WHAT THEY ARE AND HOW THEY DIFFERUnity User Manual Color space

Shader编写

基础效果

目标效果:整体透明,带有基色,Cull Off
没啥东西,改 Tag,关 Cull,设置 Blend,完事

Pass
{
   
	Tags {
   "RenderType" = "Transparent" "Queue" = "Transparent"}
	Cull off
	Blend SrcAlpha one
	
	HLSLPROGRAM
	// ...
		
	fixed4 frag (v2f i) : SV_Target
	{
   
		return _Color;
	}
	ENDHLSL
}

基础效果图
在这里插入图片描述
 

蜂窝搏动

目标效果:蜂窝纹理从护盾中心沿x轴搏动,带有一定先后顺序

效果拆分:

  1. 蜂窝纹理 + 整体呼吸效果
  2. 打乱呼吸顺序
  3. 呼吸效果沿x轴扩散

1.蜂窝纹理 + 整体呼吸效果

fixed4 frag (v2f i) : SV_Target
{
   
	// 蜂窝图案
	fixed4 pulseTex = tex2D(_PulseTex, i.uv);
	fixed4 pulseTerm = pulseTex * _Color * _PulseIntensity;
	
	// 呼吸效果
	pulseTerm *= abs(sin(_Time.y * _PulseTimeScale));
	
	return fixed4(_Color.rgb + pulseTerm.rgb, _Color.a);
}

在这里插入图片描述
 

2.打乱呼吸顺序

打乱顺序的方法有很多,因为这里使用的蜂窝纹理刚好有深浅变化,可以直接用来作为决定呼吸顺序的因子

这里的代码会让 r 通道越大的地方亮的越早

fixed4 frag (v2f i) : SV_Target
{
   
	// 蜂窝图案
	fixed4 pulseTex = tex2D(_PulseTex, i.uv);
	fixed4 pulseTerm = pulseTex * _Color * _PulseIntensity;

	float breath = _Time.y * _PulseTimeScale;
	float pulseOffset = pulseTex.r * _PulseTexOffsetScale;

	// 随机呼吸效果
	pulseTerm *= abs(sin(breath + pulseOffset));

	return fixed4(_Color.rgb + pulseTerm.rgb, _Color.a);
}

为了防止 sin() 的负值导致奇怪的结果出现,需要滤去负值
同时也不希望负值直接被处理为常数导致图案无变化,使用了 abs()
 

3.呼吸效果沿x轴扩散

由于制作模型时,将模型的中心点设置在了正中心(默认是底部中心),可以直接取模型空间的x坐标来确定顶点与中心轴的距离

fixed4 frag (v2f i) : SV_Target
{
   
	// 蜂窝图案
	fixed4 pulseTex = tex2D(_PulseTex, i.uv);
	fixed4 pulseTerm = pulseTex * _Color * _PulseIntensity;

	float breath = _Time.y * _PulseTimeScale;
	float pulseOffset = pulseTex.r * _PulseTexOffsetScale;
	float horizontalDist = abs(i.posOS.x);
	float xOffset = horizontalDist  * _PulsePosScale;

	// 沿x轴扩散的随机呼吸效果
	pulseTerm *= abs(sin(breath + pulseOffset - xOffset));

	return fixed4(_Color.rgb + pulseTerm.rgb, _Color.a);
}

 

电流脉冲

目标效果:蜂窝电流从护盾中心按菱形扩散
大体上思路和蜂窝搏动效果有很多相似的地方

效果拆分:

  1. 蜂窝电流 + 整体呼吸效果
  2. 蜂窝电流以菱形从护盾中心扩散

1.蜂窝电流 + 整体呼吸效果

fixed4 frag (v2f i) : SV_Target
{
   
	// 蜂窝搏动
	// ...
	
	// 蜂窝电流
	fixed4 hexEdgeTex = tex2D(_HexEdgeTex, i.uv);
	fixed4 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;

	// 呼吸
	float edgeBreath = _Time.y * _HexEdgeTimeScale;
	
	hexEdgeTerm *= saturate(sin(edgeBreath));
	
	// 只看电流
Unity的内置着色器中经常使用UNITY_VPOS_TYPE枚举类型,它表示当前顶点的坐标空间类型。以下是一个使用UNITY_VPOS_TYPE的实例: ```hlsl v2f vert(appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_FORWARDADD) UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.worldPos = mul(unity_ObjectToWorld, v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.screenPos = ComputeScreenPos(o.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); #if defined(_ALPHATEST_ON) o.alpha = v.color.a; #endif #elif defined(UNITY_PASS_SHADOWCASTER) o.worldPos = mul(unity_ObjectToWorld, v.vertex); #endif return o; } ``` 在这个顶点着色器函数中,我们可以看到在#if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_FORWARDADD)条件下,我们通过UNITY_SETUP_INSTANCE_ID(v)和UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o)来设置实例ID和立体视图,然后根据UNITY_VPOS_TYPE的不同,我们可以对o.worldPos和o.vertex进行不同的计算。在这个例子中,我们通过UnityObjectToClipPos(v.vertex)来将顶点坐标从对象空间转换到裁剪空间,并将结果保存在o.vertex中。 需要注意的是,UNITY_VPOS_TYPE枚举类型的取值是根据渲染管线的不同而不同的。在顶点着色器中,我们需要根据当前的渲染管线类型来确定UNITY_VPOS_TYPE的取值,以正确地计算顶点的位置和法线等信息。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值