先看一下效果图
实现思路:在烧杯倾倒液体时,烧杯内的液体始终不会超过烧杯口的高度,所以我们计算得到这个高度值,然后截取掉超过该高度的像素
首先准备烧杯内液体的模型,挂载一下shader对应的材质球
shader部分:
Shader "Unlit/CullShader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
Value("ClipValue",Range(-0.1,0.15)) = 0
_Color("Color", Color) = (1,1,1,1)
}
CGINCLUDE
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 pos:TEXCOORD1;
float3 WorldNor:TEXCOORD2;
float4 WorldPos:TEXCOORD3;
};
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float Value;
uniform float objPosY;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.WorldPos = mul(UNITY_MATRIX_M, v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.WorldNor = normalize(UnityObjectToWorldNormal(v.normal));
o.pos = v.vertex;
return o;
}
fixed4 fragfront(v2f i) : SV_Target
{
if (i.WorldPos.y > Value + objPosY) {
discard;
}
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 LightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed4 col = _Color;
float3 diffuse = _LightColor0 * col * max(0, dot(i.WorldNor,LightDir) * 0.5 + 0.5);
fixed3 color = ambient + diffuse;
return fixed4(color, _Color.a);
}
fixed4 fragback(v2f i) : SV_Target
{
if (i.WorldPos.y > Value + objPosY) {
discard;
}
return _Color;
}
ENDCG
SubShader
{
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
ZWrite On
Blend SrcAlpha OneMinusSrcAlpha
LOD 100
Pass
{
cull back//剔除背面,渲染正面
CGPROGRAM
#pragma vertex vert
#pragma fragment fragfront
ENDCG
}
Pass
{
cull front//剔除正面,渲染背面
CGPROGRAM
#pragma vertex vert
#pragma fragment fragback
ENDCG
}
}
}
烧杯内的液体始终相对世界坐标保持水平,因此需要将模型的本地顶点坐标通过UNITY_MATRIX_M矩阵转换到世界坐标,然后水平截取(即使烧杯倾斜,水杯内的液体始终保持世界空间水平)
c#代码部分文章来源:https://www.toymoban.com/news/detail-539579.html
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class Test : MonoBehaviour
{
private Material mat;
// Start is called before the first frame update
void OnEnable()
{
mat = GetComponent<MeshRenderer>().sharedMaterial;
}
// Update is called once per frame
void Update()
{
mat.SetFloat("objPosY", transform.position.y);
}
}
转换到世界空间的坐标需要以烧杯自身的坐标为基准截取,这样保证无论烧杯在时间空间的什么位置,截取的高度都是相对于烧杯本身的文章来源地址https://www.toymoban.com/news/detail-539579.html
到了这里,关于UnityShader实现液体倾倒效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!