Unity地面交互效果——1、局部UV采样和混合轨迹

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

回到目录

  大家好,我是阿赵。
  这期开始,打算介绍一下地面交互的一些做法。
比如:

Unity引擎制作沙地实时凹陷网格的脚印效果

或者:

Unity引擎制作雪地效果

  这些效果的实现,需要基于一些基础的知识。所以这一篇先介绍一下简单的局部UV采样,然后映射纹理到地面的做法。
  大概需要实现的效果是这个视频的前半部分:

Unity曲面细分制作雪地效果

一、轨迹的绘制

  看这段视频的前半部分。可以看到,球在移动的过程中,在地面产生了移动的轨迹
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  这个效果可能很多朋友都会做,一般的做法是计算球的坐标相对于整个地面的位置,然后拾像素绘制在地面的遮罩贴图上面。
  不过这种做法会有一个问题,假如地面很大的时候,通过一张和整个地面匹配UV的遮罩贴图来绘制轨迹,那么这张遮罩贴图的分辨率需要多大,才能显示足够的精度呢?比如一个4096米4096米的地面,就算我们用一张40964096的贴图做遮罩,那么每平方米的面积,才占一个像素,明显是绘制不出这么清晰的轨迹图形的。
  其实我们没有必要去绘制整张贴图,只需要局部绘制就好了
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  绘制这一个小局部,然后通过局部UV采样的方式,把这个贴图叠加到大贴图上面去。
  这时候,就需要给Shader传入一个范围,让Shader知道,这个局部UV,最终占整个地面UV的多少。
地面的Shader代码是这样的:

Shader "azhao/GroundFootStep"
{
    Properties
    {
		_MainTex("Texture", 2D) = "white" {}
		_Color("Color", Color) = (1,1,1,1)
		_centerPos("CenterPos", Vector) = (0,0,0,0)
		_footstepRect("footstepRect",Vector) = (0,0,0,0)
		_footstepTex("footstepTex",2D) = "gray"{}
		_footstepColor("footstepColor",Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag


            #include "UnityCG.cginc"
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Color;

			uniform float3 _centerPos;

			float4 _footstepRect;
			sampler2D _footstepTex;
			float4 _footstepColor;
			struct appdata
			{
				float4 pos	: POSITION;
				float2 uv  : TEXCOORD0;
			};

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


			float RemapUV(float min, float max, float val)
			{
				return (val - min) / (max - min);
			}
			
			v2f vert(appdata i)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(i.pos);
				o.worldPos = mul(unity_ObjectToWorld,i.pos.xyz);
				o.uv = i.uv*_MainTex_ST.xy+ _MainTex_ST.zw;
				o.footstepUV = float2(RemapUV(_footstepRect.x, _footstepRect.z, o.worldPos.x), RemapUV(_footstepRect.y, _footstepRect.w, o.worldPos.z));
				return o;
			}
			
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv)*_Color;
				fixed4 footstepCol = tex2D(_footstepTex, i.footstepUV);
				fixed3 footstepRGB = _footstepColor.rgb;
				fixed3 finalRGB = col.rgb*(1 - footstepCol.a) + footstepRGB * footstepCol.a;
				fixed4 finalCol = fixed4(saturate(finalRGB), 1);
                return finalCol;
				return col;
            }
            ENDCG
        }
    }
}

  从代码可以看出footstepRect是一个很关键的东西,它告诉了Shader,需要绘制轨迹的范围在哪里。然后通过RemapUV方法,拿这个范围和当前的顶点世界坐标去计算出,当前的点该占整体UV的实际位置。
  这个footstepRect其实是C#动态算出来的,根据角色所在的坐标和半径,算出来一个范围。
C#的代码大概是这样:

Vector3 pos = role.transform.position;
mat.SetVector("_centerPos", pos);
mat.SetFloat("_maxVal", radius);
mat.SetVector("_footstepRect", new Vector4(pos.x - radius, pos.z - radius, pos.x + radius, pos.z + radius));

  其实就是中心点加减半径而已。
  这个做法的优点是,只需要局部绘制一张贴图,就能达到比较清晰的轨迹图形
  缺点是,只能在一定范围内显示,超出了footstepRect范围,轨迹就会消失了。

二、绘制轨迹的手段

  绘制轨迹,其实就是连贯的把某个笔刷的像素复制到一张图片上。这个应该不是很难理解的概念。
  上面的例子,球是一个笔刷,它移动的时候,它所在的位置会产生一个圆形的笔刷,通过连续每帧的覆盖,就形成了一个轨迹。
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  如果绘制的间隔拉大一点,看到的情况大概是这样的。
  那么问题来了,球移动的时候,上面说到,相对于地表贴图的footstepRect,是会变化的,所以说,我们不能直接把球的笔刷印到之前的那张图去。
