UV坐标应用范例——计算屏幕坐标作为UV

迷幻角色背景

大家好,我是阿赵。
之前介绍过了经典的Shader写法,物体顶点坐标在顶点程序转换到裁剪空间,然后在片段程序里面通过模型的UV进行贴图采样,然后把颜色显示在模型上面。
之前也介绍过经典的顶点程序应用,树木的生长。通过特定的信息,控制顶点的偏移,模拟出树干树枝生长的效果。
这次带来一个我觉得比较经典的UV坐标控制例子。
在这里插入图片描述

一、例子说明

在做这个例子之前,先来考虑一个问题。我们采样模型的贴图,是不是必须使用模型的UV信息?
答案肯定是否的。
我之前在很多例子里面已经用过MatCap效果,采样MatCap就并不是用模型的UV坐标,而是把物体的法线方向转到观察空间,然后再转换到[0,1]的范围,作为采样MatCap贴图的UV信息,可以看代码回顾一下:

float2 GetMatCapUV(float3 objNormal)
	{
float3 normalWorld = mul(unity_ObjectToWorld, objNormal);
float3 normalView = mul(UNITY_MATRIX_IT_MV, normalWorld);
return normalView.xy*0.5+0.5;
}

在这次这个例子里面,我做了一个使用顶点坐标转屏幕空间坐标的操作,使用这个屏幕坐标作为UV来采样贴图。这样得到的效果,模型上的贴图会一直正面朝着屏幕,不会因为模型的旋转或者摄像机的旋转而导致贴图的方向发生变化。模型的顶点范围,只会影响到贴图显示的范围大小。
为了让这个例子看起来更有意思,所以我使用了2个视频作为贴图,所以我先会说一下在Unity里面怎样使用AVProVideo插件播放视频。

二、详细做法说明

1、AVProVideo播放视频

1.导入AVProVideo

在这里插入图片描述

先购买插件,然后导入项目,会看到有一个AVProVideo文件夹,里面会有Demo,想深入了解的话可以去看看各种demo的应用。我这个例子用的不是很复杂,只是单纯的用来播放视频而已,所以那些360播放、VR播放的功能,就不去管了。

2.创建播放器

在这里插入图片描述

导入插件之后,在创建物体对象的地方就可以创建MediaPlayer,在场景里面创建MediaPlayer。

3.指定播放的视频

在这里插入图片描述

选择MediaPlayer,可以看到选项。资源路径里面可以指定播放视频的地址。然后可以设置自动打开、自动播放、循环等。

4.渲染到指定的材质球

在这里插入图片描述

添加一个ApplyToMaterial的组件,可以把指定的MediaPlayer渲染的视频,以RenderTexture的形式传入指定的材质球。

5.为例子准备的2个视频

(1)背景视频

这个背景是我自己用AE做出来的一个循环背景,如果有人有兴趣,可以提出来,我下次再告诉大家怎样做。
在这里插入图片描述

(2)角色身上的视频

这个是AVProVideo插件的demo自带的视频,本来是一个可以贴到球体上的循环视频,我觉得效果不错,就直接拿来做例子了。
在这里插入图片描述

2、制作背景材质

背景其实就是是一个面片,但由于没有拿模型的UV坐标作为采样,而是拿了顶点坐标转屏幕坐标作为UV来采样,所以不管怎样旋转面片,显示的画面都会是正对着我们的
在这里插入图片描述
在这里插入图片描述

关键的计算步骤是:

1.顶点程序

o.screen_pos = ComputeScreenPos(o.vertex);

其中ComputeScreenPos函数是UnityCG.cginc内置的,通过这个方法获取到的屏幕坐标的取值范围已经是[0,1],比自己计算方便。

inline float4 ComputeScreenPos (float4 pos)
{
    float4 o = pos * 0.5f;
    o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
    o.zw = pos.zw;
    
    return o;
}

2.片段程序

//用透视除法,将裁剪空间坐标转换到NDC空间坐标

			float2 screenUV = i.screen_pos.xy / (i.screen_pos.w + 0.000001);
			float aspect = _ScreenParams.x / _ScreenParams.y;
			float aspectx = aspect * _scale.x;
			screenUV.x *= aspectx;
			screenUV.x += (1 - aspectx) / 2;
			//判断屏幕UV是否左上角是0,是否需要上下翻转V坐标。
			//由于例子用的是视频插件生成的RenderTexture,没有Repeat,所以不能直接UV坐标乘以-1,改为用1-V坐标
			if(_ProjectionParams.x < 0)
			{
				screenUV.y = 1 - screenUV.y;
			}
			//用屏幕UV采样贴图,得到颜色1
			half4 col = tex2D(_MainTex, screenUV);

