Unity大面积草地渲染——2、草地的动态交互

news2024/9/27 17:36:14

大家好,我是阿赵。
这里继续讲大面积草地渲染的第二个部分,草地动态交互。这里主要有风吹效果和球体碰撞效果2种。

一、风吹效果

Unity使用shader控制草的渲染和动画


风吹动草的效果,主要还是使用顶点程序来控制顶点的偏移
回顾一下之前的基础草的shader的顶点程序

v2f vert (appdata v)
            {
                v2f o;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;

				o.pos = UnityObjectToClipPos(v.vertex+float3(vVertexOffset.x, hVertexOffset, vVertexOffset.y));
                return o;
            }

在这个顶点程序里面,已经使用顶点偏移来实现了草的受重力弯曲的形态,所以现在只需要对它进行一些小修改,加入一个风力的影响,就可以实现左右摇摆的效果了。
这里我添加一个风的方向,还有一个Sin曲线来模拟风的左右摇摆:

float2 wind = _windOffset * hVal*_SinTime.w;
float2 windPosXZOffset = vVertexOffset + wind;
o.pos = UnityObjectToClipPos(v.vertex + float3(windPosXZOffset.x, hVertexOffset, windPosXZOffset.y));

在这里插入图片描述
在这里插入图片描述

通过调节windOffset,可以让草摆动的幅度产生变化

修改之后的完整shader是这样的:

Shader "azhao/GrassWind"
{
    Properties
    {
		_MainTex("MainTex", 2D) = "white" {}
		_hmin("hmin", Range(0 , 1)) = 0
		_hmax("hmax", Range(0 , 1)) = 1
		_hOffset("hOffset", Range(-1 , 1)) = 0
		_vmin("vmin", Range(0 , 1)) = 0
		_vmax("vmax", Range(0 , 1)) = 1
		_vOffset("vOffset", Range(-5 , 5)) = 0
		_topCol("topCol", Color) = (0,1,0,0)
		_windOffset("windOffset", Vector) = (0,0,0,0)
		_bottomCol("bottomCol", Color) = (0,0,0,0)

    }
    SubShader
    {
		Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
		Cull Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag


			#include "UnityShaderVariables.cginc"
			#pragma target 3.0
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;

            };

            struct v2f
            {                
                float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 centerPos : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float3 hvVal : TEXCOORD3;
            };

			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;
			uniform float2 _windOffset;
			uniform float _hOffset;
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;

            v2f vert (appdata v)
            {
                v2f o;

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;
				float2 wind = _windOffset * hVal*_SinTime.w;


				float2 windPosXZOffset = vVertexOffset + wind;

				o.pos = UnityObjectToClipPos(v.vertex+float3(windPosXZOffset.x, hVertexOffset, windPosXZOffset.y));
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {

                // sample the texture
                half4 col = tex2D(_MainTex, i.uv);
				half3 finalCol = col.rgb * _topCol.rgb*i.hvVal.z + col.rgb;
				finalCol = clamp(finalCol*i.hvVal.x + _bottomCol * (1 - i.hvVal.x)*finalCol,  half3(0, 0, 0), half3(1, 1, 1));
				half alpha = col.a;
				clip(alpha - 0.5);
                return half4(finalCol,alpha);
            }
            ENDCG
        }
		
		//为了产生影子,加多一个pass
		Pass {
			Name "ShadowCaster"
			Tags { "LightMode" = "ShadowCaster" }

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 2.0
			#include "UnityCG.cginc"

						struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;

			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 centerPos : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float3 hvVal : TEXCOORD3;
			};

			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;
			uniform float2 _windOffset;
			uniform float _hOffset;
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;

			v2f vert(appdata v)
			{
				v2f o;

				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;
				float2 wind = _windOffset * hVal*_SinTime.w;
				float2 windPosXZOffset = vVertexOffset + wind;
				o.pos = UnityObjectToClipPos(v.vertex + float3(windPosXZOffset.x, hVertexOffset, windPosXZOffset.y));
				return o;
			}

			half4 frag(v2f i) : SV_Target
			{

				// sample the texture
				half4 col = tex2D(_MainTex, i.uv);
				clip(col.a - 0.5);
				return col;
			}
			ENDCG

		}

    }
}

二、和球的交互

unity草地互动


这里说的和球交互,是因为我不想花太多的时间去放一个角色模型进去并控制,所以我就简单的用一个球来代替。其实是模拟了一个人在草丛里面行走时,对草的交互。
既然是草被人挤开的动画,那么还是使用顶点动画来做了
在这里插入图片描述

