creator shader:从零开始 第二篇,做个动态的光影

从零开始第一篇 画个彩虹

这一篇也要从创建一个新的effect开始,先改一下
先把CCProgram fs{}改成截图的样


在这里插入图片描述

从纯白色的图开始,做下面的效果。

所以这个效果到底叫啥,为啥想不起来在哪见过的。蓝瘦。
在这里插入图片描述

shader的随机函数

先上一个随机函数,用 o.xyz = vec3(hashOld12(uv));,用uv生成一个随机值并填入gl_FragColor.rgb生成看一下,一个无规则的噪声图。用这个函数的功能就是从uv生成随机数 但是这是用数学计算出的伪随机数,效果是对不同uv出来的值是随机的,但是对固定uv随机,每次随机的结果也是固定的。
原理参考:随机数生成

  float hashOld12(vec2 p)
  {
	return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
  }

在这里插入图片描述

上正文

屏幕分格

  float  drawLayer(float scale,vec2 uv)
  {
    // 注释设 scale为10的情况
    // 输出值
    float co = 0.;

    // 拷贝一份uv用,不修改原uv
    vec2 uv1 = uv;

    // uv值范围从 0-1变为 0-scale scale值为10就是0-10范围
    uv1 *= scale;

    // floor向下取整,计算出格子所在下标,
    // 10*10的格子,如果uv是 0.2345,0.2345,uv1就是2.345,2.345
    // 取整后 即为该uv所在格子下表,2,2
    // 0.2 <= uv < 0.3 该范围内所有uv坐标,处理后hv均为 2,2
    vec2 hv = floor(uv1);

    // fr是 fract对数字取小数部分, 0.2345,0.2345 -> uv1 2.345,2.345 -> 0.345,0.345    
    // 如 0.2 <= uv < 0.3 处理后就是一个范围 0-1的范围
    vec2 fr = fract(uv1);

    // 用fr.x+fr.y作为输出看一下结果
    co += fr.x + fr.y;
    return co;
  }

在这里插入图片描述
把处理后的坐标反映到颜色上可以直观的看到处理后的数值,图片已经成功分成了10*10的格子
下面,就可以用 像素点计算出来的 分格后的 下表和在格内的坐标来对每个格子画圆看一下。

每个格子内画圆

    vec2 hv = floor(uv1);
    vec2 fr = fract(uv1);

    // 画圆,用fr(即格内坐标)和 0.5,0.5的点的距离作为颜色值,circle范围0-0.5
    float circle = distance(fr,vec2(.5));
    
    // 上面的结果是距离值计算出来的,有明暗变化,用step把圆内都变成纯白色
    float radius = 0.4; // 半径
    circle = step(radius,circle);
    
    // 1. - circle 翻转色值,使距离圆心越近颜色越亮
    circle = 1. - circle;
    
    co += circle;
    co += drawGird(fr);
    return co;

圆太规律了,要加点变化

随机函数终于用上了。

// 用hv引入随机的半径 因为一格内hv相同,随机值结果也就是半径值相等
    float radius = hashOld12(hv);
    

大小差异太大了。
调一下,


    // radius 0-1 映射到 0.1-0.4的范围
    radius = radius * 0.3 + 0.1;

每个格里都有,太规整了,去掉一半圆吧

// 半径*10取整,对2取余,舍弃一半的圆
    float f1 = mod(floor(radius * 10.),2.);
    radius *= f1;

还是太整齐了

用hv.y做个随机,给该行的uv1.x做个差值吧

vec2 hvtemp = floor(uv1);
    
    float n = hashOld12(vec2(hvtemp.y));
    uv1.x += n;

    // floor向下取整,计算出格子所在下标,
    // 10*10的格子,如果uv是 0.2345,0.2345,uv1就是2.345,2.345
    // 取整后 即为该uv所在格子下表,2,2
    // 0.2 <= uv < 0.3 该范围内所有uv坐标,处理后hv均为 2,2
    vec2 hv = floor(uv1);
    
    // fr是 fract对数字取小数部分, 0.2345,0.2345 -> uv1 2.345,2.345 -> 0.345,0.345    
    // 如 0.2 <= uv < 0.3 处理后就是一个范围 0-1的范围
    vec2 fr = fract(uv1);

