Unity Shader 学习笔记(18)纹理动画、顶点动画、广告牌技术

44 篇文章 5 订阅
36 篇文章 5 订阅

Unity Shader 学习笔记(18)纹理动画、顶点动画、广告牌技术

参考书籍:《Unity Shader 入门精要》
【《Real-Time Rendering 3rd》 提炼总结】(九) 第十章 · 游戏开发中基于图像的渲染技术总结


Unity Shader的内置变量

在这里插入图片描述


纹理动画

可用于代替粒子系统模拟动画效果。

帧序列动画

8x8帧序列纹理动画
在这里插入图片描述

Properties {
	_Color ("Color Tint", Color) = (1, 1, 1, 1)
	_MainTex ("Image Sequence", 2D) = "white" {}			// 包含所有关键帧图像的纹理
	_HorizontalAmount ("Horizontal Amount", Float) = 4		// 水平方向包含关键帧图像个数
	_VerticalAmount ("Vertical Amount", Float) = 4			// 同上(竖直方向)
	_Speed ("Speed", Range(1, 100)) = 30					// 播放速度
}

因为序列帧图像通常是透明纹理,所以就按半透明的方式设置。

// 序列帧图像通常包含透明通道
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}

Pass {
	Tags { "LightMode"="ForwardBase" }
	
	// 关闭深度写入,开启混合模式
	ZWrite Off
	Blend SrcAlpha OneMinusSrcAlpha

	...

	fixed4 frag (v2f i) : SV_Target {
		float time = floor(_Time.y * _Speed);				// floor()取整。CG的函数
		float row = floor(time / _HorizontalAmount);		// 行索引
		float column = time % _HorizontalAmount;		// 列索引
		
		// 纹理坐标从上到下,所以是-row。下面两种计算方法
		//half2 uv = float2(i.uv.x /_HorizontalAmount, i.uv.y / _VerticalAmount);
		//uv.x += column / _HorizontalAmount;
		//uv.y -= row / _VerticalAmount;		

		half2 uv = i.uv + half2(column, -row);
		uv.x /=  _HorizontalAmount;
		uv.y /= _VerticalAmount;
		
		fixed4 c = tex2D(_MainTex, uv);
		c.rgb *= _Color;
		
		return c;
	}
}

滚动动画

可以模拟2D跑酷游戏背景的视察效果。
在这里插入图片描述

Properties {
	_MainTex ("Base Layer (RGB)", 2D) = "white" {}			// 第一层背景(较远背景)
	_DetailTex ("2nd Layer (RGB)", 2D) = "white" {}			// 较近背景
	_ScrollX ("Base layer Scroll Speed", Float) = 1.0		// 第一层背景滚动速度
	_Scroll2X ("2nd layer Scroll Speed", Float) = 1.0		// 第二层速度
	_Multiplier ("Layer Multiplier", Float) = 1				// 纹理亮度
}
v2f vert (a2v v) {
	v2f o;
	o.pos = UnityObjectToClipPos(v.vertex);
	
	// TRANSFORM_TEX 得到初始纹理坐标,加上偏移坐标。frac为获取小数部分的值。
	o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex) + frac(float2(_ScrollX, 0.0) * _Time.y);
	o.uv.zw = TRANSFORM_TEX(v.texcoord, _DetailTex) + frac(float2(_Scroll2X, 0.0) * _Time.y);
	
	return o;
}

fixed4 frag (v2f i) : SV_Target {
	fixed4 firstLayer = tex2D(_MainTex, i.uv.xy);
	fixed4 secondLayer = tex2D(_DetailTex, i.uv.zw);
	
	fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);
	c.rgb *= _Multiplier;
	
	return c;
}

顶点动画

即修改顶点信息的动画。

一张长方形纹理,通过修改顶点信息,实现扭曲效果。
在这里插入图片描述

Properties {
	_MainTex ("Main Tex", 2D) = "white" {}
	_Color ("Color Tint", Color) = (1, 1, 1, 1)
	_Magnitude ("Distortion Magnitude", Float) = 1					// 幅度
	_Frequency ("Distortion Frequency", Float) = 1					// 频率
	_InvWaveLength ("Distortion Inverse Wave Length", Float) = 10	// 波长的倒数
	_Speed ("Speed", Float) = 0.5
}
// DisableBatching:取消批处理。需要对模型空间下的顶点位置进行偏移,所以就不合并模型。
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}

