写在前面

最近一直在思考下面的学习该怎么进行,当然自己有在一边做项目一边学OpenGL,偶尔翻翻论文之类的。但是,写shader是一个需要实战和动手经验的过程,而模仿是前期学习的必经之路。很多人都会问,怎么学shader,看什么书。当然我经验也不够,目前的路线是:掌握一门着色语言+读几本经典书籍+学习优秀的shader实例+动手实践+动手实践+动手实践。每一个都不容易,所以学shader是一个漫长而艰辛的过程。

当当当~所以,在继Surface Shader系列之后,我打算学习一下现在已有的各种案例shader。这些shader可能来自于某些网站,可能来自于开源项目,也可能来自于我自己看书的总结。而ShadowGun就是其中一个开源项目。

ShadowGun

ShadowGun其实最开始是2011年的一个移动平台的第三人称射击游戏。当然,也是用Unity开发的。当年,由于在画面上的出色表现赢得了很多眼球~更难能可贵的是,在2012的时候,它的开发者放出了示例场景,来让更多的开发者学习如何优化移动平台上的shader。下载地址请戳官方博客。看不懂英文的可以看这篇(写得很不错)。项目里共包含了将近20个优化后的shader。关于使用许可的问题,项目里的shader都是可以免费使用的,而贴图和模型是不可用于商业用途的呦~

虽然ShadowGun的出场时间有点久远了,但很多技术还是可以借鉴滴~而且它现在仍然在更新,并且价格为高昂的¥30,可见其对自信程度。

ShadowGun里包含了几个比较重要的shader,例如非常有名的旗帜飘动的shader,动态效果的天空盒子的shader,环境高光纹理映射等等。而这篇的飞机坠毁的浓烟效果shader应该是其中非常好理解的一篇。我们也从这里开始学习。

浓烟效果

飞机坠毁的浓烟效果可以从下图看出来:

传统的实现这种效果通常是使用粒子系统,而众所周知,粒子系统对性能的消耗太大,更何况是资源紧缺的移动平台。因此ShadowGun使用了网格+纹理+移动UV的方法来模拟这个效果。同时,还巧妙地使用了顶点颜色和透明度,来模拟火焰颜色以及平滑网格的边缘。结果从画面上看来表现还是可以接受的~

具体分析见下。

Shader源码

Shader "MADFINGER/Environment/Scroll 2 Layers Sine AlphaBlended" {
Properties {
_MainTex ("Base layer (RGB)", 2D) = "white" {}
_DetailTex ("2nd layer (RGB)", 2D) = "white" {}
_ScrollX ("Base layer Scroll speed X", Float) = 1.0
_ScrollY ("Base layer Scroll speed Y", Float) = 0.0
_Scroll2X ("2nd layer Scroll speed X", Float) = 1.0
_Scroll2Y ("2nd layer Scroll speed Y", Float) = 0.0
_SineAmplX ("Base layer sine amplitude X",Float) = 0.5
_SineAmplY ("Base layer sine amplitude Y",Float) = 0.5
_SineFreqX ("Base layer sine freq X",Float) = 10
_SineFreqY ("Base layer sine freq Y",Float) = 10
_SineAmplX2 ("2nd layer sine amplitude X",Float) = 0.5
_SineAmplY2 ("2nd layer sine amplitude Y",Float) = 0.5
_SineFreqX2 ("2nd layer sine freq X",Float) = 10
_SineFreqY2 ("2nd layer sine freq Y",Float) = 10
_Color("Color", Color) = (1,1,1,1) _MMultiplier ("Layer Multiplier", Float) = 2.0
} SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } Blend SrcAlpha OneMinusSrcAlpha
Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) } LOD 100 CGINCLUDE
#pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
#pragma exclude_renderers molehill
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _DetailTex; float4 _MainTex_ST;
float4 _DetailTex_ST; float _ScrollX;
float _ScrollY;
float _Scroll2X;
float _Scroll2Y;
float _MMultiplier; float _SineAmplX;
float _SineAmplY;
float _SineFreqX;
float _SineFreqY; float _SineAmplX2;
float _SineAmplY2;
float _SineFreqX2;
float _SineFreqY2;
float4 _Color; struct v2f {
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
fixed4 color : TEXCOORD1;
}; v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord.xy,_MainTex) + frac(float2(_ScrollX, _ScrollY) * _Time);
o.uv.zw = TRANSFORM_TEX(v.texcoord.xy,_DetailTex) + frac(float2(_Scroll2X, _Scroll2Y) * _Time); o.uv.x += sin(_Time * _SineFreqX) * _SineAmplX;
o.uv.y += sin(_Time * _SineFreqY) * _SineAmplY; o.uv.z += sin(_Time * _SineFreqX2) * _SineAmplX2;
o.uv.w += sin(_Time * _SineFreqY2) * _SineAmplY2; o.color = _MMultiplier * _Color * v.color;
return o;
}
ENDCG Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
fixed4 frag (v2f i) : COLOR
{
fixed4 o;
fixed4 tex = tex2D (_MainTex, i.uv.xy);
fixed4 tex2 = tex2D (_DetailTex, i.uv.zw); o = tex * tex2 * i.color; return o;
}
ENDCG
}
}
}

