1、物体切割效果是什么
在游戏开发中,物体切割效果就是物体看似被切割、分割或隐藏一部分的视觉效果。
这种效果常用与游戏和动画中,比如角色攻击时的切割效果,场景中的墙壁切割效果等等。
2、物体切割效果的基本原理
在片元着色器中判断片元的世界坐标是否满足切割条件,如果满足则直接抛弃片元不渲染
判断片元在模型中的正反面,决定使用哪种纹理进行渲染
关键点:
- 如何判断世界坐标
在Shader中声明一个Vector坐标变量,通过C#代码把控制切割位置的物体世界空间位置(比如在一个物体下放一个子对象 cutObj 来控制这个位置)传递给Shader,在Shader中用片元的世界坐标和传递进来的世界坐标做比较,在这个课程中,我们可以分三种模式,分别比较x、y、z坐标,切割x、y、z方向的片元
- 如何抛弃片元不渲染
Unity Shader中提供了一个内置函数 clip(x)它的作用就是在片元着色器中调用时来丢弃片元的
传入的值x小于0,则会丢弃当前片元,被丢弃的片元不会被进一步处理也就不会被渲染了,也就是说,当我们判断片元位置需要被切割时,直接执行clip函数传入一个负数,这个片元就会被丢弃
不会被渲染了
- 如何判断片元的正反面
Unity Shader中提供了一个语义 VFACE,它只能在片元着色器中使用,它可以作为参数传递给片元着色器
传入值1表示为正面,-1表示为背面,我们可以利用它判断该片元是模型正面还是背面片元,决定使用哪种纹理或颜色进行渲染
注意:
在使用它时建议加上编译指令 #pragma target 3.0 或 4.0 、5.0表示设置着色器模型版本,可以让VFACE语义使用上的兼容性和稳定性更好
3、实现
Shader "ShaderProj/17/ObjectCutting"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BackTex ("BackTex", 2D) = "white" {}
// 切割的防线 0-x, 1-y, 2-z
_CuttingDir ("CuttingDir", Float) = 0
//是否翻转切割 0-不反转 1-反转
_Invert ("Invert", Float) = 0
_CuttingPos ("CuttingPos", Vector) = (0,0,0,0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
Cull Off // 正反两面都要渲染
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BackTex;
fixed _CuttingDir;
fixed _Invert;
float4 _CuttingPos;
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag (v2f i, fixed face : VFACE) : SV_Target
{
fixed4 col = face > 0 ? tex2D(_MainTex, i.uv) : tex2D(_BackTex, i.uv);
fixed cutValue;
if (_CuttingDir == 0)
cutValue = step(_CuttingPos.x, i.worldPos.x);
else if (_CuttingDir == 1)
cutValue = step(_CuttingPos.y, i.worldPos.y);
else if (_CuttingDir == 2)
cutValue = step(_CuttingPos.z, i.worldPos.z);
cutValue = _Invert ? 1 - cutValue : cutValue;
cutValue = cutValue == 0 ? -1 : cutValue;
clip(cutValue);
return col;
}
ENDCG
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteAlways]
public class ObjectCutting : MonoBehaviour
{
private Material material;
public GameObject cutObj;
// Start is called before the first frame update
void Start()
{
material = this.GetComponent<Renderer>().sharedMaterial;
}
// Update is called once per frame
void Update()
{
if (material != null && cutObj != null) {
material.SetVector("_CuttingPos", cutObj.transform.position);
}
}
}