比如上一张图的位置是在这里
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  下一张图的位置就变成了这里
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  留意看左下角的球,它在世界中的位置是一直没有变化的,但在这个footstepRect的局部里面,它的相对位置是变化了的。
  下面来说一下具体的做法。

1、通过摄像机绘制RenderTexture

Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  这里为了渲染一张顶视图,我是打了一个摄像机在运动的球的上方,然后摄像机跟随这球移动。
  需要注意的是,摄像机一定要是正交的,然后通过控制orthographicSize参数,可以准确的绘制符合footstepRect的范围。最后,给这个摄像机的targetTexture赋予一张RenderTexture,作为输出。

2、通过偏移来叠加上一张图

  刚才那个RenderTexture是每帧都会渲染一次的。我们需要2张RenderTexture,一张是上一次留下的,一张是这一帧渲染出来的。
  接下来就是把两张RenderTexture,通过Graphics.Blit方法合并在一起。由于Graphics.Blit方法是可以传入一个材质球的,所以可以通过写一个Shader来混合2张贴图。具体的方式是,计算上一帧和当前帧角色所在位置的偏移,然后用偏移来控制上一帧的贴图的UV采样,再把两张贴图合并在一起就可以了。

3、合并的Shader

Shader "azhao/DrawFootstep"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_lastTex("lastTex",2D) = "black"{}
		_offset("offset",Vector) = (0,0,0,0)

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

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			sampler2D _lastTex;
			float2 _offset;


            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
				half4 col = saturate(tex2D(_MainTex, i.uv));
				half3 curRGB = col.rgb * 2 - 1;
				half4 lastCol = saturate(tex2D(_lastTex, i.uv - _offset));
				float lastAlpha = lastCol.a;

				half3 lastRGB = lastCol.rgb*2-1;

				half mr = lastRGB.r*lastAlpha;

				if (col.a >0)
				{
					if (curRGB.r > 0)
					{
						if (lastAlpha == 0)
						{
							mr = curRGB.r;
						}
					}
					else if (curRGB.r < 0)
					{
						mr = min(curRGB.r,mr);
					}
				}
				else
				{
					mr = lastRGB.r;
				}
				mr = (mr + 1) / 2;
				float alpha = max(col.a, lastAlpha);
				half3 mixRGB = half3(mr, mr, mr);
				half3 finalRGB = mixRGB;
                return half4(finalRGB, alpha);

            }
            ENDCG
        }
    }
}

三、细节问题

  第一步绘制轨迹通过局部UV坐标采样,和地表的贴图纹理混合。这里会存在一个问题。通过第二步绘制出来的轨迹贴图,是Clamp平铺方式的
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  这意味着,超出了UV的0到1范围的坐标,会直接采样了0或者1的UV。具体的表现是这样的:
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  这个黑线,其实就是到边缘了,所以超出的部分,都会是黑的
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  为了解决这个问题,可以加一个渐变的遮罩叠加
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  把UV接近0和1的地方都变成纯黑色,这样就不会出现Clamp平铺的问题,也可以让接近边缘的地方不会有一个很硬的消失,而是稍微柔软的过渡。
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互
Unity地面交互效果——1、局部UV采样和混合轨迹,Unity引擎Shader效果,unity,uv,局部采样,Shader,地面交互

  所以用于绘制轨迹混合的shader会变成这样:文章来源地址https://www.toymoban.com/news/detail-734932.html

Shader "azhao/DrawFootstep"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_lastTex("lastTex",2D) = "black"{}
		_offset("offset",Vector) = (0,0,0,0)
		_maskTex("maskTex",2D) = "white"{}
		_reduceVal("reduceVal",float) = 0.001
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			sampler2D _lastTex;
			float2 _offset;
			sampler2D _maskTex;
			float _reduceVal;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
				half4 col = saturate(tex2D(_MainTex, i.uv));
				half3 curRGB = col.rgb * 2 - 1;
				half4 lastCol = saturate(tex2D(_lastTex, i.uv - _offset));
				float lastAlpha = saturate(lastCol.a - _reduceVal);
				half4 maskCol = tex2D(_maskTex, i.uv);
				half3 lastRGB = lastCol.rgb*2-1;

				half mr = lastRGB.r*lastAlpha;

				if (col.a >0)
				{
					if (curRGB.r > 0)
					{
						if (lastAlpha == 0)
						{
							mr = curRGB.r;
						}
					}
					else if (curRGB.r < 0)
					{
						mr = min(curRGB.r,mr);
					}
				}
				else
				{
					mr = lastRGB.r;
				}
				mr = (mr + 1) / 2;
				float alpha = max(col.a, lastAlpha)*maskCol.r;
				half3 mixRGB = half3(mr, mr, mr);
				half3 finalRGB = mixRGB * maskCol.rgb;
                return half4(finalRGB, alpha);

            }
            ENDCG
        }
    }
}

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

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

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

