本文章用于帮助自己学习,因此只记录一些个人认为比较重要或者还不够熟悉的内容。
原作者:http://blog.csdn.net/candycat1992/article/

第十一章 让画面动起来

11.1 Unity Shader中的内置变量(时间篇)

动画效果往往都是把时间添加到一些变量的计算中,以便在时间变化时画面也可以随之变化。
Unity Shader提供了一系列关于时间的内置变量来允许我们方便地在Shader中访问运行时间,实现各种动画效果。下表给出了这些内置的时间变量。

11.2 纹理动画

在各种资源都比较局限的移动平台上,我们往往会使用纹理动画来代替复杂的粒子系统等模拟各种动画效果。

11.2.1序列帧动画

最常见的纹理动画之一就是序列帧动画。序列帧动画的原理非常简单,就是依次播放一系列关键帧图像,当播放速度达到一定数值时,看起来就是一个连续的动画。它的优点在于灵活性很强,我们不需要进行任何物理计算就可以得到非常细腻的动画效果

要想实现序列帧动画,我们先要提供一张包含了关键帧图像的图像。如图所示。

代码:

Shader "Unity Shaders Book/Chapter 11/ImageSequenceAnimation"
{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}SubShader{//由于序列帧图像通常是透明纹理,//我们需要设置Pass的相关状态以渲染透明效果Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}Pass{Tags{"Lightmode"="ForwardBase"}Zwrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;float _HorizontalAmount;float _VerticalAmount;float _Speed;struct a2v{float4 vertex : POSITION;float2 texcoord : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 pos : SV_POSITION;};v2f vert (a2v v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);//存储顶点纹理坐标o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{//_Time.y是自该场景加载后经过的时间,floor函数取整float time = floor(_Time.y * _Speed);//求出当前对应的行索引float row = floor(time / _HorizontalAmount);//余数就是列索引float column = time - row * _HorizontalAmount;//unity纹理坐标从下到上递增,因此row符号为-。half2 uv = i.uv + half2(column, -row);uv.x /= _HorizontalAmount;uv.y /= _HorizontalAmount;fixed4 c = tex2D(_MainTex, uv);c.rgb *= _Color;return c;}ENDCG}}Fallback "Transparent/VertexLit"
}

效果如下:

11.2.2滚动的背景

