【OpenGL】Shader实例分析(七)- 雪花飘落效果
来源:程序员人生 发布时间:2014-11-17 08:20:19 阅读次数:5117次
转发请保持地址:http://blog.csdn.net/stalendp/article/details/40624603
研究了1个雪花飘落效果,感觉挺不错的,分享给大家,效果以下:
代码以下:
Shader "shadertoy/Flakes" { // https://www.shadertoy.com/view/4d2Xzc
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
#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 = gl_FragCoord.xy/iResolution.xy;
vec3 col = vec3(0,0,0);
float dd = 150;
for( int i=0; i<dd; i++ )
{
float an = 6.2831*float(i)/dd;
vec2 of = vec2( cos(an), sin(an) ) * (1.0+0.6*cos(7.0*an+iGlobalTime)) + vec2( 0.0, iGlobalTime );
col = max( col, texture2D( iChannel0, p + 20*of/iResolution.xy ).xyz );
col = max( col, texture2D( iChannel0, p + 5.0*of/iResolution.xy ).xyz );
}
col = pow( col, vec3(1.0,2.0,3.0) ) * pow( 4.0*p.y*(1.0-p.y), 0.2);
return vec4( col, 1.0 );
}
ENDCG
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
FallBack Off
}
代码分析:
1)7边形雪花的绘制算法
具体代码以下:
float dd = 150;
for( int i=0; i<dd; i++ )
{
float an = 6.2831*float(i)/dd;
vec2 of = vec2( cos(an), sin(an) ) * (1.0+0.6*cos(7.0*an+iGlobalTime)) + vec2( 0.0, iGlobalTime );
col = max( col, texture2D( iChannel0, p + 20*of/iResolution.xy ).xyz );
col = max( col, texture2D( iChannel0, p + 5.0*of/iResolution.xy ).xyz );
}
在理解这段代码前,先理解怎样画1个圈,代码以下:
float dd = 30;
for( int i=0; i<dd; i++ )
{
float an = 6.2831*float(i)/dd;
vec2 of = vec2( cos(an), sin(an) );
col = max( col, texture2D( iChannel0, p + 20*of/iResolution.xy ).xyz );
}
然后再准备1张贴图,图片中间是1个白色像素,周围都是黑色
效果以下:
这段代码处于fragment shader中,意味着屏幕上每一个点都会进行上述的算法。具体以下,遍历贴图中该点周围的点(上面的代码中为距离该点为20单位的圆上的点),把周围点中最亮的作为该点的色彩。 上面的贴图有点特殊,只有1个点是白色,其余点都是黑色的。那末只有距离该点正好为20单位的点才会变成亮色,其余的点都是黑色,如上图的结果。1句话总结上面算法的效果:贴图中的每个“相对亮点”的周围都会产生特定的图形,图形的亮度取决于该点的亮度。效果可以参考文末的图片。
接下来理解这段代码:
float dd = 150;
for( int i=0; i<dd; i++ )
{
float an = 6.2831*float(i)/dd;
vec2 of = vec2( cos(an), sin(an) ) * (1.0+0.7*cos(7.0*an));
col = max( col, texture2D( iChannel0, p + 20*of/iResolution.xy ).xyz );
// col = max( col, texture2D( iChannel0, p + 5.0*of/iResolution.xy ).xyz );
}
输出结果以下:
a) 1.0+0.7*cos(7.0*an)的图象以下:
b)算法中 of 向量的路径为:
结果就很清晰了;其实这里算法和《【OpenGL】Shader实例分析(2)- Heart》中绘制心形的算法很类似。
最后加上时间就能够实现动画了:
vec2 of = vec2( cos(an), sin(an) ) * (1.0+0.6*cos(7.0*an+iGlobalTime)) + vec2( 0.0, iGlobalTime );
第1个iGlobalTime,用来控制雪花的旋转,第2个iGlobalTime使雪花着落。
2)后期色彩等处理
这里可以理解为1种postEffect处理,具体是以下的代码贡献的效果:
col = pow( col, vec3(1.0,2.0,3.0) ) * pow( 4.0*p.y*(1.0-p.y), 0.2);
a) pow(col, vec3(1.0, 2.0, 3.0)) 这句话使得色彩变成暖色调。col值的范围为[0,1],对小数继续pow运算,次数越高,该值越小。比如:0.5的1次方是0.5, 2次方为0.25, 3次方为0.125等;所以这句话的作用很明显:red成分不变,green变小1些,blue变的更小。到达的效果,使得整体色彩会偏向暖色调。
b)pow(4.0*p.y*(1.0-p.y), 0.2) 使得屏幕上下两边变暗。
最后附上shader中用到的贴图:
经进程序处理后,得到以下:
文章终了,欢迎讨论。
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