zoukankan      html  css  js  c++  java
  • 【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

    Chapter 11 Gameplay and Screen Effects

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

  • 相关阅读:
    struts.xml文件中package元素的各大属性讲解
    strus2 struts.xml详解
    既使用maven编译,又使用lib下的Jar包
    Maven项目同时使用lib下的Jar包
    PreparedStatement ResultSet
    SearchBySql
    java 生成GUID
    获取当前时间的字符串
    C#字符串比较
    浮点数比较大小
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/7105090.html
Copyright © 2011-2022 走看看