转发请保持地址:http://blog.csdn.net/stalendp/article/details/40859441

AngryBots是Unity官方的一个非常棒的样例。非常有研究价值。

曾经研究的时候。因为其内容丰富,一时间不知道从哪入手写文章分析。

这一段时间研究shader技术比較多一些,就从shader的这一方面開始吧。首先分析当中的一个屏幕特效:当主角受到攻击时会出现的全屏效果(postScreenEffect)。效果例如以下:

事实上这是一种的Bloom效果,相关文件有:MobileBloom.js 和 MobileBloom.shader;关于怎样查看这两个文件,请參考下图:

JS代码分析

MobileBloom.js部分代码例如以下:

function OnRenderImage (source : RenderTexture, destination : RenderTexture) {
#if UNITY_EDITOR
FindShaders ();
CheckSupport ();
CreateMaterials ();
#endif agonyTint = Mathf.Clamp01 (agonyTint - Time.deltaTime * 2.75f); var tempRtLowA : RenderTexture = RenderTexture.GetTemporary (source.width / 4, source.height / 4, rtFormat);
var tempRtLowB : RenderTexture = RenderTexture.GetTemporary (source.width / 4, source.height / 4, rtFormat); // prepare data apply.SetColor ("_ColorMix", colorMix);
apply.SetVector ("_Parameter", Vector4 (colorMixBlend * 0.25f, 0.0f, 0.0f, 1.0f - intensity - agonyTint)); // downsample & blur Graphics.Blit (source, tempRtLowA, apply, agonyTint < 0.5f ? 1 : 5);
Graphics.Blit (tempRtLowA, tempRtLowB, apply, 2);
Graphics.Blit (tempRtLowB, tempRtLowA, apply, 3); // apply apply.SetTexture ("_Bloom", tempRtLowA);
Graphics.Blit (source, destination, apply, QualityManager.quality > Quality.Medium ? 4 : 0); RenderTexture.ReleaseTemporary (tempRtLowA);
RenderTexture.ReleaseTemporary (tempRtLowB);
}

知识点准备

1)OnRenderImage函数

这是一个回调函数,是MonoBehaviour的生命周期的一部分。每一帧都会被调用;当这个函数被调用时。全部的3d渲染已经完毕,渲染结果以參数source传入到函数中,后期效果的实现就是对source的处理,并把结果整合到destination中。这个函数所在的脚本一般绑定在Camera上。

此函数仅仅有在Unity Pro版本号中才可以使用。

2)Graphics.Blit函数

static void Blit(Texture source, RenderTexture dest);
static void Blit(Texture source, RenderTexture dest, Material mat, int pass = -1);
static void Blit(Texture source, Material mat, int pass = -1);

这个函数就像过转化器一样。source图片经过它的处理变成了dest图片,当中材质对象mat负责算法实施(更准确的说法:算法是绑定到该mat上的shader来实现的。shader能够有多个pass。能够通过pass參数指定特定的shader,-1表示运行这个shader上全部的pass)

3)RenderTexture.GetTemporary函数RenderTexture.ReleaseTemporary函数

GetTemporary获取暂时的RenderTexture。ReleaseTemporary用来释放指定的RenderTexture

RenderTexture一般在GPU中实现,速度快但资源稀缺。unity内部对RenderTexture做了池化操作。以便复用之。对GetTemporary函数的调用事实上就是获取unity中RenderTexture的引用;当处理完之后。使用ReleaseTemporary来释放对此RenderTexture的引用。达到复用的目的。提高性能。

JS代码分析

了解了三个知识点,上面代码的功能就很清晰了,分析例如以下:

  • a)获取两个渲染贴图tempRtLowA和tempRtLowB(长宽都是原图的1/4,用以加快渲染速度)
  • b)设置Mat中Shader的參数
  • c)通过Mat来处理贴图,终于渲染到destination贴图中。用来显示
  • d)释放暂时的贴图。

这里先解释a和c。

【步骤a】。获取两个贴图,并缩小到原来的1/16(长宽都缩小为原来的1/4,面积为原来的1/16),节约了GPU内存,同一时候提高渲染速度;因为接下来的步骤是对图片进行模糊处理(对质量要求不高),这样做是可行的。

【步骤c】(注:调用Blit函数来过滤贴图,当中最后一个数字參数是用来指代shader的pass的)