差不多了,在多个随机亮度

......
    float radius = hashOld12(hv);
    // 亮度 用这个初始随机值做亮度用
    float strength = radius;
......
circle = 1. - circle;
circle *= strength;
......

从creator传入参数

传入几个颜色看一下

        colorLeft: {  value: [1.,1.,1.,1.],editor: { type: "color"}}
        colorRight: { value: [1.,1.,1.,1.],editor: { type: "color"}}
        color1: { value: [1.,1.,1.,1.],editor: { type: "color"}}
        color2: { value: [1.,1.,1.,1.],editor: { type: "color"}}
        color3: { value: [1.,1.,1.,1.],editor: { type: "color"}}


一定要注意缩进,这个格式的文件格式很严格

加入这两块地方,片元着色器就可以访问这一堆变量了,从材质面板也可以编辑这一堆变量的值了。
在这里插入图片描述

加个背景色 在多家几层颜色

  void main () {
    vec2 uv = vec2(v_uv0.x,v_uv0.y);
    vec3 co = vec3(0.);
    // 加个背景色
    co += mix(colorLeft,colorRight,uv.y).xyz;
    
    vec4 carr[3];
    carr[0] = color1;
    carr[1] = color2;
    carr[2] = color3;
    
    for(int i = 0;i < 3;i++ ){
      float idx = float(i);
      // 用循环下表做一个递增的层半径
      float p1 = idx * 5. + 3.;

      // 给每一层做一个随机运动方向 也就是一个速度向量
      vec2 uvoff = vec2(hashOld12(vec2(p1)),hashOld12(vec2(p1 * 10.0)));
      // 速度*时间 = 偏移距离 让该层随时间运动 可以注释掉 *u_time 就不会运动了
      uvoff = uvoff *u_time * .1;
      
      vec2 p2 = vec2(uv.x,uv.y) + uvoff;

      // p1 半径, p2 供计算的uv值
      float layer = drawLayer(p1,p2);
      
      co += layer * carr[i].xyz;
    }
    
    gl_FragColor = vec4(co,1.);
  }

在这里插入图片描述

每一层都很亮,要做出远暗进亮怎么办?

让层亮度和格子大小成正比比例

// 让层亮度和格子大小成正比比例 (scale是uv的缩放数,越大 格子就越小)
    // * 9 是因为有些暗,变亮点,这个值可以随便调调
    strength *= 1. / scale * 9.;
    circle *= strength;

在这里插入图片描述

现在,可以运行起来看一看了,

动态给shader传入参数 u_time

sp.getMaterial(0).effect.setProperty('u_time',this._utime); this._utime自己用update累加一下哈 我的是每帧 += 0.01;

用js脚本实时修改shader的u_time的值,这时画面上的圆圈应该都能动起来了。

圆圈优化

    //circle = step(radius,circle); 这一行注释掉,用下一行。这时模糊圆圈边缘的函数,0.02*scale就是模糊的宽度,这个系数也可以自己调整到喜欢的数值,
    // 这个系数和strength乘的系数调整个不同的值,组合起来效果也大不一样。
circle = smoothstep(radius - .02 * scale,radius,circle);

在这里插入图片描述

我的审美也就止步于此了。。

44赞

厉害厉害,学习了

厉害厉害,学习了

太牛了!!

膜拜大佬~

炸出了楼上几位大佬,大佬们客气了,我就是个兴趣爱好者 小萌新

为何如此优秀

讲过程才是最为受益的,厉害厉害!

1赞

刚看到大佬的彩虹,这马上又来了!顶

大佬牛逼,立刻学习!

膜拜大佬~

大佬牛批~

膜拜大佬~大佬NB ~!

大佬牛逼,这个应该叫光点模糊失焦特效

1赞

7h4hp-9yk4d
现学现用,

1赞

大佬牛逼。

big old six six six

下雨下雪都可以参考这个方案来做。

对的,之前一直想实现下雨的效果,但没思路,看了大佬的这文章真是受益匪浅啊

image
https://www.shadertoy.com/results?query=rain
搜一下,很多的

1赞