【OpenGL】Shader实例分析(九)- AngryBots中的主角受伤特效
转发请保持地址: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
[GPU Gems] Real-Time Glow:http://http.developer.nvidia.com/GPUGems/gpugems_ch21.html
【OpenGL】Shader实例分析(九)- AngryBots中的主角受伤特效的更多相关文章
- 【OpenGL】Shader实例分析(七)- 雪花飘落效果
转发请保持地址:http://blog.csdn.net/stalendp/article/details/40624603 研究了一个雪花飘落效果.感觉挺不错的.分享给大家,效果例如以下: 代码例如 ...
- 【OpenGL】Shader实例分析(六)- 卡牌特效
转发请保持地址:http://blog.csdn.net/stalendp/article/details/30989295 本文将介绍怎么通过alpha通道来隐藏信息.并实现卡牌特效. 执行效果例如 ...
- 实例分析Vue.js中 computed和methods不同机制
在vue.js中,有methods和computed两种方式来动态当作方法来用的 1.首先最明显的不同 就是调用的时候,methods要加上() 2.我们可以使用 methods 来替代 comput ...
- php中return的用法实例分析
本文实例讲述了php中return的用法.分享给大家供大家参考.具体分析如下: 首先,它的意思就是返回;return()是语言结构而不是函数,仅在参数包含表达式时才需要用括号将其括起来.当返回一个变量 ...
- C++中重载、重写(覆盖)和隐藏的区别实例分析
这篇文章主要介绍了C++中重载.重写(覆盖)和隐藏的区别,是C++面向对象程序设计非常重要的概念,需要的朋友可以参考下 本文实例讲述了C++中重载.重写(覆盖)和隐藏的区别,对于C++面向对象程序设计 ...
- 【玩转cocos2d-x之四十】怎样在Cocos2d-x 3.0中使用opengl shader?
有小伙伴提出了这个问题.事实上GLProgramCocos2d-x引擎自带了.全然能够直接拿来用. 先上图吧. 使用opengl前后的对照: watermark/2/text/aHR0cDovL2Js ...
- 实例分析ASP.NET在MVC5中使用MiniProfiler监控MVC性能的方法
这篇文章主要为大家详细介绍了ASP.NET MVC5使用MiniProfiler监控MVC性能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 MiniProfiler ,一个简单而有效的迷你剖析器 ...
- C#保留2位小数几种场景总结 游标遍历所有数据库循环执行修改数据库的sql命令 原生js轮盘抽奖实例分析(幸运大转盘抽奖) javascript中的typeof和类型判断
C#保留2位小数几种场景总结 场景1: C#保留2位小数,.ToString("f2")确实可以,但是如果这个数字本来就小数点后面三位比如1.253,那么转化之后就会变成1.2 ...
- Python中的单继承与多继承实例分析
Python中的单继承与多继承实例分析 本文实例讲述了Python中的单继承与多继承.分享给大家供大家参考,具体如下: 单继承 一.介绍 Python 同样支持类的继承,如果一种语言不支持继承,类就没 ...
随机推荐
- 【转】非常好的Java反射例子
转自 http://www.douban.com/note/306848299/ 原文: 1.Java反射的概念 反射含义:可以获取正在运行的Java对象. 2.Java反射的功能 1)可以判断运行时 ...
- SpringBoot+Mybatis 自动创建数据表(适用mysql)
Mybatis用了快两年了,在我手上的发展史大概是这样的 第一个阶段 利用Mybatis-Generator自动生成实体类.DAO接口和Mapping映射文件.那时候觉得这个特别好用,大概的过程是这样 ...
- arx代码片段
ObjectARX代码片段二 转载自网络 一 在ARX中禁用AutoCAD的某个命令 以LINE命令为例,在程序中加入下面的一句即可禁用LINE命令: acedCommand(RTSTR, &q ...
- 前端请求操作类型(get post put delete)
get:获取数据 post:增加 put:修改 delete:删除
- Nginx 通过 certbot 为网站自动配置 SSL 证书并续期
一.背景知识 1.1.http 和 https 是什么? 简单来说,http 是一个传输网页内容的协议,比如你看到的 http 开头的网站 http://www.163.com ,其网页上的文字.图片 ...
- 02C++基本语法
基本语法 2.1.1单行注释 // 2.1.2多行注释 /* * */ 2.1.3标识符 C++ 标识符是用来标识变量.函数.类.模块,或任何其他用户自定义项目的名称.一个标识符以字母 A-Z 或 a ...
- day02 Python完结
一. 常用数据类型及内置法 1 列表 定义: 列表是Python中内置有序.可变序列,列表的所有元素放在一对中括号“[]”中,并使用逗号分隔开: 当列表元素增加或删除时,列表对象自动进行扩展或收缩内存 ...
- 洛谷——P1471 方差
P1471 方差 题目描述 蒟蒻HansBug在一本数学书里面发现了一个神奇的数列,包含N个实数.他想算算这个数列的平均数和方差. 借一下远航之曲大佬的图片,特别清晰: 那么只要维护区间平方和,就可以 ...
- Luogu P2822 组合数问题
思路 组合数的话,首先肯定是想到杨辉三角啊.不傻的都知道要预处理一张组合数表,但是你以为这样就可以了吗???显然,不可能的.那询问的时候复杂度就成了$\large{O(t*n)}$,凉凉.那咋办,用二 ...
- Python学习之前
编程语言的分类: 1.机器语言:直接以0和1编写指令代码,计算机能直接识别处理: 特点:运行速度最快,太复杂,开发效率低,可执行操作最多. 2.汇编语言:本质上依然是机器语言,用英文代替0和1,更容易 ...