很多2D游戏都使用了不断滚动的背景来模拟游戏角色在场景中的穿梭,这些背景往往包含 了多个层(layers)来模拟一种视差效果。而这些背景的实现往往就是利用了纹理动画。在本节中, 我们将实现一个包含了两层的无限滚动的2D游戏背景。本节使用的纹理资源均来自OpenGameArt (http://opengameart.org)网站。

由于本例模拟的是2D游戏中的滚动背景,因此我们需要把摄像 机的投影模式设置为正交投影。
代码:

Shader "Unity Shaders Book/Chapter 11/Scrolling Background"
{Properties {//MainTex和DetailTex分别是第一层(较远)和第二层(较近)的背景纹理_MainTex ("Base Layer (RGB)", 2D) = "white" {}_DetailTex ("2nd Layer (RGB)", 2D) = "white" {}//ScrollX 和_Scroll2X对应了各自的水平滚动速度_ScrollX ("Base layer Scroll Speed", Float) = 1.0_Scroll2X ("2nd layer Scroll Speed", Float) = 1.0//Multiplier参数用于控制纹理的整体亮度_Multiplier ("Layer Multiplier", Float) = 1}SubShader {Tags { "RenderType"="Opaque" "Queue"="Geometry"}Pass { Tags { "LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _MainTex;sampler2D _DetailTex;float4 _MainTex_ST;float4 _DetailTex_ST;float _ScrollX;float _Scroll2X;float _Multiplier;struct a2v {float4 vertex : POSITION;float4 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float4 uv : TEXCOORD0;};v2f vert (a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);//初始纹理坐标加上偏移量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);![在这里插入图片描述](https://img-blog.csdnimg.cn/20210404165817703.gif)return o;}fixed4 frag (v2f i) : SV_Target {fixed4 firstLayer = tex2D(_MainTex, i.uv.xy);fixed4 secondLayer = tex2D(_DetailTex, i.uv.zw);//使用第二层纹理的透明通道调用lerp函数插值混合纹理fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);c.rgb *= _Multiplier;return c;}ENDCG}}FallBack "VertexLit"
}

效果如下:

11.3 顶点动画

11.3.1 流动的河流

河流的模拟是顶点动画最常见的应用之一。它的原理通常就是使用正弦函数等来模拟水流的波动效果。
代码:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Unity Shaders Book/Chapter 11/Water"
{Properties{_MainTex ("Texture", 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}SubShader{//需要关闭批处理,//因为批处理会合并所有相关的模型,//模型各自的模型空间会丢失。//本例中需要在物体模型空间下对顶点位置进行偏移Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent""DisableBatching"="True"}Pass{Tags{"LightMode"="ForwardBase"}Zwrite OffBlend SrcAlpha OneMinusSrcAlphaCull OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;float _Magnitude;float _Frequency;float _InvWaveLength;float _Speed;struct a2v {float4 vertex : POSITION;float4 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};v2f vert (a2v v){v2f o;float4 offset;//只对顶点x方向进行位移,//因此yzw的位移量置为0offset.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;o.pos = UnityObjectToClipPos(v.vertex + offset);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);o.uv +=  float2(0.0, _Time.y * _Speed);return o;}fixed4 frag(v2f i) : SV_Target{fixed4 c = tex2D(_MainTex,i.uv);c.rgb *= _Color.rgb;return c;}ENDCG}}FallBack "Transparent/VertexLit"
}

效果如下:

11.3.2 广告牌

广告牌技术会根据视角方向来旋转。一个被纹理着色的多边形(通常就是简单的四边形,这个多边形就是广告牌),使得多边形看起来好像总是面对着摄像机。广告牌技术被用于很多应用,比如渲染烟雾、云朵、闪光效果等

广告牌技术的本质就是构建旋转矩阵,而我们知道一个变换矩阵需要3个基向量。广告牌技术使用的基向量通常就是表面法线(normal)、指向上的方向(up)以及指向右的方向(right)。除此之外,我们还需要指定一个锚点(anchorlocation),这个锚点在旋转过程中是固定不变的, 以此来确定多边形在空间中的位置。

广告牌技术的难点在于,如何根据需求来构建3个相互正交的基向量。计算过程通常是,我们首先会通过初始计算得到目标的表面法线(例如就是视角方向)和指向上的方向,而两者往往是不垂直的。
但是,两者其中之一是固定的,例如当模拟草丛时,我们希望广告牌的指向上的方向永远是(0,1,0),而法线方向应该随视角变化;而当模拟粒子效果时,我们希望广告牌的法线方向是固定的,即总是指向视角方向,指向上的方向则可以发生变化。

我们假设法线方向是固定的,首先,我们根据初始的表面法线和指向上的方向来计算出目标方向的指向右的方向(通过叉积操作):
right = up × normal
对其归一化后,再由法线方向和指向右的方向计算出正交的指向上的方向即可:
up’ = normal × right
至此,我们就可以得到用于旋转的3个正交基了。下图给出了上述计算过程的图示。如果指向上的方向是固定的,计算过程也是类似的。

代码:

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Unlit/Chapter11-Billboard"
{Properties{_MainTex ("Texture", 2D) = "white" {}_Color("Color Tint",Color) = (1,1,1,1)//调整是固定法线还是固定指向上的方向_VerticalBillboarding("Vertical Restraints", Range(0, 1)) = 1}SubShader{Tags { "Queue" = "Transparent" "Ignoreprojector" = "True" "RenderType" = "Transparent""Disable Batching" = "True"}Pass{ZWrite OffBlend SrcAlpha OneMinusSrcAlphaCull OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;//让广告牌的每个面都能显示float _VerticalBillboarding;struct a2v {float4 vertex : POSITION;float4 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};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时,向上方向固定为(0,1,0)//因为法线y方向分量为0时,与之垂直的向上方向必为(0,1,0)normalDir.y =normalDir.y * _VerticalBillboarding;normalDir = normalize(normalDir);//得到粗略的向上方向(abs函数返回绝对值)//由法线方向和粗略的向上方向得到向右方向//,并归一化(因为是粗略的,所以向右方向结果不是归一化的)得到准确向右方向//,再由法线方向和向右方向得到准确的向上方向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));//根据顶点原始位置相对于中心的偏移量以及3个正交基矢量//,得到新的顶点位置。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;}fixed4 frag (v2f i) : SV_Target{fixed4 c = tex2D (_MainTex, i.uv);c.rgb *= _Color.rgb;return c;}ENDCG}}Fallback"Transparent/VertexLit"
}

效果如下:
Vertical Restraints为1:

Vertical Restraints为0:

11.3.3 注意事项

在模型空间下进行顶点动画时,需要强制取消对该Unity Shader的批处理。但是取消批处理会带来一定的性能下降,增加了 Draw Call,因此我们应该尽量避免使用模型空间下的一些绝对位置和方向来进行计算。在广告牌的例子中,为了避免显式使用模型空间的中心来作为锚点,我们可以利用顶点颜色来存储每个顶点到锚点的距离值, 这种做法在商业游戏中很常见。

其次,如果我们想要对包含了顶点动画的物体添加阴影,那么如果仍然像9.4节中那样使用内置的Diffuse等包含的阴影Pass来渲染,就得不到正确的阴影效果。
这是因为,我们讲过Unity的阴影绘制需要调用一个ShadowCasterPass,而如果直接使用这些内置的ShadowCaster Pass,这个Pass中并没有进行相关的顶点动画,因此Unity会仍然按照原来的顶点位置来计算阴影,这并不是我们希望看到的。
这时,我们就需要提供一个 自定义的ShadowCaster Pass,在这个Pass中,我们将进行同样的顶点变换过程。需要注意的是, 在前面的实现中,如果涉及半透明物体我们都把Fallback设置成了Transparent/VertexLit,而 Transparent/VertexLit没有定义ShadowCaster Pass,因此也就不会产生阴影

在11.3.1节的场景中,开启了场景中平行光的阴影效果,并添加了一个平面来接收来自“水流”的阴影。还把这个Unity Shader的Fallback设置为了内置的VertexLit,这样Unity将根据Fallback最终找到VertexLit中ShadowCasterPass来渲染阴影,效果如下:

可以看出,此时虽然Water模型发生了形变,但它的阴影并没有产生相应的动画效果。为了 正确绘制变形对象的阴影,我们就需要提供自定义的ShadowCasterPass。
代码:

Pass {Tags { "LightMode" = "ShadowCaster" }CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_shadowcaster#include "UnityCG.cginc"float _Magnitude;float _Frequency;float _InvWaveLength;float _Speed;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}

效果如下:

《Unity Shader入门精要》学习笔记第11章 让画面动起来相关推荐

  1. 《Unity Shader入门精要》笔记01 前言

    <Unity Shader入门精要>笔记01 前言 --本系列是基于人民邮电出版社<Unity Shader入门精要>(冯乐乐著 )的自学Unity Shader笔记,如果您发 ...

  2. 《Unity Shader入门精要》笔记:初级篇(2)

    本篇博客主要为个人学习所编写读书笔记,不用于任何商业用途,以及不允许任何人以任何形式进行转载. 本篇博客会补充一些扩展内容(例如其他博客链接). 本篇博客还会提供一些边读边做的效果截图.文章内所有数学 ...

  3. 《Unity Shader入门精要》笔记:初级篇(1)

    本篇博客主要为个人学习所编写读书笔记,不用于任何商业用途,以及不允许任何人以任何形式进行转载. 本篇博客会补充一些扩展内容(例如其他博客链接). 本篇博客还会提供一些边读边做的效果截图.文章内所有数学 ...

  4. 《Unity Shader入门精要》笔记:高级篇(3)以及扩展

    本篇博客主要为个人学习所编写读书笔记,不用于任何商业用途,以及不允许任何人以任何形式进行转载. 本篇博客会补充一些扩展内容(例如其他博客链接). 本篇博客还会提供一些边读边做的效果截图.文章内所有数学 ...

  5. 《Unity Shader入门精要》笔记02 第1章+第2章

    基础篇 第1章+第2章 --本系列是基于人民邮电出版社<Unity Shader入门精要>(冯乐乐著 )的自学Unity Shader笔记,如果您发现了本文的纰漏,还望不吝指正. 基础篇 ...

  6. 【unity shader 入门精要 读书笔记】透明

    一.透明 1.透明度测试[Alpha Test] 它采用一种"霸道极端"的机制:只要有一个片元的透明度不满足条件[通常是小于某个阈值],那么它对应的片元就会被舍弃.被舍弃的片元将不 ...

  7. 《Unity Shader入门精要》笔记:基础篇(2)

    本篇博客主要为个人学习所编写读书笔记,不用于任何商业用途,以及不允许任何人以任何形式进行转载. 本篇博客会补充一些扩展内容(例如其他博客链接). 本篇博客还会提供一些边读边做的效果截图.文章内所有数学 ...

  8. Unity Shader入门精要读书笔记(14)

    尽管渲染大多以照片写实主义为主要目标,但是非真实感渲染(NPR)也经常被使用. 卡通风格的渲染 卡通风格是游戏中常见的一种渲染风格.使用这种风格的游戏画面通常有一些共有的特点,例如物体都被黑色的线条描 ...

  9. 【unity shader 入门精要 读书笔记】折射

    当光线从一种介质[例如空气]斜射入另外一种介质[例如玻璃]时,传播方向一般会发生改变. 当给定入射角时,可以使用 斯涅耳定律[Snell's Law]来计算反射角. 当光从介质 1 沿着和表面法线夹角 ...

最新文章

  1. 日记 [2007年01月26日] 用 phpMyAdmin 让 MySQL 数据库管理温和化
  2. Android判断当前的android设备是否处于联网状态
  3. Deleted表用于存储DELETE和UPDATE语句所影响的行的复本
  4. 在 Delphi 下使用 DirectSound (4): 设置音量、相位、播放频率和播放位置
  5. 深度学习,究竟该如何学?
  6. qq地区采集_用户诉QQ浏览器违法收集个人隐私,法院裁定腾讯立即停止相关行为...
  7. JavaScript实现使用DisjointSet 检测无向循环算法(附完整源码)
  8. 护理方面关于人工智能的构想_如何提出惊人的AI,ML或数据科学项目构想。
  9. oracle获取一段时间内所有的小时、天、月
  10. WebService之Axis2 (3):使用services.xml文件发布WebService
  11. 【渝粤教育】电大中专电商运营实操_1作业 题库
  12. 史玉柱:创业不是靠忽悠,我的最后四个忠告
  13. Linux9.0下构建FTP服务器
  14. I2C总线驱动框架详解
  15. JSON格式输出Struts2
  16. java web项目个人博客_javaWeb项目个人博客系统
  17. Servlet面试题
  18. zabbix mysql安装配置_Zabbix安装图解教程 | 系统运维
  19. 懵逼树上懵逼果:学习二分搜索树
  20. SSM搭建-Spring之bean的属性值XML注入方式(4)

热门文章

  1. Ubuntu14.04安装librealsense(camera R200)
  2. XSS闯关小游戏通关笔记
  3. 程序员鼓励师,六年前昙花一现的职业,现在居然还存在?
  4. 【蓝桥杯31日冲刺】冲刺第11日(C++)
  5. 融优学堂生物演化14.9
  6. sha2 java_java – sun.security.provider.SHA2使用100%cpu并在一段时间后挂起5分钟
  7. 两个月面试6家(美团、搜狐)已拿滴滴18k*16薪offer
  8. 智能扫地机器人 陀螺仪导航系统
  9. 分享一个把照片变成漫画的方法
  10. 使用vs2008+wdk7600编译遇到的坑