相关文章

  • Unity制作下雨中的地面效果

    Unity引擎制作下雨效果   大家好,我是阿赵。   之前介绍了Unity引擎里面通过UV偏移做序列帧动画的做法,这里再介绍一个进阶的用法,模拟地面下雨的雨点效果。   最基本的原理,还是基于这个序列帧动画的做法。不过这里做一点改变。我不再用网格的UV作为计算的

    2024年02月10日
    浏览(38)
  • Unity Shader ASE基础效果思路与代码(一):遮罩、硬边溶解、光边溶解、UV扰动

    效果展示: 思路与代码 :主纹理和遮罩纹理相乘,其中,两个纹理给到UV流动 步骤与详解 :panner节点:平移 效果展示: 思路与代码 :原图和噪声贴图的透明通道混合,改变噪声贴图的透明度即可 步骤与详解 : 在SubShader中关掉深度写入,混合模式为Alpha Blend,Tags设为透明

    2024年02月22日
    浏览(51)
  • 【游戏开发小技】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日
    浏览(52)
  • 无人机航拍高度与地面采样距离

    为搞清无人机航拍高度与地面采样距离的关系,首先需要了解像素与像元之间的细小差别(个人理解)。像素偏重于图片描述,也就是常说的一张图片像素是多少。像元则指一个像素点的实际大小。 对同样大小面积的图片,像元越小,即像素面积越小,进而该场景图片像素数

    2024年02月09日
    浏览(46)
  • 解决UGUI的图集导致Shader采样时UV错误的问题

    大家好,我是阿赵。 在我们用UGUI的时候,很多时候需要通过在UI上面挂材质球,写Shader,来实现一些特殊的效果。 这里句一个很简单的例子,只为说明问题。 这个例子是这样的,我想在某个Image上面加一个渐变遮罩,只显示角色的头像。 这里我准备了一张角色贴图,然后根

    2024年02月10日
    浏览(22)
  • Unity通过深度图做有交互效果的水泡沫

    通过深度图做交互水泡沫 大家好,我是阿赵。 这里做一个有交互效果的水面,物体浸入水面时,会根据物体的形状,有一圈水泡沫的效果,并且水泡沫的形状会跟随这物体变化。由于想做得稍微完整一点,又不想其他效果太强泡沫的风头,所以简单加了一个水面纹理流动的

    2024年02月12日
    浏览(38)
  • pygame伪3d 实现地面效果

    教程来自What is Mode 7? Let’s code it! 油管镜像

    2024年02月13日
    浏览(60)
  • pygame伪3d地面 移动效果

    2024年02月13日
    浏览(85)
  • (一)连续随机数的生成-从混合高斯分布中采样

    Example 1 : Mixture of Gaussian distribution Let X 1 ∼ N ( μ 1 , σ 1 2 ) , X 2 ∼ N ( μ 2 , σ 2 2 ) X_1 sim Nleft(mu_1, sigma_1^2right), X_2 sim Nleft(mu_2, sigma_2^2right) X 1 ​ ∼ N ( μ 1 ​ , σ 1 2 ​ ) , X 2 ​ ∼ N ( μ 2 ​ , σ 2 2 ​ ) with σ 1 0 sigma_10 σ 1 ​ 0 and σ 2 0 sigma_20 σ 2 ​ 0 , and let X 1 X_1 X 1 ​

    2024年02月11日
    浏览(41)
  • Cocos creator(2d) 使用 shader + uv 实现单张图片衔接滚动效果

    在游戏中,当我们需要让背景图片无缝衔接无限滚动时(打飞机这种背景一直滚动,或者肉鸽游戏地图一直在走等等),通常的做法是 在游戏中放两个背景node,在update中控制这两张背景图片的移动,并让其收尾衔接即可。(具体代码忽略) 可是在肉鸽类游戏中,玩家的走向是全方

    2024年02月13日
    浏览(96)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包