Unity地面交互效果——4、制作地面凹陷轨迹

这篇具有很好参考价值的文章主要介绍了Unity地面交互效果——4、制作地面凹陷轨迹。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

回到目录

Unity曲面细分制作雪地效果


  大家好,我是阿赵。
  上一篇介绍了曲面细分着色器的基本用法和思路,这一篇在曲面细分的基础上,制作地面凹陷的轨迹效果。

一、思路分析

  这次需要达到的效果是这样的:
unity 和土地交互,Unity引擎Shader效果,unity,游戏引擎,Shader,地面交互,曲面细分

  从效果上看,这个凹陷在地面下的轨迹,里面有法线变化的效果,然后地表模型也是真实的发生了凹陷变化。所以其实就是之前说到的法线混合轨迹和曲面细分的综合应用。
unity 和土地交互,Unity引擎Shader效果,unity,游戏引擎,Shader,地面交互,曲面细分

  曲面细分的等级实际上是根据自己的需要去调整的
unity 和土地交互,Unity引擎Shader效果,unity,游戏引擎,Shader,地面交互,曲面细分

  做到这么高的细分也可以,但实际上也没太大必要。
  之前已经介绍过法线轨迹的混合,然后有了曲面细分之后,剩下的事情就是把两者结合起来,并且做一个顶点偏移效果了。顶点偏移,实际上也是通过绘制出来的轨迹做偏移,比如我现在有一张黑白的图片,轨迹就绘制在上面,然后对贴图进行局部采样,最后把某些色值范围内的顶点做一个高度的偏移,就做出了这个凹陷的效果了。

二、贴图通道的利用

  还记得最开始的一篇的内容吗?我是使用一个顶视正交摄像机去拍摄这个范围内的轨迹的。
unity 和土地交互,Unity引擎Shader效果,unity,游戏引擎,Shader,地面交互,曲面细分

  这里的这个法线贴图,实际上只用到了RGB三个通道而已,还有一个A通道可以用。于是,可以把需要实现高度偏移的黑白图片,记录在这张贴图的A通道。
unity 和土地交互,Unity引擎Shader效果,unity,游戏引擎,Shader,地面交互,曲面细分

  这样,只需要打一个摄像机,渲染一张RenderTexture,就同时实现了法线贴图和顶点偏移2种效果了。
  值得注意的是,我这里只把需要凹陷的地方刷成了白色,然后不需要凹陷的地方默认是黑色。这样做的好处是计算简单,只需要根据A通道的0-1去决定凹陷的深度就行了。不过有时候,做轨迹是不止凹陷的,不如一个球在沙地上移动,实际上轨迹的边缘还会凸起来的。如果要实现这种效果,那么就要把平地的Alpha值设置成0.5,然后大于0.5的部分是凸起,小于0.5的部分是凹陷。

三、Shader实现

1、顶点片段着色器里的实现:

Shader "azhao/VFGround"
{
    Properties
    {
		_MainTex("Texture", 2D) = "white" {}
		_Color("Color", Color) = (1,1,1,1)
		_centerPos("CenterPos", Vector) = (0,0,0,0)
		_minDis("minVal", Float) = 0
		_maxDis("maxVal", Float) = 20
		_factor("factor", Float) = 15
		_footstepRect("footstepRect",Vector) = (0,0,0,0)
		_footstepTex("footstepTex",2D) = "gray"{}
		_height("height" ,Float) = 0.3
		_NormalTex("Normal Tex", 2D) = "black"{}
		_normalScale("normalScale", Range(-1 , 1)) = 0
		_specColor("SpecColor",Color) = (1,1,1,1)
		_shininess("shininess", Range(1 , 100)) = 1
		_specIntensity("specIntensity",Range(0,1)) = 1
		_ambientIntensity("ambientIntensity",Range(0,1)) = 1


    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
			//在正常的vertex和fragment之间还需要hull和domain,所以在这里加上声明
			#pragma hull hullProgram
			#pragma domain domainProgram
            #pragma fragment frag


            #include "UnityCG.cginc"
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Color;
			uniform float _minDis;
			uniform float _maxDis;
			uniform float3 _centerPos;
			uniform float _factor;
			float4 _footstepRect;
			sampler2D _footstepTex;
			float _height;
			sampler2D _NormalTex;
			float4 _NormalTex_ST;
			float _normalScale;
			float4 _specColor;
			float _shininess;
			float _specIntensity;
			float _ambientIntensity;
			struct a2v
			{
				float4 pos	: POSITION;
				float2 uv  : TEXCOORD0;
				float3 normal:NORMAL;
				float3 tangent:TANGENT;
			};

			struct v2t
			{				
				float2 uv  : TEXCOORD0;
				float2 footstepUV : TEXCOORD1;
				float4 worldPos	: TEXCOORD2;
				float3 worldNormal : TEXCOORD3;
				float3 worldTangent :TEXCOORD4;
				float3 worldBitangent : TEXCOORD5;
			};
			struct t2f
			{
				float4 clipPos:SV_POSITION;
				float2 uv: TEXCOORD0;				
				float2 footstepUV:TEXCOORD1;
				float4 worldPos:TEXCOORD2;
				float3 worldNormal : TEXCOORD3;
				float3 worldTangent :TEXCOORD4;
				float3 worldBitangent : TEXCOORD5;
				
			};

			struct TessOut
			{
				float2 uv  : TEXCOORD0;
				float4 worldPos	: TEXCOORD1;
				float2 footstepUV:TEXCOORD2;
				float3 worldNormal : TEXCOORD3;
				float3 worldTangent :TEXCOORD4;
				float3 worldBitangent : TEXCOORD5;
				
			};
			struct TessParam
			{
				float EdgeTess[3]	: SV_TessFactor;//各边细分数
				float InsideTess : SV_InsideTessFactor;//内部点细分数
			};

			float RemapUV(float min, float max, float val)
			{
				return (val - min) / (max - min);
			}
			half3 UnpackScaleNormal(half4 packednormal, half bumpScale)
			{
				half3 normal;
				//由于法线贴图代表的颜色是0到1,而法线向量的范围是-1到1
				//所以通过*2-1,把色值范围转换到-1到1
				normal = packednormal * 2 - 1;
				//对法线进行缩放
				normal.xy *= bumpScale;
				//向量标准化
				normal = normalize(normal);
				return normal;
			}
			//获取Lambert漫反射值
			float GetLambertDiffuse(float3 worldPos, float3 worldNormal)
			{
				float3 lightDir = UnityWorldSpaceLightDir(worldPos);
				float NDotL = saturate(dot(worldNormal, lightDir));
				
				return NDotL;
			}

			//获取BlinnPhong高光
			float GetBlinnPhongSpec(float3 worldPos, float3 worldNormal)
			{
				float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
				float specDir = max(dot(normalize(worldNormal), halfDir), 0);
				float specVal = pow(specDir, _shininess);
				return specVal;
			}

			v2t vert(a2v i)
			{
				v2t o;
				o.worldPos = mul(unity_ObjectToWorld,i.pos);
				o.uv = i.uv;
				o.footstepUV = float2(RemapUV(_footstepRect.x, _footstepRect.z, o.worldPos.x), RemapUV(_footstepRect.y, _footstepRect.w, o.worldPos.z));
				o.worldNormal = UnityObjectToWorldNormal(i.normal);
				o.worldTangent = UnityObjectToWorldDir(i.tangent);
				o.worldBitangent = cross(o.worldNormal, o.worldTangent);
				return o;
			}
			//在hullProgram之前必须设置这些参数,不然会报错
			[domain("tri")]//图元类型,可选类型有 "tri", "quad", "isoline"
			[partitioning("integer")]//曲面细分的过渡方式是整数还是小数
			[outputtopology("triangle_cw")]//三角面正方向是顺时针还是逆时针
			[outputcontrolpoints(3)]//输出的控制点数
			[patchconstantfunc("ConstantHS")]//对应之前的细分因子配置阶段的方法名
			[maxtessfactor(64.0)]//最大可能的细分段数

			//vert顶点程序之后调用,计算细分前的三角形顶点信息
			TessOut hullProgram(InputPatch<v2t, 3> i, uint idx : SV_OutputControlPointID)
			{
				TessOut o;
				o.worldPos = i[idx].worldPos;
				o.uv = i[idx].uv;
				o.footstepUV = i[idx].footstepUV;
				o.worldNormal = i[idx].worldNormal;
				o.worldTangent = i[idx].worldTangent;
				o.worldBitangent = i[idx].worldBitangent;
				return o;
			}

			//指定每个边的细分段数和内部细分段数
			TessParam ConstantHS(InputPatch<v2t, 3> i, uint id : SV_PrimitiveID)
			{
				TessParam o;
				float4 worldPos = (i[0].worldPos + i[1].worldPos + i[2].worldPos) / 3;
				float smoothstepResult = smoothstep(_minDis, _maxDis, distance(worldPos.xz, _centerPos.xz));
				float fac = max((1.0 - smoothstepResult)*_factor, 1);
				//由于我这里是根据指定的中心点和半径范围来动态算细分段数,所以才有这个计算,不然可以直接指定变量来设置。
				o.EdgeTess[0] = fac;
				o.EdgeTess[1] = fac;
				o.EdgeTess[2] = fac;
				o.InsideTess = fac;
				return o;
			}

			//在domainProgram前必须设置domain参数,不然会报错
			[domain("tri")]
			//细分之后,把信息传到frag片段程序
			t2f domainProgram(TessParam tessParam, float3 bary : SV_DomainLocation, const OutputPatch<TessOut, 3> i)
			{
				t2f o;				
				//线性转换

				float2 uv = i[0].uv * bary.x + i[1].uv * bary.y + i[2].uv * bary.z;
				o.uv = uv;
				o.footstepUV = i[0].footstepUV * bary.x + i[1].footstepUV * bary.y + i[2].footstepUV * bary.z;
				float4 footstepCol = tex2Dlod(_footstepTex, float4(o.footstepUV, 0, 0.0));
				float addVal = footstepCol.a*_height;

				float4 worldPos = i[0].worldPos * bary.x + i[1].worldPos * bary.y + i[2].worldPos * bary.z;
				worldPos.y = worldPos.y - addVal;
				o.worldPos = worldPos;
				o.clipPos = UnityWorldToClipPos(worldPos);
				o.worldNormal = i[0].worldNormal * bary.x + i[1].worldNormal * bary.y + i[2].worldNormal * bary.z;
				o.worldTangent = i[0].worldTangent * bary.x + i[1].worldTangent * bary.y + i[2].worldTangent * bary.z;
				o.worldBitangent = i[0].worldBitangent * bary.x + i[1].worldBitangent * bary.y + i[2].worldBitangent * bary.z;
				return o;
			}
            fixed4 frag (t2f i) : SV_Target
            {
    //            // sample the texture
				//float2 mainUV = i.uv*_MainTex_ST.xy + _MainTex_ST.zw;
    //            fixed4 col = tex2D(_MainTex, mainUV)*_Color;
				//fixed4 footstepCol = tex2D(_footstepTex, i.footstepUV);
				//fixed3 footstepRGB = fixed3(footstepCol.r - 0.5, footstepCol.g - 0.5, footstepCol.b - 0.5);
				//footstepRGB = footstepRGB * footstepCol.a;
				//fixed4 finalCol = col;// fixed4(saturate(col.rgb + footstepRGB), 1);
    //            return finalCol;
				//采样漫反射贴图的颜色
				half4 col = tex2D(_MainTex, i.uv*_MainTex_ST.xy + _MainTex_ST.zw);
				//计算法线贴图的UV
				half2 normalUV = i.uv * _NormalTex_ST.xy + _NormalTex_ST.zw;
				//采样法线贴图的颜色
				half4 normalCol = tex2D(_NormalTex, normalUV);

				fixed4 footstepCol = tex2D(_footstepTex, i.footstepUV);
				fixed3 footstepRGB = UnpackScaleNormal(footstepCol*footstepCol.a, _normalScale).rgb;

				//得到切线空间的法线方向
				half3 normalVal = UnpackScaleNormal(normalCol, _normalScale).rgb;
				//normalVal = footstepRGB;
				//normalVal = normalize(normalVal + footstepRGB);
				normalVal -= footstepRGB;
				//构建TBN矩阵
				float3 tanToWorld0 = float3(i.worldTangent.x, i.worldBitangent.x, i.worldNormal.x);
				float3 tanToWorld1 = float3(i.worldTangent.y, i.worldBitangent.y, i.worldNormal.y);
				float3 tanToWorld2 = float3(i.worldTangent.z, i.worldBitangent.z, i.worldNormal.z);

				//通过切线空间的法线方向和TBN矩阵,得出法线贴图代表的物体世界空间的法线方向
				float3 worldNormal = float3(dot(tanToWorld0, normalVal), dot(tanToWorld1, normalVal), dot(tanToWorld2, normalVal));

				//用法线贴图的世界空间法线,算漫反射
				half diffuseVal = GetLambertDiffuse(i.worldPos, worldNormal);
				diffuseVal = clamp(diffuseVal, 0.6, 1);
				diffuseVal = pow(diffuseVal, 1.5);

				//用法线贴图的世界空间法线,算高光角度
				half3 specCol = _specColor * GetBlinnPhongSpec(i.worldPos, worldNormal)*_specIntensity;

				//最终颜色 = 环境色+漫反射颜色+高光颜色
				half3 finalCol = UNITY_LIGHTMODEL_AMBIENT * _ambientIntensity + saturate(col.rgb*diffuseVal) + specCol;
				return half4(finalCol,1);
            }
            ENDCG
        }
    }
}