这里需要定义一下球对草地的影响范围
在这里插入图片描述

假设球的坐标可以输入到shader里面,那么求出草的顶点和球坐标的距离,就可以过滤出离球比较近的一圈才会受到球的影响。
在这里插入图片描述

在刚才的顶点程序上面,再添加一点代码:

float roleDis = (1 - distance(o.worldPos.xz, rolePos.xz));
float2 roleNor = (o.worldPos.xz - rolePos.xz)*step(0, roleDis)*(roleDis*_roleMul);
float2 rolePosXZOffset = vVertexOffset + wind * (1 - roleNor) + roleNor * hVal;
float rolePosYOffset = hVertexOffset - saturate(roleDis*_roleHOffset);
o.pos = UnityObjectToClipPos(v.vertex+float3(rolePosXZOffset.x, rolePosYOffset, rolePosXZOffset.y));
return o;

需要注意:
1、rolePos不需要在shader的Properties里面声明,它是一个全局的变量,在C#代码里面,使用Shader.SetGlobalVector(“rolePos”, role.transform.position);来给它赋值
2、这里模拟的是一个角色的影响,如果是多个角色在草地里面移动,可以把rolePos全局变量变成数组,具体的实现可以自己试试。

这时候顶点的控制就受到了3个方面的影响:
1、重力
2、风力
3、球体的碰撞

完整的Shader:

Shader "azhao/GrassWindBall"
{
    Properties
    {
		_MainTex("MainTex", 2D) = "white" {}
		_hmin("hmin", Range(0 , 1)) = 0
		_hmax("hmax", Range(0 , 1)) = 1
		_hOffset("hOffset", Range(-1 , 1)) = 0
		_vmin("vmin", Range(0 , 1)) = 0
		_vmax("vmax", Range(0 , 1)) = 1
		_vOffset("vOffset", Range(-5 , 5)) = 0
		_topCol("topCol", Color) = (0,1,0,0)
		_windOffset("windOffset", Vector) = (0,0,0,0)
		_bottomCol("bottomCol", Color) = (0,0,0,0)
		_roleMul("roleMul", Range(0 , 10)) = 0
		_roleHOffset("roleHOffset", Range(0 , 10)) = 0
    }
    SubShader
    {
		Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
		Cull Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

			#pragma target 3.0
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {                
                float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 centerPos : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float3 hvVal : TEXCOORD3;
            };

			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;
			uniform float2 _windOffset;
			uniform float3 rolePos;
			uniform float _roleMul;
			uniform float _hOffset;
			uniform float _roleHOffset;
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;
			SamplerState sampler_MainTex;

            v2f vert (appdata v)
            {
                v2f o;

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;
				float2 wind = _windOffset * hVal*_SinTime.w;
				float roleDis = (1 - distance(o.worldPos.xz, rolePos.xz));
				float2 roleNor = (o.worldPos.xz - rolePos.xz)*step(0, roleDis)*(roleDis*_roleMul);
				float2 rolePosXZOffset = vVertexOffset + wind * (1 - roleNor) + roleNor * hVal;
				float rolePosYOffset = hVertexOffset - saturate(roleDis*_roleHOffset);
				o.pos = UnityObjectToClipPos(v.vertex+float3(rolePosXZOffset.x, rolePosYOffset, rolePosXZOffset.y));
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
                half4 col = tex2D(_MainTex, i.uv);
				half3 finalCol = col.rgb * _topCol.rgb*i.hvVal.z + col.rgb;
				finalCol = clamp(finalCol*i.hvVal.x + _bottomCol * (1 - i.hvVal.x)*finalCol,  half3(0, 0, 0), half3(1, 1, 1));
				half alpha = col.a;
				clip(alpha - 0.5);
                return half4(finalCol,alpha);
            }
            ENDCG
        }
		
		//为了产生影子,加多一个pass
		Pass {
			Name "ShadowCaster"
			Tags { "LightMode" = "ShadowCaster" }

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 2.0
			#include "UnityCG.cginc"

			 struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 centerPos : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float3 hvVal : TEXCOORD3;
			};

			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;
			uniform float2 _windOffset;
			uniform float3 rolePos;
			uniform float _roleMul;
			uniform float _hOffset;
			uniform float _roleHOffset;
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;
			SamplerState sampler_MainTex;

			v2f vert(appdata v)
			{
				v2f o;

				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;
				float2 wind = _windOffset * hVal*_SinTime.w;
				float roleDis = (1 - distance(o.worldPos.xz, rolePos.xz));
				float2 roleNor = (o.worldPos.xz - rolePos.xz)*step(0, roleDis)*(roleDis*_roleMul);
				float2 rolePosXZOffset = vVertexOffset + wind * (1 - roleNor) + roleNor * hVal;
				float rolePosYOffset = hVertexOffset - saturate(roleDis*_roleHOffset);
				o.pos = UnityObjectToClipPos(v.vertex + float3(rolePosXZOffset.x, rolePosYOffset, rolePosXZOffset.y));
				return o;
			}

			half4 frag(v2f i) : SV_Target
			{
				// sample the texture
				half4 col = tex2D(_MainTex, i.uv);

				clip(col.a - 0.5);
				return col;
			}
			ENDCG

		}

    }
}

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

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

