日积月累Shader - 09 分形图 / Patterns图案

提示

教程例子都可以到下面网址进行运行,不需要另外安装软件环境:
官方提供在线编写shader工具:https://thebookofshaders.com/edit.php
glslsandbox网站:http://glslsandbox.com/
shadertoy网站:https://www.shadertoy.com/

第一个图案

首先让我们记住 fract() 函数。它返回一个数的分数部分,本质上是除1的余数(mod(x,1.0))。换句话说, fract() 返回小数点后的数。 我们单位化的坐标系变量 (st) 已经是 0.0 到 1.0 之间的了。所以像下面这么做并没有必要:

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution;
    vec3 color = vec3(0.0);
    st = fract(st);
    color = vec3(st,0.0);
    gl_FragColor = vec4(color,1.0);
}

我们将这个坐标系缩放就能产生最原始的分形效果

// Author @patriciogv - 2015

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

float circle(in vec2 _st, in float _radius){
    vec2 l = _st-vec2(0.5);
    return 1.-smoothstep(_radius-(_radius*0.01),
                         _radius+(_radius*0.01),
                         dot(l,l)*4.0);
}

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution;
    vec3 color = vec3(0.0);

    st *= 10.0;      // Scale up the space by 3
    st = fract(st); // Wrap arround 1.0

    // Now we have 3 spaces that goes from 0-1

    color = vec3(st,0.0);
    //color = vec3(circle(st,0.5));

    gl_FragColor = vec4(color,1.0);
}
  • 将色彩改为图形


在图案内部应用矩阵

鉴于每个细分或者说单元都是我们正在使用的单位化坐标系的小单元,我们可以对每个内部空间施以矩阵变换来平移,旋转和缩放。

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

#define PI 3.14159265358979323846

vec2 rotate2D(vec2 _st, float _angle){
    _st -= 0.5;
    _st =  mat2(cos(_angle),-sin(_angle),
                sin(_angle),cos(_angle)) * _st;
    _st += 0.5;
    return _st;
}

vec2 tile(vec2 _st, float _zoom){
    _st *= _zoom;
    return fract(_st);
}

float box(vec2 _st, vec2 _size, float _smoothEdges){
    _size = vec2(0.5)-_size*0.5;
    vec2 aa = vec2(_smoothEdges*0.5);
    vec2 uv = smoothstep(_size,_size+aa,_st);
    uv *= smoothstep(_size,_size+aa,vec2(1.0)-_st);
    return uv.x*uv.y;
}

void main(void){
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);

    // Divide the space in 4
    st = tile(st,4.);

    // Use a matrix to rotate the space 45 degrees
    st = rotate2D(st,PI*0.138);

    // Draw a square
    color = vec3(box(st,vec2(0.7),0.01));
    // color = vec3(st,0.0);

    gl_FragColor = vec4(color,1.0);
}
  • 截图并没有歪,想歪的是你的大脑,


偏移图案

假如我们想要模仿砖墙。看,下面的墙,你是不是看到一半的砖在x方向上偏移了一半砖的长度,没隔一行偏移一次。我们如何实现?
第一步我们需要知道某行的线程是奇数还是偶数,以为我们可以通过奇偶来决定是否要在x方向上偏移那一行。

我们需要两段来解决这个问题
要判断我们的线程是一个奇数行或者偶数行,我们要用 2.0mod() 。 然后根据结果是否大于 1.0 来判断。看一下下面的函数,取消最后两行的注释。

  • 先取模,这里0.5是为了方便截图:y = mod(x,2.0).5;
  • 然后对值进行判断 y = mod(x,2.0) < 1.0 ? 0. : 1. ;


  • 我们刚好有一个函数来处理这个条件:y = step(1.,mod(x,2.));


因为虽然要知道每个显卡如何优化和编译代码并不容易,但是可以安全地假设内置函数总比非内置的函数快。任何时候你都以调用内置函数,干嘛不用呢!

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

vec2 brickTile(vec2 _st, float _zoom){
    _st *= _zoom;

    // Here is where the offset is happening
    _st.x += step(1., mod(_st.y,2.0)) * 0.5;

    return fract(_st);
}