值得注意的地方:
1.为了获得细分后的顶点做偏移,所以顶点偏移的过程是写在domainProgram
2.在顶点类程序里面如果需要采样贴图,并不是使用tex2D方法,而是使用tex2Dlod方法

2、Surface着色器里的实现:

Shader "azhao/SurfaceGround"
{
	Properties
	{
		_MainTex("MainTex", 2D) = "white" {}
		_NormalTex("NormalTex", 2D) = "white" {}
		_minDis("minVal", Float) = 0
		_maxDis("maxVal", Float) = 15
		_factor("factor", Float) = 1
		_height("height",float) = 0

		_centerPos("centerPos", Vector) = (0,0,0,0)		
		_footstepRect("footstepRect", Vector) = (0,0,0,0)		
		_footstepTex("footstepTex", 2D) = "black" {}		
		[HideInInspector] _texcoord("", 2D) = "white" {}
		[HideInInspector] __dirty("", Int) = 1
	}

	SubShader
	{
		Tags{ "RenderType" = "Opaque"  "Queue" = "Geometry+0" }
		Cull Back
		CGPROGRAM
		#include "UnityStandardUtils.cginc"
		#pragma target 4.6
		#pragma surface surf Standard keepalpha addshadow fullforwardshadows vertex:vertexDataFunc tessellate:tessFunction 
		struct Input
		{
			float2 uv_texcoord;
			float3 worldPos;
		};

		sampler2D _NormalTex;
		float4 _NormalTex_ST;
		sampler2D _footstepTex;
		float4 _footstepRect;
		sampler2D _MainTex;
		float4 _MainTex_ST;
		float _minDis;
		float _maxDis;
		float4 _centerPos;
		float _factor;
		float _height;



		float RemapUV(float min, float max, float val)
		{
			return (val - min) / (max - min);
		}


		float4 tessFunction( appdata_full v0, appdata_full v1, appdata_full v2 )
		{
			float4 vertex = (v0.vertex + v1.vertex + v2.vertex) / 3;
			float3 worldPos = mul( unity_ObjectToWorld, vertex );
			float smoothstepResult = smoothstep( _minDis , _maxDis , distance(_centerPos.xz, worldPos.xz));
			float tessVal = max( ( ( 1.0 - smoothstepResult ) * _factor ) , 0.1 );
			return tessVal;
		}

		void vertexDataFunc( inout appdata_full v )
		{
			float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
			float temp_output_1_0_g1 = _footstepRect.x;
			float temp_output_1_0_g2 = _footstepRect.y;
			float2 footstepUV = float2(RemapUV(_footstepRect.x, _footstepRect.z, worldPos.x), RemapUV(_footstepRect.y, _footstepRect.w, worldPos.z));

			float4 tex2DNode = tex2Dlod(_footstepTex, float4(footstepUV, 0, 0.0));
			float offset = tex2DNode.a * _height*-0.01;
			v.vertex.y += offset;
		}

		void surf( Input i , inout SurfaceOutputStandard o )
		{
			float2 uv_NormalTex = i.uv_texcoord * _NormalTex_ST.xy + _NormalTex_ST.zw;
			float3 worldPos = i.worldPos;
			float temp_output_1_0_g1 = _footstepRect.x;
			float temp_output_1_0_g2 = _footstepRect.y;
			float2 footstepUV = float2(RemapUV(_footstepRect.x, _footstepRect.z, worldPos.x), RemapUV(_footstepRect.y, _footstepRect.w, worldPos.z));
			o.Normal = BlendNormals( UnpackNormal( tex2D( _NormalTex, uv_NormalTex ) ) , UnpackNormal( tex2D( _footstepTex, footstepUV) ) );
			float2 uv_MainTex = i.uv_texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
			o.Albedo = tex2D( _MainTex, uv_MainTex ).rgb;
			o.Alpha = 1;
		}

		ENDCG
	}
	Fallback "Diffuse"
}

  用Surface写,过程会简单很多,毕竟不需要自己去声明曲面细分的各个步骤,也不需要自己写光照模型和法线贴图的计算。不过我觉得如果能用顶点片段程序去实现一下,会对这个过程更了解一些。文章来源地址https://www.toymoban.com/news/detail-761535.html