Pass {
	Tags { "LightMode"="ForwardBase" }
	
	ZWrite Off
	Blend SrcAlpha OneMinusSrcAlpha
	Cull Off							// 关闭剔除模式(正反面都显示)
	
	...
		
	v2f vert(a2v v) {
		v2f o;
		
		float4 offset;
		offset.yzw = float3(0.0, 0.0, 0.0);
		// 只改变X变量
		offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
		o.pos = UnityObjectToClipPos(v.vertex + offset);
		
		o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
		o.uv +=  float2(0.0, _Time.y * _Speed);
		
		return o;
	}

}

在这里插入图片描述

注意到前面使用内置阴影的Pass是没有处理顶点动画的,所以最后投影的阴影是错误的,需要自定义ShadowCasterPass,在这个Pass做相同顶点变换即可。

下面用到阴影的宏都在UnityCG.cginc中定义。

Pass {
	Tags { "LightMode" = "ShadowCaster" }
	
	...
	
	#pragma multi_compile_shadowcaster
	
	...

	struct v2f { 
		// 内置宏,阴影投射需要的变量
	    V2F_SHADOW_CASTER;
	};
	
	v2f vert(appdata_base v) {
		v2f o;
		
		float4 offset;
		offset.yzw = float3(0.0, 0.0, 0.0);
		offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
		v.vertex = v.vertex + offset;

		// 计算阴影
		TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
		
		return o;
	}
	
	fixed4 frag(v2f i) : SV_Target {
		// 阴影投射
	    SHADOW_CASTER_FRAGMENT(i)
	}
	ENDCG
}

广告牌技术(Billboarding)

即让纹理始终面对着镜头。本质就是构建旋转矩阵,三个基向量:表面法线、向上的方向、向右的方向。还需要指定一个锚点,即旋转中心。

计算方法(如下图):

  1. 先计算向右方向 right = up × normal。起始up是竖直向上的。
  2. 叉乘即可得到向上方向 up’ = normal × right。

Vertical Restraints值为1,所有星星不管原来什么角度都会转向到镜头。
在这里插入图片描述

Vertical Restraints值为0,所有星星固定向上方向,并最大限度面向镜头。
在这里插入图片描述

Properties {
	_MainTex ("Main Tex", 2D) = "white" {}
	_Color ("Color Tint", Color) = (1, 1, 1, 1)
	_VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1		// 垂直程度,0:固定向上,1:固定法线
}
// 需要模型空间下的位置来作为锚点计算,所以要关掉批处理。
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}

Pass { 
	Tags { "LightMode"="ForwardBase" }
	
	ZWrite Off
	Blend SrcAlpha OneMinusSrcAlpha
	Cull Off

	CGPROGRAM
	
	...
	
	v2f vert (a2v v) {
		v2f o;
		
		float3 center = float3(0, 0, 0);				// 模型空间的原点作为广告牌锚点
		float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1));	// 获取模型空间下视角
		
		float3 normalDir = viewer - center;
		// _VerticalBillboarding 为1为法线固定(始终向上),为0为向上方向固定。 
		normalDir.y =normalDir.y * _VerticalBillboarding;
		normalDir = normalize(normalDir);
		// 通过normalDir.y 先判断法线和向上是否平行(叉积会错),来改变向上方向
		float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
		float3 rightDir = normalize(cross(upDir, normalDir));
		upDir = normalize(cross(normalDir, rightDir));	
		
		// 通过三个正交基矢量,计算得到新的顶点位置
		float3 centerOffs = v.vertex.xyz - center;
		float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
      
		o.pos = UnityObjectToClipPos(float4(localPos, 1));		// 模型转裁剪空间
		o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);

		return o;
	}

关于 DisableBatching

如果要进行一些顶点动画,就要关闭批处理。性能会下降,所以尽量避免使用模型空间下的绝对位置和方向进行计算。如广告拍技术中,可以用顶点颜色来存储顶点到锚点的距离。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值