首页 > 代码库 > 【OpenGL】Shader实例分析(九)- AngryBots中的主角受伤特效
【OpenGL】Shader实例分析(九)- AngryBots中的主角受伤特效
转发请保持地址:http://blog.csdn.net/stalendp/article/details/40859441
AngryBots是Unity官方的一个非常棒的例子,很有研究价值。以前研究的时候,由于其内容丰富,一时间不知道从哪入手写文章分析。这一段时间研究shader技术比较多一些,就从shader的这一方面开始吧。首先分析其中的一个屏幕特效:当主角受到攻击时会出现的全屏效果(postScreenEffect),效果如下:
其实这是一种的Bloom效果,相关文件有:MobileBloom.js 和 MobileBloom.shader;关于如何查看这两个文件,请参考下图:
相关代码如下:
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渲染已经完成,用来处理3d渲染的结果。本文所描述的效果就是在这个函数中实现的。这个函数所在的脚本一般绑定在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;
unity内部对RenderTextures做了池化操作,所以GetTemporary函数会很快地返回(很有可能就是获取了已有的RenderTexture);当处理完之后,请使用ReleaseTemporary来释放对此RenderTexture的引用,以便复用RenderTexture,提高性能。
了解了三个知识点,上面的代码的功能也就非常清晰了:
- a)获取两个渲染贴图tempRtLowA和tempRtLowB(长宽都是原图的1/4,用以加快渲染速度)
- b)设置Mat中Shader的参数
- c)通过Mat来处理贴图,最终渲染到destination贴图中,用来显示
- d)释放临时的贴图。
这里先解释a和c;
【步骤a】,把贴图缩小的原来的1/16(长宽都缩小为原来的1/4),是用来节约GPU内存,同时提高渲染速度;可以这样做的原因是,我们接下来对图片的处理都是要进行模糊的,对图像的要求不是很高。
【步骤c】(注:调用Blit函数来过滤贴图,其中最后一个数字参数是用来指代shader的pass的)
pass1 或者 pass5, 提取颜色中最亮的部分;pass2 对高亮图片进行横向模糊;pass3 对高亮图片进行纵向模糊;pass0或pass4;把模糊的图片叠加到原图片上。
一个两点,经过很想模糊,然后经过纵向模糊的过程,如下图所示(也就是把一个点向周围扩散的算法):
接下来具体解释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
Chapter 11 Gameplay and Screen Effects
[GPU Gems] Real-Time Glow:http://http.developer.nvidia.com/GPUGems/gpugems_ch21.html
【OpenGL】Shader实例分析(九)- AngryBots中的主角受伤特效