pass1 或者 pass5, 提取颜色中最亮的部分。pass2 对高亮图片进行纵向模糊;pass3 对高亮图片进行横向模糊;pass0或pass4;把模糊的图片叠加到原图片上。

一个亮点。先经过横向模糊。再经过纵向模糊的过程,例如以下图所看到的(能够把这理解为“使一个点向周围扩散的算法”)

图解算法

如今的重点是【步骤c】中的shader算法是怎么实现的了。先图解一下算法:

图1 原图

图2【初始化】原图缩放成原来的1/16

图3【步骤1】扩大高亮区域

图4 【步骤2】纵向模糊

图5 【步骤3】横向模糊

图6 【步骤4a】(原图 + 步骤3的效果)终于叠加的效果,这个效果称之为glow或者bloom。

图7 【步骤4b】(原图 + 步骤3的效果)终于叠加的效果 《===(注意:这个效果须要在步骤1中加入红色成份)

调节步骤1中的图片颜色强度。能够形成对应的动画,例如以下图所看到的:

Shader分析

接下来,我将依照上图的序列来分析shader開始。

图3【步骤1】扩大高亮区域

js代码:

 Graphics.Blit (source, tempRtLowA, apply, 1);  

shader代码:

struct v2f_withMaxCoords {
half4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
half2 uv2[4] : TEXCOORD1;
}; v2f_withMaxCoords vertMax (appdata_img v)
{
v2f_withMaxCoords o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord;
o.uv2[0] = v.texcoord + _MainTex_TexelSize.xy * half2(1.5,1.5);
o.uv2[1] = v.texcoord + _MainTex_TexelSize.xy * half2(-1.5,1.5);
o.uv2[2] = v.texcoord + _MainTex_TexelSize.xy * half2(-1.5,-1.5);
o.uv2[3] = v.texcoord + _MainTex_TexelSize.xy * half2(1.5,-1.5);
return o;
} fixed4 fragMax ( v2f_withMaxCoords i ) : COLOR
{
fixed4 color = tex2D(_MainTex, i.uv.xy);
color = max(color, tex2D (_MainTex, i.uv2[0]));
color = max(color, tex2D (_MainTex, i.uv2[1]));
color = max(color, tex2D (_MainTex, i.uv2[2]));
color = max(color, tex2D (_MainTex, i.uv2[3]));
return saturate(color - ONE_MINUS_INTENSITY);
} // 1
Pass {
CGPROGRAM #pragma vertex vertMax
#pragma fragment fragMax
#pragma fragmentoption ARB_precision_hint_fastest ENDCG
}

这段代码的作用能够描写叙述为:当渲染某一点时。在这一点及其周围四点(左上、右上、左下、右下)中。选取最亮的一点作为该点的颜色。详细解释为:在vertMax的代码中,构造了向四个方向偏移的uv坐标。结合本身uv。共5个uv,一起提交给openGL。光栅化后传给fragmentShader使用。在fragMax中从5个uv所相应的像素中。选取当中最大的作为颜色输出。结果如图3所看到的。

图4 【步骤2】纵向模糊

js端

Graphics.Blit (tempRtLowA, tempRtLowB, apply, 2);

Shader端代码:

struct v2f_withBlurCoords {
half4 pos : SV_POSITION;
half2 uv2[4] : TEXCOORD0;
}; v2f_withBlurCoords vertBlurVertical (appdata_img v)
{
v2f_withBlurCoords o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv2[0] = v.texcoord + _MainTex_TexelSize.xy * half2(0.0, -1.5);
o.uv2[1] = v.texcoord + _MainTex_TexelSize.xy * half2(0.0, -0.5);
o.uv2[2] = v.texcoord + _MainTex_TexelSize.xy * half2(0.0, 0.5);
o.uv2[3] = v.texcoord + _MainTex_TexelSize.xy * half2(0.0, 1.5);
return o;
} fixed4 fragBlurForFlares ( v2f_withBlurCoords i ) : COLOR
{
fixed4 color = tex2D (_MainTex, i.uv2[0]);
color += tex2D (_MainTex, i.uv2[1]);
color += tex2D (_MainTex, i.uv2[2]);
color += tex2D (_MainTex, i.uv2[3]);
return color * 0.25;
} // 2
Pass {
CGPROGRAM #pragma vertex vertBlurVertical
#pragma fragment fragBlurForFlares
#pragma fragmentoption ARB_precision_hint_fastest ENDCG
}

