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写,过程会简单很多,毕竟不需要自己去声明曲面细分的各个步骤,也不需要自己写光照模型和法线贴图的计算。不过我觉得如果能用顶点片段程序去实现一下,会对这个过程更了解一些。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/138381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PostMan授权认证使用

Authorization 对于很多应用&#xff0c;出于安全考虑我们的接口并不希望对外公开。这个时候就需要使用授权(Authorization)机制。 授权过程验证您是否具有访问服务器所需数据的权限。 当发送请求时&#xff0c;通常必须包含参数&#xff0c;以确保请求具有访问和返回所需数据…

蓝桥杯算法竞赛第一周题型总结

本专栏内容为&#xff1a;蓝桥杯学习专栏&#xff0c;用于记录蓝桥杯的学习经验分享与总结。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &#x1f339;&#x1f33…

skynet学习笔记02— skynet介绍、skynet基础API与环境变量

01、Skynet与Actor模型 在系统Skynet之前&#xff0c;先了解一下Skynet与Actor模型&#xff0c;下列是风云大佬的介绍以及一个大佬的博客 https://github.com/cloudwu/skynet/wiki/GettingStartedhttps://blog.csdn.net/qq769651718/article/details/79432793 02、Skynet基础…

python升级pip的时候一直失败

如图,一直提示使用 python.exe -m pip install --upgrade pip 进行升级pip,但是执行这句命令又不成功.然后综合了几篇文章以后使用了下面的命令可以升级了 python -m pip install --upgrade pip --user -i https://mirrors.aliyun.com/pypi/simple/ 主要是在推荐的语句上使用…

润色论文Prompt

你好&#xff0c;我现在开始写论文了&#xff0c;我希望你可以扮演帮我润色论文的角色我写的论文是关于xxxxx领域的xxxxx&#xff0c;我希望你能帮我检查段落中语句的逻辑、语法和拼写等问题我希望你能帮我检查以下段落中语句的逻辑、语法和拼写等问题同时提供润色版本以符合学…

Android Studio新建项目下载依赖慢,只需一个操作解决

新建的安卓工程&#xff0c;下载依赖贼慢怎么办&#xff1f;水一篇吧。 首先新建工程&#xff0c;建好以后项目就自动开始下载了&#xff0c;底部开始出现进度条&#xff0c;但是进度条一直不怎么动&#xff0c;网速也就十几k&#xff0c;要是等他下载得下一天。 直入主题&…

第四节(2):修改WORD中表格数据的方案

《VBA信息获取与处理》教程(10178984)是我推出第六套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。这部教程给大家讲解的内容有&#xff1a;跨应用程序信息获得、随机信息的利用、电子邮件的发送、VBA互联网…

Django之三板斧的使用,全局配置文件介绍,request对象方法,pycharm链接数据库,Django链接数据库,ORM的增删改查

【1】三板斧(3个方法)的使用 Httpresponse() 括号内写什么字符串&#xff0c;返回的就是什么字符串返回的是字符串 render(request&#xff0c; 静态文件 ) request是固定的静态文件是写在templates文件夹里面的&#xff0c;如&#xff0c;HTML文件 redirect( 重定向的地址 ) 重…

Markdown使用教程

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

基于VSCode + PlatformIO创建运行第一个esp32程序

文章目录 使用VSCode创建项目安装驱动下载驱动安装驱动连接开发板电脑识别开发板 编写程序烧录程序第一步、编译程序第二步、烧录程序第三步、开发板观察效果 原理讲解项目源码 在之前的课程&#xff0c;我们已经介绍了ESP32单片机&#xff0c;并且也已经安装好了开发环境&…

干洗店洗鞋店管理系统app小程序;

干洗店洗鞋店管理系统是一款专业的洗衣店管理软件&#xff0c;集成了前台收费收银系统、会员卡管理系统和财务报表系统等强大功能。界面简洁优美&#xff0c;操作直观简单。这款系统为干洗店和洗衣店提供了成本分析、利润分析、洗衣流程管理等诸多实用功能&#xff0c;用全新的…

深度学习 opencv python 公式识别(图像识别 机器视觉) 计算机竞赛

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…

嵌入式养成计划-49----ARM--计算机相关理论--ARM相关内容

一百二十三、计算机相关理论 123.1 计算机的组成 目前的计算机都还是 冯诺依曼体系 &#xff0c;也就是由&#xff1a;输入、输出、运算器、存储器、控制器&#xff0c;这个五个部分构成。输入&#xff1a;Input&#xff0c;常见的输入设备&#xff0c;比如鼠标&#xff0c;键…

LeetCode | 234. 回文链表

LeetCode | 234. 回文链表 O链接 这里的解法是先找到中间结点然后再将中间节点后面的节点逆序一下然后再从头开始和从中间开始挨个比较如果中间开始的指针到走最后都相等&#xff0c;就返回true&#xff0c;否则返回false 代码如下&#xff1a; struct ListNode* reverseLis…

18. 深度学习 - 从零理解神经网络

文章目录 本文目标预测趋势与关系波士顿房价预测 Hi, 你好。我是茶桁。 我们终于又开启新的篇章了&#xff0c;从今天这节课开始&#xff0c;我们会花几节课来理解一下深度学习的相关知识&#xff0c;了解神经网络&#xff0c;多层神经网络相关知识。并且&#xff0c;我们会尝…

ModStartBlog v8.5.0 评论开关布局调整,系统后台全面优化

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议。 功能特性 丰富的模块市场&#xff0c;后台一键快速安装 …

吴恩达《机器学习》7-1->7-4:过拟合问题、代价函数、线性回归的正则化、正则化的逻辑回归模型

一、过拟合的本质 过拟合是指模型在训练集上表现良好&#xff0c;但在新数据上的泛化能力较差。考虑到多项式回归的例子&#xff0c;我们可以通过几个模型的比较来理解过拟合的本质。 线性模型&#xff08;欠拟合&#xff09;&#xff1a; 第一个模型是一个线性模型&#xff0…

安全框架SpringSecurity-1(认证入门数据库授权)

一、Spring Security ①&#xff1a;什么是Spring Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式&#xff08;注解&#xff09;的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean&#xff0c;充分利用了Spring …

微服务-开篇-个人对微服务的理解

从吃饭说起 个人理解新事物的时候喜欢将天上飞的理念转换成平常生活中的实践&#xff0c;对比理解这些高大上的名词&#xff0c;才能让我们减少恐慌的同时加深理解。废话不多说&#xff0c;我们从吃饭开始说起&#xff0c;逐渐类比出微服务的思想。 &#xff08;个人见解&…

【基础算法模板梳理】再也不想学算法了!(待更新)

目录 1、【二分】 &#xff08;1&#xff09;rmid —— 大于等于某数的最小值 &#xff08;2&#xff09;lmid —— 小于等于某数的最大值 2、【前缀和】 &#xff08;1&#xff09;一维前缀和 &#xff08;2&#xff09;二维前缀和 3、【差分】 &#xff08;1&#x…