float box(vec2 _st, vec2 _size){
    _size = vec2(0.5)-_size*0.5;
    vec2 uv = smoothstep(_size,_size+vec2(1e-4),_st);
    uv *= smoothstep(_size,_size+vec2(1e-4),vec2(1.0)-_st);
    return uv.x*uv.y;
}

void main(void){
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);

    // Modern metric brick of 215mm x 102.5mm x 65mm
    // http://www.jaharrison.me.uk/Brickwork/Sizes.html
    // st /= vec2(2.15,0.65)/1.5;

    // Apply the brick tiling
    st = brickTile(st,5.0);

    color = vec3(box(st,vec2(0.9)));

    // Uncomment to see the space coordinates
    // color = vec3(st,0.0);

    gl_FragColor = vec4(color,1.0);
}
  • 试着根据时间变化对偏移量做动画
  • 另做一个动画,让偶数行向左移,奇数行向右移动。
  • 能不能根据列重复这样的效果?
  • 试着结合 x 和 y 轴的偏移来得到下面这样的效果:


https://thebookofshaders.com/edit.php#09/marching_dots.frag

Truchet 瓷砖

考虑到 Truchet Tiles 的例子,即一个单一设计元素可以以四种不同的方式呈现:

// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015

#ifdef GL_ES
precision mediump float;
#endif

#define PI 3.14159265358979323846

uniform vec2 u_resolution;
uniform float u_time;

vec2 rotate2D (vec2 _st, float _angle) {
    _st -= 0.5;
    _st =  mat2(cos(_angle),-sin(_angle),
                sin(_angle),cos(_angle)) * _st;
    _st += 0.5;
    return _st;
}

vec2 tile (vec2 _st, float _zoom) {
    _st *= _zoom;
    return fract(_st);
}

vec2 rotateTilePattern(vec2 _st){

    //  Scale the coordinate system by 2x2
    _st *= 2.0;

    //  Give each cell an index number
    //  according to its position
    float index = 0.0;
    index += step(1., mod(_st.x,2.0));
    index += step(1., mod(_st.y,2.0))*2.0;

    //      |
    //  2   |   3
    //      |
    //--------------
    //      |
    //  0   |   1
    //      |

    // Make each cell between 0.0 - 1.0
    _st = fract(_st);

    // Rotate each cell according to the index
    if(index == 1.0){
        //  Rotate cell 1 by 90 degrees
        _st = rotate2D(_st,PI*0.5);
    } else if(index == 2.0){
        //  Rotate cell 2 by -90 degrees
        _st = rotate2D(_st,PI*-0.5);
    } else if(index == 3.0){
        //  Rotate cell 3 by 180 degrees
        _st = rotate2D(_st,PI);
    }

    return _st;
}

void main (void) {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;

    st = tile(st,3.);
    st = rotateTilePattern(st);

    // Make more interesting combinations
    // st = tile(st,2.0);
    // st = rotate2D(st,-PI*u_time*0.25);
    // st = rotateTilePattern(st*2.);
    // st = rotate2D(st,PI*u_time*0.25);

    // step(st.x,st.y) just makes a b&w triangles
    // but you can use whatever design you want.
    gl_FragColor = vec4(vec3(step(st.x,st.y)),1.0);
}
  • tile() 函数用于分割区域,这里将图形分割成了3x3区域

  • rotateTilePattern() 将每个小块区域分成四个区域,在此函数中,st的范围是从0~1放大到了0~2,再通过step将其分割成(0,0),(1,0),(0,1),(1,1)四份 (ps:gpu并行执行,这些象限并没有先后之分),再最后进行一次旋转,就得到了大风车分形图案


  • 制作八卦图


制定自己的规则

》制作程序图案是种寻找最小可重复元素的古老练习(灵修)。我们作为长时间使用网格和图案来装饰织物、地面和物品的镶边物种:从古希腊的弯曲图案,到中国的窗栅设计,重复和变化的愉悦吸引我们的想象。花些时间浏览 decorative patterns 并看看在漫长的历史里,艺术家和设计师是如何寻找在秩序的预测性和(由)混沌和衍变(产生)的惊奇之间的边界的。从阿拉伯几何图案,到斑斓的非洲编制艺术,这里有一整个图案的宇宙要学习。

你可能感兴趣的:(日积月累Shader - 09 分形图 / Patterns图案)