这段代码的作用能够描写叙述为:当渲染某一点时,在竖直方向上距其0.5和1.5个单位的四个点(上下各两个)的颜色叠加起来。作为该点的颜色。

结果如图4所看到的。

图5 【步骤3】横向模糊 (同图四的描写叙述)

图6 【步骤4a】终于叠加的效果

(原图 + 步骤3的效果)终于叠加的效果,这个效果称之为glow或者bloom。

js段代码:

apply.SetTexture ("_Bloom", tempRtLowA);
Graphics.Blit (source, destination, apply, 0);

Shader端代码:

struct v2f_simple {
half4 pos : SV_POSITION;
half4 uv : TEXCOORD0;
}; v2f_simple vertBloom (appdata_img v)
{
v2f_simple o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xyxy;
#if SHADER_API_D3D9
if (_MainTex_TexelSize.y < 0.0)
o.uv.w = 1.0 - o.uv.w;
#endif
return o;
} fixed4 fragBloom ( v2f_simple i ) : COLOR
{
fixed4 color = tex2D(_MainTex, i.uv.xy);
return color + tex2D(_Bloom, i.uv.zw);
} // 0
Pass {
CGPROGRAM #pragma vertex vertBloom
#pragma fragment fragBloom
#pragma fragmentoption ARB_precision_hint_fastest ENDCG
}

这段代码的作用能够描写叙述为:把图5的结果叠加到原图上。

结果如图6所看到的。

Shader的完整代码

MobileBloom.shader:

Shader "Hidden/MobileBloom" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Bloom ("Bloom (RGB)", 2D) = "black" {}
} CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex;
sampler2D _Bloom; uniform fixed4 _ColorMix; uniform half4 _MainTex_TexelSize;
uniform fixed4 _Parameter; #define ONE_MINUS_INTENSITY _Parameter.w struct v2f_simple {
half4 pos : SV_POSITION;
half4 uv : TEXCOORD0;
}; struct v2f_withMaxCoords {
half4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
half2 uv2[4] : TEXCOORD1;
}; struct v2f_withBlurCoords {
half4 pos : SV_POSITION;
half2 uv2[4] : TEXCOORD0;
}; v2f_simple vertBloom (appdata_img v)
{
v2f_simple o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xyxy;
#if SHADER_API_D3D9
if (_MainTex_TexelSize.y < 0.0)
o.uv.w = 1.0 - o.uv.w;
#endif
return o;
} v2f_withMaxCoords vertMax (appdata_img v)
{
v2f_withMaxCoords o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord;
o.uv2[0] = v.texcoord + _MainTex_TexelSize.xy * half2(1.5,1.5);
o.uv2[1] = v.texcoord + _MainTex_TexelSize.xy * half2(-1.5,1.5);
o.uv2[2] = v.texcoord + _MainTex_TexelSize.xy * half2(-1.5,-1.5);
o.uv2[3] = v.texcoord + _MainTex_TexelSize.xy * half2(1.5,-1.5);
return o;
} v2f_withBlurCoords vertBlurVertical (appdata_img v)
{
v2f_withBlurCoords o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv2[0] = v.texcoord + _MainTex_TexelSize.xy * half2(0.0, -1.5);
o.uv2[1] = v.texcoord + _MainTex_TexelSize.xy * half2(0.0, -0.5);
o.uv2[2] = v.texcoord + _MainTex_TexelSize.xy * half2(0.0, 0.5);
o.uv2[3] = v.texcoord + _MainTex_TexelSize.xy * half2(0.0, 1.5);
return o;
} v2f_withBlurCoords vertBlurHorizontal (appdata_img v)
{
v2f_withBlurCoords o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv2[0] = v.texcoord + _MainTex_TexelSize.xy * half2(-1.5, 0.0);
o.uv2[1] = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5, 0.0);
o.uv2[2] = v.texcoord + _MainTex_TexelSize.xy * half2(0.5, 0.0);
o.uv2[3] = v.texcoord + _MainTex_TexelSize.xy * half2(1.5, 0.0);
return o;
} fixed4 fragBloom ( v2f_simple i ) : COLOR
{
fixed4 color = tex2D(_MainTex, i.uv.xy);
return color + tex2D(_Bloom, i.uv.zw);
} fixed4 fragBloomWithColorMix ( v2f_simple i ) : COLOR
{
fixed4 color = tex2D(_MainTex, i.uv.xy); half colorDistance = Luminance(abs(color.rgb-_ColorMix.rgb));
color = lerp(color, _ColorMix, (_Parameter.x*colorDistance));
color += tex2D(_Bloom, i.uv.zw); return color;
} fixed4 fragMaxWithPain ( v2f_withMaxCoords i ) : COLOR
{
fixed4 color = tex2D(_MainTex, i.uv.xy);
color = max(color, tex2D (_MainTex, i.uv2[0]));
color = max(color, tex2D (_MainTex, i.uv2[1]));
color = max(color, tex2D (_MainTex, i.uv2[2]));
color = max(color, tex2D (_MainTex, i.uv2[3]));
return saturate(color + half4(0.25,0,0,0) - ONE_MINUS_INTENSITY);
} fixed4 fragMax ( v2f_withMaxCoords i ) : COLOR
{
fixed4 color = tex2D(_MainTex, i.uv.xy);
color = max(color, tex2D (_MainTex, i.uv2[0]));
color = max(color, tex2D (_MainTex, i.uv2[1]));
color = max(color, tex2D (_MainTex, i.uv2[2]));
color = max(color, tex2D (_MainTex, i.uv2[3]));
return saturate(color - ONE_MINUS_INTENSITY);
} fixed4 fragBlurForFlares ( v2f_withBlurCoords i ) : COLOR
{
fixed4 color = tex2D (_MainTex, i.uv2[0]);
color += tex2D (_MainTex, i.uv2[1]);
color += tex2D (_MainTex, i.uv2[2]);
color += tex2D (_MainTex, i.uv2[3]);
return color * 0.25;
} ENDCG SubShader {
ZTest Always Cull Off ZWrite Off Blend Off
Fog { Mode off } // 0
Pass {
CGPROGRAM #pragma vertex vertBloom
#pragma fragment fragBloom
#pragma fragmentoption ARB_precision_hint_fastest ENDCG
}
// 1
Pass {
CGPROGRAM #pragma vertex vertMax
#pragma fragment fragMax
#pragma fragmentoption ARB_precision_hint_fastest ENDCG
}
// 2
Pass {
CGPROGRAM #pragma vertex vertBlurVertical
#pragma fragment fragBlurForFlares
#pragma fragmentoption ARB_precision_hint_fastest ENDCG
}
// 3
Pass {
CGPROGRAM #pragma vertex vertBlurHorizontal
#pragma fragment fragBlurForFlares
#pragma fragmentoption ARB_precision_hint_fastest ENDCG
}
// 4
Pass {
CGPROGRAM #pragma vertex vertBloom
#pragma fragment fragBloomWithColorMix
#pragma fragmentoption ARB_precision_hint_fastest ENDCG
}
// 5
Pass {
CGPROGRAM #pragma vertex vertMax
#pragma fragment fragMaxWithPain
#pragma fragmentoption ARB_precision_hint_fastest ENDCG
}
}
FallBack Off
}

