【OpenGL】Shader实例分析(八)- 彩色光圈

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

研究了一个彩色光圈效果,感觉挺不错的,分享给大家,效果如下:

【OpenGL】Shader实例分析(八)- 彩色光圈_第1张图片

代码如下:

Shader "shadertoy/TotalNoob" {  //https://www.shadertoy.com/view/XdlSDs
	Properties{
		iMouse ("Mouse Pos", Vector) = (100,100,0,0)
		iChannel0("iChannel0", 2D) = "white" {}  
		iChannelResolution0 ("iChannelResolution0", Vector) = (100,100,0,0)
	}
	  
	CGINCLUDE    
	 	#include "UnityCG.cginc"   
  		#pragma target 3.0      
  		#pragma glsl

  		#define vec2 float2
  		#define vec3 float3
  		#define vec4 float4
  		#define mat2 float2x2
  		#define iGlobalTime _Time.y
//  		#define mod fmod  // mod = sign*fmod
  		#define mix lerp
  		#define atan atan2
  		#define fract frac 
  		#define texture2D tex2D
  		// 屏幕的尺寸
  		#define iResolution _ScreenParams
  		// 屏幕中的坐标,以pixel为单位
  		#define gl_FragCoord ((_iParam.srcPos.xy/_iParam.srcPos.w)*_ScreenParams.xy) 
  		
  		#define PI2 6.28318530718
  		#define pi 3.14159265358979
  		#define halfpi (pi * 0.5)
  		#define oneoverpi (1.0 / pi)
  		
  		fixed4 iMouse;
  		sampler2D iChannel0;
  		fixed4 iChannelResolution0;
  		
        struct v2f {    
            float4 pos : SV_POSITION;    
            float4 srcPos : TEXCOORD0;   
        };              
        
       //   precision highp float;
        v2f vert(appdata_base v){  
        	v2f o;
        	o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            o.srcPos = ComputeScreenPos(o.pos);  
            return o;    
        }  
        
        vec4 main(v2f _iParam);
        
        fixed4 frag(v2f _iParam) : COLOR0 {  
			return main(_iParam);
        }  
        
		vec4 main(v2f _iParam) {
		   	vec2 p = (2.0*gl_FragCoord.xy-iResolution.xy)/iResolution.y;
		    float tau = 3.1415926535*2.0;
		    float a = atan(p.x,p.y);
		    float r = length(p)*0.75;
		    vec2 uv = vec2(a/tau,r);
			
			//get the color
			float xCol = (uv.x - (iGlobalTime / 3.0)) * 3.0;
			xCol = sign(xCol)*fmod(xCol, 3.0);
			vec3 horColour = vec3(0.25, 0.25, 0.25);
			
			if (xCol < 1.0) {
				horColour.r += 1.0 - xCol;
				horColour.g += xCol;
			} else if (xCol < 2.0) {
				xCol -= 1.0;
				horColour.g += 1.0 - xCol;
				horColour.b += xCol;
			} else {
				xCol -= 2.0;
				horColour.b += 1.0 - xCol;
				horColour.r += xCol;
			}

			// draw color beam
			uv = (2.0 * uv) - 1.0;
			float beamWidth = (0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iGlobalTime)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y));
			vec3 horBeam = vec3(beamWidth,beamWidth,beamWidth);
			vec4 gl_FragColor = vec4((( horBeam)* horColour ), 1.0);
			
			return gl_FragColor;
		}

    ENDCG    
    SubShader {    
        Pass {    
            CGPROGRAM    
            #pragma vertex vert    
            #pragma fragment frag    
            #pragma fragmentoption ARB_precision_hint_fastest     
            ENDCG    
        }    
    }     
    FallBack Off    
}

代码分析

代码分两部分,颜色 * 光圈,如下图:

 *  = 

彩色的算法

代码如下:

vec2 p = (2.0*gl_FragCoord.xy-iResolution.xy)/iResolution.y;
float tau = 3.1415926535*2.0;
float a = atan(p.x,p.y);
float r = length(p)*0.75;
vec2 uv = vec2(a/tau,r);

//get the color
float xCol = (uv.x - (iGlobalTime / 3.0)) * 3.0;
xCol = mod(xCol, 3.0);
vec3 horColour = vec3(0.25, 0.25, 0.25);

if (xCol < 1.0) {
	horColour.r += 1.0 - xCol;
	horColour.g += xCol;
} else if (xCol < 2.0) {
	xCol -= 1.0;
	horColour.g += 1.0 - xCol;
	horColour.b += xCol;
} else {
	xCol -= 2.0;
	horColour.b += 1.0 - xCol;
	horColour.r += xCol;
}

这段代码是写在fragment shader中的,也就是说,每个像素点的渲染都会调用这段代码。

a) vec2 p = (2.0*gl_FragCoord.xy-iResolution.xy)/iResolution.y;

p表示把当前的坐标轴缩小到原来的1/2,原点移动到屏幕中间,并把x,y轴的坐标范围缩小到1左右的值(即p的y轴范围在-1到1之间,x轴的范围也在附近);

b)float a = atan(p.x, p.y);

a表示p点绕原点的角度,范围为[-π,π];所以uv.x = a/tau的范围为[-1/2, 1/2];

float xCol = (uv.x - (iGlobalTime / 3.0)) * 3.0; xCol=mod(xCol, 3)的范围为 [0,3]

c) xCol经过上面处理,其范围为[0,3]; 现在把这个范围平均分成3份,每一份做一个颜色的混合:

[0,1]:Red和Green混合;[1,2]:Green和Blue混合;[2,3]:Blue和Red混合。


光圈的算法

a)画光圈

式子:abs(1.0 / (30.0*uv.y)) 

知识:在shader中,如果color的值为负数,则认为是0,不显示该颜色。

uv变量中uv.y表示点到原点的距离,值的范围为 [0, ]

a-1) uv = (2.0 * uv) - 1.0;  先把uv缩小到原来的1/2,然后向外移动1单位。uv.y的值为[-1/2, ];由于负值color不被显示,如下图A:

a-2) 1.0/(30.0* uv.y); 缩小到原来的1/30,并做个倒数,如下图B

a-3) abs(1.0/(30.0* uv.y)); 然后做个绝对值,如下图C

=》=》

画光圈的算法和《【OpenGL】Shader实例分析(一)-Wave》中画线的算法很类似。

b)光圈动画 

式子:(0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iGlobalTime)), 0.0, 10.0)))

为了方便,把上面的式子分解如下:

式1:float tt = 5.0 + 10.0*cos(iGlobalTime); 
式2:float param = clamp(floor(tt), 0.0, 10.0);

式3:float beamWidth = (0.7+0.5*cos(uv.x*pi*param));

我们把beamWidth作为颜色输出;

先理解式3,如果当param为0,、1、2、3、10时,分别参考下图: 

 =》  =》=》 =》

式2的作用,把tt的值做一个包装,使其为0到10之间的整数

式1的作用,起周期作用,值域为[-5,15]; 其值如左下图所示; 又由于式2做了clamp,把大于10和小于0的值去掉,最终的动画如右下图所示:

【OpenGL】Shader实例分析(八)- 彩色光圈_第2张图片 ====》

把光圈和颜色整合起来就看到了和文章开头的动画一样的效果了。

最后吧所有的效果整合起来,如下图:

【彩色】 => 【彩色旋转】 =》【彩色旋转+动画】 =》【彩色旋转+动画+光圈】

=》=》=》【OpenGL】Shader实例分析(八)- 彩色光圈_第3张图片


本次分析到此结束,欢迎讨论。

你可能感兴趣的:(Unity3D)