其实,ShadowGun里的Shader都不长,但有些shader要完全理解还是需要一些时间的。当然这篇还是很简单的~

SubShader Tags

Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }

这个shader使用的tags算是Unity里半透明物体渲染的标配。 "Queue"="Transparent"指明了该shader的渲染队列,"IgnoreProjector"="True"指明该shader不会受Projector的影响(半透明物体一般设为true),"RenderType"="Transparent"指明了它的渲染类别,文档上说是可以被用于shader replacement,但我现在还不是很理解,有人知道请留言告诉我~谢谢。

关于SubShader Tags的说明,请见官网

渲染设置

	Blend SrcAlpha OneMinusSrcAlpha
Cull Off
Lighting Off
ZWrite Off
Fog { Color (0,0,0,0) }

这篇shader的特点就是充分利用了alpha混合的技术,因此第一行就是指明了它的混合系数。Alpha混合是本篇的重点。在场景中所有的shader被渲染完毕后,每个shader产生的像素写入了帧缓存中,因为它们的渲染是按照一定顺序的,因此如何控制这些前后像素的混合顺序就是靠Blend指令控制的。它会指定源像素和目标像素的混合系数,按这个系数对两种像素进行处理后作为输出像素。具体可见这篇文章以及官网。而Blend SrcAlpha OneMinusSrcAlpha就是用于alpha混合的系数,也就是说靠alpha通道来控制像素颜色。

Cull Off 指明该shader不会剔除面,即背面也会被渲染。

ZWrite Off 指明不写入深度缓存,而是根据渲染顺序来决定显示的。其实"Queue"="Transparent"会自动生成ZWrite Off 语句的。

其他命令可以参见官网

算法分析

这篇shader的关键在于vert函数:

	v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord.xy,_MainTex) + frac(float2(_ScrollX, _ScrollY) * _Time);
o.uv.zw = TRANSFORM_TEX(v.texcoord.xy,_DetailTex) + frac(float2(_Scroll2X, _Scroll2Y) * _Time); o.uv.x += sin(_Time * _SineFreqX) * _SineAmplX;
o.uv.y += sin(_Time * _SineFreqY) * _SineAmplY; o.uv.z += sin(_Time * _SineFreqX2) * _SineAmplX2;
o.uv.w += sin(_Time * _SineFreqY2) * _SineAmplY2; o.color = _MMultiplier * _Color * v.color;
return o;
}

里面主要用到了下面的函数公式

u = sin(freq * x) * ampl + scroll * x;

上述公式了表示了U方向上的输出。这个函数其实就是正弦波函数+一次函数,由此得到特定方向上的波动效果。这种函数的函数图像(其中scroll = -2,ampl = 0.005,freq = 250)类似下面这样:

可以看出来就是一个倾斜波动的样子,以此来模拟火焰缓慢波动并上移的样子。

Shader利用了两种纹理来模拟立体效果,每张纹理对应了6个参数,分别代表了U方向和V方向上的函数参数。

除了函数里利用,这篇shader还有一个非常巧妙的地方,就是利用了顶点颜色。从一开始的动态图可以看出来其中有红色的火焰,但其实这不是纹理里的颜色,而是顶点颜色。如果我们把纹理都去掉,可以发现它真实的模型其实是长这样的:

也就是说它本身的顶点颜色就模拟了火焰颜色。他们很巧妙地利用了顶点颜色本身及其alpha通道的值,来模拟从火焰到浓烟的过渡效果。下面的代码虽然只有一行,但起到了很关键的作用:

		o.color = _MMultiplier * _Color * v.color;

Shader允许我们在面板中(通过_MMultiplier和_Color)在顶点颜色的基础上调整整体颜色,并将结果存储到v2f中。要注意的是,这里的顶点颜色,即v.color,包含了重要的透明度信息,浓烟的透明过渡效果其实都是它的功劳。