參考文献

官方样例AngryBots的链接地址:http://u3d.as/content/unity-technologies/angry-bots/5CF

《Unity Shaders and Effects Cookbook》的章节:

Chapter 10 Screen Effects with Unity Render Textures

id=05l3h2YEl-UC&pg=PT381&dq=Unity+Shaders+and+Effects+Cookbook+google+book&source=gbs_toc_r&cad=3#v=onepage&q&f=false">Chapter 11 Gameplay and Screen Effects

[GPU Gems] Real-Time Glow:http://http.developer.nvidia.com/GPUGems/gpugems_ch21.html

【OpenGL】Shader实例分析(九)- AngryBots中的主角受伤特效的更多相关文章

  1. 【OpenGL】Shader实例分析(七)- 雪花飘落效果

    转发请保持地址:http://blog.csdn.net/stalendp/article/details/40624603 研究了一个雪花飘落效果.感觉挺不错的.分享给大家,效果例如以下: 代码例如 ...

  2. 【OpenGL】Shader实例分析(六)- 卡牌特效

    转发请保持地址:http://blog.csdn.net/stalendp/article/details/30989295 本文将介绍怎么通过alpha通道来隐藏信息.并实现卡牌特效. 执行效果例如 ...

  3. 实例分析Vue.js中 computed和methods不同机制

    在vue.js中,有methods和computed两种方式来动态当作方法来用的 1.首先最明显的不同 就是调用的时候,methods要加上() 2.我们可以使用 methods 来替代 comput ...

  4. php中return的用法实例分析

    本文实例讲述了php中return的用法.分享给大家供大家参考.具体分析如下: 首先,它的意思就是返回;return()是语言结构而不是函数,仅在参数包含表达式时才需要用括号将其括起来.当返回一个变量 ...

  5. C++中重载、重写(覆盖)和隐藏的区别实例分析

    这篇文章主要介绍了C++中重载.重写(覆盖)和隐藏的区别,是C++面向对象程序设计非常重要的概念,需要的朋友可以参考下 本文实例讲述了C++中重载.重写(覆盖)和隐藏的区别,对于C++面向对象程序设计 ...

  6. 【玩转cocos2d-x之四十】怎样在Cocos2d-x 3.0中使用opengl shader?

    有小伙伴提出了这个问题.事实上GLProgramCocos2d-x引擎自带了.全然能够直接拿来用. 先上图吧. 使用opengl前后的对照: watermark/2/text/aHR0cDovL2Js ...

  7. 实例分析ASP.NET在MVC5中使用MiniProfiler监控MVC性能的方法 

    这篇文章主要为大家详细介绍了ASP.NET MVC5使用MiniProfiler监控MVC性能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 MiniProfiler ,一个简单而有效的迷你剖析器 ...

  8. C#保留2位小数几种场景总结 游标遍历所有数据库循环执行修改数据库的sql命令 原生js轮盘抽奖实例分析(幸运大转盘抽奖) javascript中的typeof和类型判断

    C#保留2位小数几种场景总结   场景1: C#保留2位小数,.ToString("f2")确实可以,但是如果这个数字本来就小数点后面三位比如1.253,那么转化之后就会变成1.2 ...

  9. Python中的单继承与多继承实例分析

    Python中的单继承与多继承实例分析 本文实例讲述了Python中的单继承与多继承.分享给大家供大家参考,具体如下: 单继承 一.介绍 Python 同样支持类的继承,如果一种语言不支持继承,类就没 ...