相关文章

全方位揭秘!大数据从0到1的完美落地之Shuffle和调优

MapReduce高级 shuffle阶段 概述 MapReduce会确保每个reducer的输入都是按键排序的。从map方法输出数据开始、到作为输入数据传给reduce方法的过程称为shuffle。在此,我们将学习shuffle是如何工作的,因为它有助于我们理解工作机制(如果需要…

MYSQL数据库进阶多表查询,MYSQL数据库主键和外键

目录 友情提醒第一章:MYSQL数据库多表主键和外键1)外键介绍(FOREIGN KEY)2)外键约束作用2)三种情况下添加外键约束①一对一关系②一对多关系多对多关系 4)删除外键约束 第二章:MYSQL…

配置您的 Web 服务器以包含 X-Frame-Options 标头

介绍 X-Frame-Options HTTP 响应头是用来给浏览器指示允许一个页面可否在 <frame>, </iframe> 或者 <object> 中展现的标记。网站可以使用此功能&#xff0c;来确保自己网站的内容没有被嵌套到别人的网站中去&#xff0c;也从而避免了点击劫持 (clickjackin…

mysql数据库之备份和恢复

1.数据备份的重要性 备份的主要目的是灾难恢复。 在生产环境中&#xff0c;数据的安全性至关重要。 任何数据的丢失都可能产生严重的后果。 造成数据丢失的原因&#xff1a; 程序错误人为,操作错误,运算错误,磁盘故障灾难&#xff08;如火灾、地震&#xff09;和盗窃. 2.数据…

Python爬虫入门之爬虫解析提取数据的四种方法

本文主要介绍了Python爬虫入门之爬虫解析提取数据的四种方法&#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家Python爬虫的学习有所帮助。 基础爬虫的固定模式 笔者这里所谈的基础爬虫&#xff0c;指的是不需要处理像异步加载、验证码、代理等高阶爬虫技术的爬虫…

Vscode开发第一个Vue+Element Plus示例

Vscode开发第一个VueElement Plus示例 目前&#xff0c;前端开发工具非常多&#xff0c;如Webstorm、Atom、HBuilder、Visual Studio Code、Sublime Text、Notepad等。对于有经验的开发者来说&#xff0c;使用哪一款工具都可以。笔者习惯使用Visual Studio Code。 Visual St…

什么是镜像?阿里云服务器镜像是什么?镜像怎么选?

阿里云服务器镜像就是云服务器的装机盘&#xff0c;镜像是为云服务器安装操作系统的。阿里云镜像分为类型分为公共镜像、自定义镜像、共享镜像、云市场镜像和社区镜像&#xff0c;如下图&#xff1a; 目录 什么是镜像&#xff1f; 镜像种类说明 公共镜像 自定义镜像 共享…

【HarmonyOS】轻量级智能穿戴应用如何在页面中实现数据传递与全局变量的使用

【关键词】 轻量级智能穿戴、LiteWearable、数据传递、全局变量 【问题描述】 开发轻量级智能穿戴LiteWearable应用&#xff0c;在app.js中定义全局变量&#xff0c;在页面中通过this.$app.$def.xxx获取时&#xff0c;报错TypeError: Cannot read property $def of undefined…

FlinkTableAPI与SQL编程实战

FlinkTableAPI与SQL编程实战 接下来我们一起来进入到FlinkSQL的编码实战当中&#xff0c;通过代码来实现FlinkSQL的编码开发 1、Flink TableAPI实践 1.1、创建Maven工程 并添加以jar包坐标依赖 <properties><maven.compiler.source>8</maven.compiler.source…

