效果图
思路
这个护盾可以拆分为以下几个效果
- 旋转UV贴图
- 菲涅尔反射
- 深度计算
- 扫光
- 点击区域光
首先是旋转的UV贴图,无需过多介绍。
float2 uv = i.uv;
uv.x += _Time.y * 0.2;//滚uv,以每秒钟滚0.2圈的速度旋转
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv) * _BaseColor;
其次是菲涅尔反射,公式计算方法在之前的章节也有讲过
接下来是深度空间计算,效果演示中,可以提高深度偏差属性来突出效果
//开始计算屏幕uv
i.TtoW0.xy /= i.TtoW0.w;//透除
#ifdef UNITY_UV_STARTS_AT_TOP//判断当前的平台是openGL还是dx
i.TtoW0.y = 1 - i.TtoW0.y;
#endif
//计算缓冲区深度,模型深度
float4 depthcolor = tex2D(_CameraDepthTexture, i.TtoW0.xy);
float depthbuffer = Linear01Depth(depthcolor, _ZBufferParams);//得到线性的深度缓冲
//计算模型深度
float depth = i.pos.z;
depth = Linear01Depth(depth, _ZBufferParams);//得到模型的线性深度
float edge = saturate(depth - depthbuffer + 0.005) * 100 * _depthoffset;//计算接触光
return edge;
然后是扫光效果
float flow = saturate(pow(1 - abs(frac(i.TtoW1.y * 0.3 - _Time.y * 0.2) - 0.5), 10) * 0.3);
float4 flowcolor = flow * _emissioncolor;
return flowcolor*2;
最后是点击后的圆形区域,点击中心为摄像机发射射线与球形的碰撞盒相交点,由C#监听碰撞检测后将坐标传给材质。
//计算点击区域
float4 dt = smoothstep(_MaskRadius + _MaskSmooth, _MaskRadius - _MaskSmooth, distance(i.TtoW1, _InteractPoint)) * _CollisionColor * _Toggle;
最后将这些效果进行叠加,就得到了我们当前的护盾效果
return float4(tex.xyz, tex.w + edge + fre) + flowcolor + dt;
完整代码
C#脚本
using UnityEngine;
public class ShieldClick : MonoBehaviour
{
//护盾对象
public GameObject _gameObject;
private Material material;
private Ray ray;
private RaycastHit hit;
private void Start()
{
material = _gameObject.GetComponent<MeshRenderer>().material;
}
private void Update()
{
if (Input.GetMouseButton(0))
{
// 主相机屏幕点转换为射线
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//射线碰到了物体
if (Physics.Raycast(ray, out hit,10000))
{
if (hit.collider.gameObject== _gameObject)
{
material.SetVector("_InteractPoint", hit.point);
material.SetFloat("_Toggle", 1);//使用toggle控制是否输入顶点有效
}
Debug.Log(hit.collider.gameObject.name);
Debug.DrawLine(ray.origin, hit.point, Color.red);
}
}
if (Input.GetMouseButtonUp(0))
{
material.SetFloat("_Toggle", 0);
}
}
}
Shader
Shader "URP/Shield"
{
Properties
{
_MainTex ("MainTex", 2D) = "white" { }
_BaseColor ("BaseColor", Color) = (1, 1, 1, 1)
_depthoffset ("深度偏差", float) = 1
[HDR]_emissioncolor ("EmissionColor", Color) = (1, 1, 1, 1)
_MaskRadius ("遮罩半径", Range(0, 3)) = 0
_MaskSmooth ("羽化", Range(0, 1)) = 0
[HDR]_CollisionColor ("碰撞颜色", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags { "RenderPipeline" = "UniversalRenderPipeline" "Queue" = "Transparent" }
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
half4 _BaseColor;
float _depthoffset;
float4 _emissioncolor;
float4 _InteractPoint;
float _Toggle;
float _MaskSmooth;
float4 _CollisionColor;
float _MaskRadius;
CBUFFER_END
// 声明纹理与采样器
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
// 声明深度纹理
SAMPLER(_CameraDepthTexture);
struct a2v
{
float4 vertex: POSITION;
float4 normal: NORMAL;
float2 texcoord: TEXCOORD;
};
struct v2f
{
float4 pos: SV_POSITION;
float2 uv: TEXCOORD0;
float4 TtoW0: TEXCOORD1;
float3 TtoW1: TEXCOORD2;
float3 TtoW2: TEXCOORD3;
};
ENDHLSL
pass
{
Tags { "LightMode" = "UniversalForward" "RenderType" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
v2f vert(a2v i)
{
v2f o;
// 转换到裁剪空间
o.pos = TransformObjectToHClip(i.vertex.xyz);
// 使用纹理的属性_ST对顶点纹理进行变化
o.uv = TRANSFORM_TEX(i.texcoord, _MainTex);
//屏幕坐标TtoW0,xy保存为未透除的屏幕uv,zw不变
o.TtoW0.xy = o.pos.xy * 0.5 + 0.5 * float2(o.pos.w, o.pos.w);
o.TtoW0.zw = o.pos.zw;
o.TtoW1 = TransformObjectToWorld(i.vertex.xyz);
o.TtoW2 = normalize(TransformObjectToWorldNormal(i.normal.xyz));
return o;
}
half4 frag(v2f i): SV_TARGET
{
float2 uv = i.uv;
uv.x += _Time.y * 0.2;//滚uv,以每秒钟滚0.2圈的速度旋转
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv) * _BaseColor;
//return tex;
//菲涅尔
half3 worldViewDir = normalize(_WorldSpaceCameraPos - (i.TtoW1));
float ndotv = dot(i.TtoW2, worldViewDir);
float fre = (0.2 + 2.0 * pow(1.0 - ndotv, 2.0));
//return fre;
//开始计算屏幕uv
i.TtoW0.xy /= i.TtoW0.w;//透除
#ifdef UNITY_UV_STARTS_AT_TOP//判断当前的平台是openGL还是dx
i.TtoW0.y = 1 - i.TtoW0.y;
#endif
//计算缓冲区深度,模型深度
float4 depthcolor = tex2D(_CameraDepthTexture, i.TtoW0.xy);
float depthbuffer = Linear01Depth(depthcolor, _ZBufferParams);//得到线性的深度缓冲
//计算模型深度
float depth = i.pos.z;
depth = Linear01Depth(depth, _ZBufferParams);//得到模型的线性深度
float edge = saturate(depth - depthbuffer + 0.005) * 100 * _depthoffset;//计算接触光
//return edge;
//计算扫光,这是一个做特效的通用公式
float flow = saturate(pow(1 - abs(frac(i.TtoW1.y * 0.3 - _Time.y * 0.2) - 0.5), 10) * 0.3);
float4 flowcolor = flow * _emissioncolor;
//return flowcolor*2;
//计算点击区域
float4 dt = smoothstep(_MaskRadius + _MaskSmooth, _MaskRadius - _MaskSmooth, distance(i.TtoW1, _InteractPoint)) * _CollisionColor * _Toggle;
//return dt;
return float4(tex.xyz, tex.w + edge + fre) + flowcolor + dt;
}
ENDHLSL
}
}
FallBack "Packages/com.unity.render-pipelines.universal/FallbackError"
}
其他
关于这种可以分开的六边形护盾提供下思路。
在建模时每个六边形的顶点是独立的,而非相邻三个六边形共享一个顶点。当他们顶点相互独立时,我们就可以在顶点着色器中对其进行偏移,以此来达到六边形向外扩展和产生缝隙的效果。