到了这里,关于Unity地面交互效果——4、制作地面凹陷轨迹的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Unity 2021 凹陷效果设置

    Unity 2021 凹陷效果设置

     首先来到Terrain地形的第二个小图标,然后有个如图的下拉列表,选择Set Height,  

    2024年02月15日
    浏览(15)
  • 【游戏开发小技】Unity中实现Dota里的角色技能地面贴花效果(URP ShaderGraph Decal)(1)

    【游戏开发小技】Unity中实现Dota里的角色技能地面贴花效果(URP ShaderGraph Decal)(1)

    [Toggle(_SupportOrthographicCamera)] _SupportOrthographicCamera(“_SupportOrthographicCamera (default = off)”, Float) = 0 } SubShader { // 关于tags的内容可以查阅官网手册:https://docs.unity3d.com/Manual/SL-SubShaderTags.html // 为了避免渲染顺序问题, Queue必须 = 2501, 它位于透明队列中 // 在透明队列中,Unity总是从后

    2024年04月16日
    浏览(11)
  • Unity ParticleSystem制作脚印效果(记录)

    Unity ParticleSystem制作脚印效果(记录)

    记录一下,防止忘记,  间隔一定距离后创建一个粒子即可 在Start中 记录 角色位置信息 然后在Update中 检测 记录的位置和角色位置的距离 超过一定值后 发射一个粒子 New 一个发射参数 并设置好位置以及旋转 然后 通过 Emit 发射粒子即可   粒子系统设置: SimulationSpace 设置为

    2024年02月01日
    浏览(3)
  • 【Unity ShaderGraph】| 快速制作一个 抖动效果

    【Unity ShaderGraph】| 快速制作一个 抖动效果

    前言 本文将使用ShaderGraph制作一个抖动效果,可以直接拿到项目中使用。 对ShaderGraph还不了解的小伙伴可以参考这篇文章:【Unity ShaderGraph】| Shader Graph入门介绍 | 简介 | 配置环境 | 窗口介绍 | 简单案例 下面就开始看一下具体的制作流程,然后自己动手制作一个吧! 资源下载

    2024年02月03日
    浏览(6)
  • Unity——使用铰链关节制作悬挂物体效果

    Unity——使用铰链关节制作悬挂物体效果

    目的 在场景中创建一个悬挂的物体,是把多个模型悬挂在一起可以自由摇摆,类似链条的效果 效果图 前言 什么是铰链关节? 铰链关节 将两个刚体(Rigid body)组会在一起,从而将其约束为如同通过铰链连接一样进行移动。它十分适合门、链条、钟摆等模拟效果。 步骤 打开

    2023年04月24日
    浏览(24)
  • 【Unity ShaderGraph】| 制作一个 高级流体水球效果

    【Unity ShaderGraph】| 制作一个 高级流体水球效果

    前言 本文将使用ShaderGraph制作一个 高级流体水球 ,可以直接拿到项目中使用。 对ShaderGraph还不了解的小伙伴可以参考这篇文章:【Unity ShaderGraph】| Shader Graph入门介绍 | 简介 | 配置环境 | 窗口介绍 | 简单案例 下面就开始看一下具体的制作流程,然后自己动手制作一个吧! 资

    2024年02月08日
    浏览(10)
  • unity 利用Graphics.Blit来制作图片效果

    c# 的代码 source可以是当前相机的RenderTexture也可以是准备好的一张图,然后利用material提供的效果将效果输出到renderTexture,第三个参数是使用哪个pass 0表示是使用第一个 下面是例子对应的shader,是一个模糊效果

    2024年01月21日
    浏览(3)
  • 【Unity ShaderGraph】| 快速制作一个 卡通阴影色块效果

    【Unity ShaderGraph】| 快速制作一个 卡通阴影色块效果

    前言 本文将使用ShaderGraph制作一个卡通阴影色块的效果,可以直接拿到项目中使用。 对ShaderGraph还不了解的小伙伴可以参考这篇文章:【Unity ShaderGraph】| Shader Graph入门介绍 | 简介 | 配置环境 | 窗口介绍 | 简单案例 下面就开始看一下具体的制作流程,然后自己动手制作一个吧

    2024年02月06日
    浏览(9)
  • 【Unity ShaderGraph使用雪碧图制作导航光效效果】

    【Unity ShaderGraph使用雪碧图制作导航光效效果】

    效果: ShaderGraph的制作: shader中uv数和雪碧图的uv数对上就能出效果。 直接上代码: 代码绑定: 下面是雪碧图:

    2024年02月15日
    浏览(12)
  • Unity初学2——瓦片的制作以及世界的交互(2d)

    该文来自学习chutianbo老师的笔记,链接b站第56P至71P 右键点击主界面Sence➡GameObject➡2D Object➡Tilemap➡Rectanglar(这样是创建了一个RPG式的视角) 创建tiles文件夹 将素材拖入右上角打开的紫色按键TIle Palette,将素材拖入 或者直接在文件夹中创建 2.素材过大需要分割怎么办 回到素

    2023年04月09日
    浏览(6)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包