Spring Redis 启用TLS配置支持(踩坑解决)

由于线上Redis要启用TLS,搜遍了google百度也没一个标准的解决方案,要不这个方法没有,要不那个类找不到...要不就是配置了还是一直连不上redis.... 本文基于 spring-data-redis-2.1.9.RELEASE 版本来提供一个解决方案: 1.运维那边提供过来三个文件,分别是redis.crt redis.key …

设计模式 -- 解释器模式

前言 月是一轮明镜,晶莹剔透,代表着一张白纸(啥也不懂) 央是一片海洋,海乃百川,代表着一块海绵(吸纳万物) 泽是一柄利剑,千锤百炼,代表着千百锤炼(输入输出) 月央泽,学习的一种过程,从白纸->吸收各种知识->不断输入输出变成自己的内容 希望大家一起坚持这个过程,也同…

易基因:精原干细胞移植后出生小鼠子代中的精子DNA甲基化变化机制|新研究

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 精原干细胞移植&#xff08;Spermatogonial stem cell transplantation&#xff0c;SSCT&#xff09;被提议作为儿童癌症幸存者的生育疗法。SSCT首先冷冻保存睾丸活检&#xff0c;然后再…

【2023 · CANN训练营第一季】应用开发深入讲解——第二章 模型推理

学习目标 学习资源 内存管理 在线课程 文档 模型推理 文档 第1节 AscendCL运行资源管理 运行资源管理概述 申请运行管理资源时&#xff0c;需按顺序依次申请&#xff1a; Device 、 Context 、 Stream &#xff0c;然后根据实际需求调用 aclrtGetRunMode 接口获取软件栈的…

黑马Redis实战项目——黑马点评笔记06 | 好友关注

黑马Redis实战项目——黑马点评笔记06 | 好友关注 1、关注和取关2、共同关注2.1 查看他人主页2.2 查询共同关注A 改造关注和取关功能B 求交集 3、关注推送3.1 Feed 流分析3.1.1、拉模式&#xff08;读扩散&#xff09;3.1.2、推模式&#xff08;写扩散&#xff09;3.1.3、推拉结…

C++ JPEG编码

依据上一篇的JPEG编码所得到的RGB信息&#xff0c;我们可以重新对RGB图片进行编码&#xff0c;也可对其他图片如BMP所得到的RGB信息进行编码,来得到*.jpg文件&#xff0c;注意我这里实现的JPEG编码不知道为啥编码出来的文件比原来大了好多。 还有要注意的地方&#xff0c;下面会…

【计算机三级网络技术】 第六篇 交换机及其配置

文章目录 IPS&#xff08;入侵防护系统&#xff09;相关知识点蓝牙服务器技术DNS 服务器WWW 服务器FTP 服务器邮件&#xff08;Winmail 邮件服务器&#xff09;生成树协议IEEEVLAN 标识的描述DHCP 服务器 IPS&#xff08;入侵防护系统&#xff09;相关知识点 1、入侵防护系统&…

迅为i.MX6ULL开发板生成 KEY 文件,并安装

使用“ssh-keygen” 生成个四个 key 文件“ssh_host_rsa_key” “ssh_host_dsa_key” “ssh_host_ecdsa_key” 和“ssh_host_ed25519_key” 。 1 在虚拟机 Ubuntu 控制台&#xff0c; “ /home/ssh/openssh-4.6p1” 目录下&#xff0c; 使用命 令“ssh-keygen -t rsa -f ssh…

帮助客户实现自助服务,企业可以打造产品知识库来解决

随着科技的不断发展&#xff0c;越来越多的企业开始将自助服务作为一种解决客户问题的方式。自助服务不仅可以提高客户满意度&#xff0c;还可以减少企业的工作量和成本。为了帮助客户实现自助服务&#xff0c;企业可以打造产品知识库来解决客户问题。本文将介绍产品知识库的定…

shell脚本----函数

文章目录 一、函数的定义1.1 shell函数:1.2函数如何定义 二、函数的返回值三、函数的传参四、函数变量的作用范围五、函数的递归六、函数库 一、函数的定义 1.1 shell函数: 使用函数可以避免代码重复使用函数可以将大的工程分割为若干小的功能模块&#xff0c;代码的可读性更…

数字农业农村解决方案(ppt可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 数字农业农村发展现状 数据基础薄弱&#xff1a;数据资源分散&#xff0c;天空地一体化数据获取能力弱&#xff1b;资源数字化、产业数字化水平不高&#xff0c;部分农业数…