写在最后

站在前人的肩膀上总是能看得更远。这篇Shader虽小,但体现了很多很常见的手法:alpha混合,UV动画,利用模型顶点信息来减少纹理输入等等。感谢前人们的贡献和分享,希望大家可以有所收获~下次见啦!

【Unity Shaders】ShadowGun系列之一——飞机坠毁的浓烟效果的更多相关文章

  1. 【Unity Shaders】Transparency —— 使用alpha通道创建透明效果

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  2. 【Unity Shaders】学习笔记——SurfaceShader(十一)光照模型

    [Unity Shaders]学习笔记——SurfaceShader(十一)光照模型 转载请注明出处:http://www.cnblogs.com/-867259206/p/5664792.html ...

  3. 【Unity Shaders】学习笔记——SurfaceShader(十)镜面反射

    [Unity Shaders]学习笔记——SurfaceShader(十)镜面反射 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以. 水 ...

  4. 【Unity Shaders】学习笔记——SurfaceShader(九)Cubemap

    [Unity Shaders]学习笔记——SurfaceShader(九)Cubemap 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以 ...

  5. 【Unity Shaders】学习笔记——SurfaceShader(八)生成立方图

    [Unity Shaders]学习笔记——SurfaceShader(八)生成立方图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5630261.html ...

  6. 【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图

    [Unity Shaders]学习笔记——SurfaceShader(七)法线贴图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5627565.html 写 ...

  7. 【Unity Shaders】学习笔记——SurfaceShader(六)混合纹理

    [Unity Shaders]学习笔记——SurfaceShader(六)混合纹理 转载请注明出处:http://www.cnblogs.com/-867259206/p/5619810.html 写 ...

  8. 【Unity Shaders】学习笔记——SurfaceShader(五)让纹理动起来

    [Unity Shaders]学习笔记——SurfaceShader(五)让纹理动起来 转载请注明出处:http://www.cnblogs.com/-867259206/p/5611222.html ...

  9. 【Unity Shaders】学习笔记——SurfaceShader(四)用纹理改善漫反射

    [Unity Shaders]学习笔记——SurfaceShader(四)用纹理改善漫反射 转载请注明出处:http://www.cnblogs.com/-867259206/p/5603368.ht ...

随机推荐

  1. 【codevs 1911 孤岛营救问题】

    ·为了分析方便,可以先做一个题目简化.去掉"钥匙"这个条件,那么就是一个BFS或者SPFA--现在加上该条件.如本题只给出最多两种钥匙,当然你可以继续坚持BFS等方式,时间不会太差 ...

  2. bzoj1926[Sdoi2010]粟粟的书架 二分 主席树

    1926: [Sdoi2010]粟粟的书架 Time Limit: 30 Sec  Memory Limit: 552 MBSubmit: 1064  Solved: 421[Submit][Stat ...

  3. oracle 分页查询数据重复问题

    最近在做项目的时候发现一个问题,oracle 在查询分页数据的时候,有几条数据重复查询了,并且有几条数据在分页的时候消失了.百度了一下发现,ORACLE 在查询数据的时候返回的行不是固定的,他只是按照 ...

  4. java 第三次作业

    (一)学习总结 1.阅读下面程序,分析是否能编译通过?如果不能,说明原因.应该如何修改?程序的运行结果是什么?为什么子类的构造方法在运行之前,必须调用父 类的构造方法?能不能反过来? class Gr ...

  5. 百度移动深度学习 Mobile-deep-learning(MDL)

    Free and open source mobile deep learning framework, deploying by Baidu. This research aims at simpl ...

  6. 【docker简易笔记】docker基础信息的分享

    docker 使用的频率越来越高,所以在后续的一些博客中会分享一些docker的安装和使用. 一.docker介绍   "Docker 最初是 dotCloud 公司创始人 Solomon ...

  7. JAVA 访问WebRoot下的目录文件

    转自 http://blog.csdn.net/jian_csdn/article/details/46119313 ClassLoader classLoader = Thread.currentT ...

  8. koa2+webSocket 聊天室

    做了一个简单的的聊天室,用来看看 koa和 websocket的使用还是挺好的,已经放到gitHub. https://github.com/zhaowanhua/koa2WebSocket

  9. c# 操作数据库

    查询 string strConnection = "Data Source=(local);Initial Catalog=zpractice;Integrated Security=SS ...

  10. Printer for Me

    今天,良心系部终于给我配了打印机^^. 办公室门外还挂了牌子.