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 updatevoid Start(){material = this.GetComponent<Renderer>().sharedMaterial;}// Update is called once per framevoid Update(){if (material != null && cutObj != null) {material.SetVector("_CuttingPos", cutObj.transform.position);}}
}