这里的重点是用透视除法求NDC空间的坐标。然后用_ScreenParams.x / _ScreenParams.y;求出屏幕的长宽比例,进行偏移,让画面可以在屏幕中间居中。
不过如果单纯是这样计算出UV然后进行采样,会发现画面是上下颠倒的,所以还要根据_ProjectionParams.x判断屏幕UV是否左上角是0,是否需要上下翻转V坐标。
最后就可以把计算出来的UV坐标来采样贴图了。

3、角色材质制作

角色的材质是在背景材质上做了加工。
这里分别用2套UV对角色模型进行了采样,一套是正常的模型UV、另外一套是顶点坐标转屏幕坐标(和背景的计算方式一样)
两套UV都是采样传进去的视频作为贴图,具体的效果如下:
在这里插入图片描述
在这里插入图片描述

接下来要做的事情就非常简单了,我很喜欢用一张噪声图,把它自身的UV平铺数值改成0,0,然后用一个_Time.y来滚动UV坐标,就能采样噪声图的单个像素,做出黑白灰闪动的效果:

float noiseUV = i.uv*_noiseMap_ST.xy + _noiseMap_ST.zw;
noiseUV.x += _Time.y*_speed;
float4 noiseCol = tex2D(_noiseMap, noiseUV);
float noiseVal = step(0.5, noiseCol.r);

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

最后,对黑白灰的效果做step,让它只会出现黑白,然后用来混合刚才两套UV的采样结果,就做出了2种效果之间的闪烁变化了。
float4 finalCol = col * noiseVal + col2 * (1 - noiseVal);

三、完整Shader

Shader "azhao/CenterImgRole"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_scale("Scale",Vector) = (1,1,1,1)
		_noiseMap("Noise",2D) = "black"{}
		_speed("Speed",float) = 1
	}
		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
				{
					float4 vertex : SV_POSITION;
					float2 uv : TEXCOORD0;
					float4 screen_pos:TEXCOORD1;
				};

				sampler2D _MainTex;
				float4 _MainTex_ST;
				float4 _scale;
				float _speed;
				sampler2D _noiseMap;
				float4 _noiseMap_ST;
				v2f vert(appdata v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.uv = TRANSFORM_TEX(v.uv, _MainTex);
					//获取顶点对应的屏幕空间坐标
					o.screen_pos = ComputeScreenPos(o.vertex);
					return o;
				}

				half4 frag(v2f i) : SV_Target
				{
					//用透视除法,将裁剪空间坐标转换到NDC空间坐标
					float2 screenUV = i.screen_pos.xy / (i.screen_pos.w + 0.000001);
					float aspect = _ScreenParams.x / _ScreenParams.y;
					float aspectx = aspect * _scale.x;
					screenUV.x *= aspectx;
					screenUV.x += (1 - aspectx) / 2;
					//判断屏幕UV是否左上角是0,是否需要上下翻转V坐标。
					//由于例子用的是视频插件生成的RenderTexture,没有Repeat,所以不能直接UV坐标乘以-1,改为用1-V坐标
					if(_ProjectionParams.x < 0)
					{
						screenUV.y = 1 - screenUV.y;
					}
					//用屏幕UV采样贴图,得到颜色1
					half4 col = tex2D(_MainTex, screenUV);

					//用正常模型坐标采样贴图,得到颜色2
					half4 col2 = tex2D(_MainTex, i.uv);
					//用模型坐标采样一张噪声贴图,用于混合颜色1和颜色2
					float noiseUV = i.uv*_noiseMap_ST.xy + _noiseMap_ST.zw;
					noiseUV.x += _Time.y*_speed;
					float4 noiseCol = tex2D(_noiseMap, noiseUV);
					float noiseVal = step(0.5, noiseCol.r);
					float4 finalCol = col * noiseVal + col2 * (1 - noiseVal);

					return finalCol;
				}
				ENDCG
			}
		}
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值