随机推荐

  1. 远程图形界面:使用putty+xmin远程登录ubuntu-kde

    让我继续用反人类的编辑器Vim和emacs,我宁愿自断三指.因此,在Win端配置WinSCP+Putty+Xming远程操作ubuntu. 参考链接:putty+xming远程登录Ubuntu16.0 ...

  2. C++ 泛型程序设计与STL模板库(1)---泛型程序设计简介及STL简介与结构

    泛型程序设计的基本概念 编写不依赖于具体数据类型的程序 将算法从特定的数据结构中抽象出来,成为通用的 C++的模板为泛型程序设计奠定了关键的基础 术语:概念 用来界定具备一定功能的数据类型.例如: 将 ...

  3. Codeforces_768_D_(概率dp)

    D. Jon and Orbs time limit per test 2 seconds memory limit per test 256 megabytes input standard inp ...

  4. 00Enterprise Resource Planning

    Enterprise Resource Planning         企业资源计划即 ERP (Enterprise Resource Planning),由美国 Gartner Group 公司 ...

  5. 洛谷——P1342 请柬

    P1342 请柬 题目描述 在电视时代,没有多少人观看戏剧表演.Malidinesia古董喜剧演员意识到这一事实,他们想宣传剧院,尤其是古色古香的喜剧片.他们已经打印请帖和所有必要的信息和计划.许多学 ...

  6. Ubuntu终端常用的快捷键(转载)

    本文转自:https://www.cnblogs.com/nucdy/p/5251659.html  侵删 Ubuntu中的许多操作在终端(Terminal)中十分的快捷,记住一些快捷键的操作更得心应 ...

  7. 集合:ListIterator

    why ? when ? how ? what ? Java 集合框架图 有了 Iterator 为什么还要有 ListIterator 呢? Iterator 遍历的时候如果你想修改集合中的元素怎么 ...

  8. How to read and write multiple files in Python?

    Goal: I want to write a program for this: In a folder I have =n= number of files; first read one fil ...

  9. ACM-ICPC国际大学生程序设计竞赛北京赛区(2015)网络赛 Scores

    #1236 : Scores 时间限制:4000ms 单点时限:4000ms 内存限制:256MB 描述 Kyle is a student of Programming Monkey Element ...

  10. MVC系统学习2—MVC路由

    在MVC下不是通过对物理文件的映射来实行访问的,而是通过定义后的路由Url来实现访问的.在前一篇讲到我们是在全局文件下进行路由配置. routes.MapRoute(                & ...