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

2023-11-08 05:28

本文主要是介绍Unity地面交互效果——4、制作地面凹陷轨迹,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

一、思路分析

  这次需要达到的效果是这样的:
在这里插入图片描述

  从效果上看,这个凹陷在地面下的轨迹,里面有法线变化的效果,然后地表模型也是真实的发生了凹陷变化。所以其实就是之前说到的法线混合轨迹和曲面细分的综合应用。
在这里插入图片描述

  曲面细分的等级实际上是根据自己的需要去调整的
在这里插入图片描述

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

二、贴图通道的利用

  还记得最开始的一篇的内容吗?我是使用一个顶视正交摄像机去拍摄这个范围内的轨迹的。
在这里插入图片描述

  这里的这个法线贴图,实际上只用到了RGB三个通道而已,还有一个A通道可以用。于是,可以把需要实现高度偏移的黑白图片,记录在这张贴图的A通道。
在这里插入图片描述

  这样,只需要打一个摄像机,渲染一张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 100Pass{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到1normal = 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);//计算法线贴图的UVhalf2 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 BackCGPROGRAM#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写,过程会简单很多,毕竟不需要自己去声明曲面细分的各个步骤,也不需要自己写光照模型和法线贴图的计算。不过我觉得如果能用顶点片段程序去实现一下,会对这个过程更了解一些。

这篇关于Unity地面交互效果——4、制作地面凹陷轨迹的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/368056

相关文章

基于WinForm+Halcon实现图像缩放与交互功能

《基于WinForm+Halcon实现图像缩放与交互功能》本文主要讲述在WinForm中结合Halcon实现图像缩放、平移及实时显示灰度值等交互功能,包括初始化窗口的不同方式,以及通过特定事件添加相应... 目录前言初始化窗口添加图像缩放功能添加图像平移功能添加实时显示灰度值功能示例代码总结最后前言本文将

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

使用Python制作一个PDF批量加密工具

《使用Python制作一个PDF批量加密工具》PDF批量加密‌是一种保护PDF文件安全性的方法,通过为多个PDF文件设置相同的密码,防止未经授权的用户访问这些文件,下面我们来看看如何使用Python制... 目录1.简介2.运行效果3.相关源码1.简介一个python写的PDF批量加密工具。PDF批量加密

使用Python实现生命之轮Wheel of life效果

《使用Python实现生命之轮Wheeloflife效果》生命之轮Wheeloflife这一概念最初由SuccessMotivation®Institute,Inc.的创始人PaulJ.Meyer... 最近看一个生命之轮的视频,让我们珍惜时间,因为一生是有限的。使用python创建生命倒计时图表,珍惜时间

防近视护眼台灯什么牌子好?五款防近视效果好的护眼台灯推荐

在家里,灯具是属于离不开的家具,每个大大小小的地方都需要的照亮,所以一盏好灯是必不可少的,每个发挥着作用。而护眼台灯就起了一个保护眼睛,预防近视的作用。可以保护我们在学习,阅读的时候提供一个合适的光线环境,保护我们的眼睛。防近视护眼台灯什么牌子好?那我们怎么选择一个优秀的护眼台灯也是很重要,才能起到最大的护眼效果。下面五款防近视效果好的护眼台灯推荐: 一:六个推荐防近视效果好的护眼台灯的

hdu 2907 凹陷面

给定一个多边形,让你求出其价值。价值的定义是:-p*凹面的个数+q*凸面的个数。。。最小值为0 凸面的个数就是凸包中的点的个数,但是当出现凹面时,就会减少一个凸面,这是因为这时候的凸面是虚拟出来的!!!!! 题意参考这里 点击打开链接 int cmp(double x){if(fabs(x) < 1e-8) return 0 ;if(x > 0) return 1 ;ret

用Unity2D制作一个人物,实现移动、跳起、人物静止和动起来时的动画:中(人物移动、跳起、静止动作)

上回我们学到创建一个地形和一个人物,今天我们实现一下人物实现移动和跳起,依次点击,我们准备创建一个C#文件 创建好我们点击进去,就会跳转到我们的Vision Studio,然后输入这些代码 using UnityEngine;public class Move : MonoBehaviour // 定义一个名为Move的类,继承自MonoBehaviour{private Rigidbo

轨迹规划-B样条

B样条究竟是干啥的?白话就是给出一堆点,用样条的方式,给这些点连接起来,并保证丝滑的。 同时B样条分为准均匀和非均匀,以下为准均匀为例。 参考链接1:https://zhuanlan.zhihu.com/p/50626506https://zhuanlan.zhihu.com/p/50626506 参考链接2: https://zhuanlan.zhihu.com/p/536470972h

uniapp设置微信小程序的交互反馈

链接:uni.showToast(OBJECT) | uni-app官网 (dcloud.net.cn) 设置操作成功的弹窗: title是我们弹窗提示的文字 showToast是我们在加载的时候进入就会弹出的提示。 2.设置失败的提示窗口和标签 icon:'error'是设置我们失败的logo 设置的文字上限是7个文字,如果需要设置的提示文字过长就需要设置icon并给