2D Tiling3_Hexagonal 六边形平面镶嵌

以下的图案都是基于Hexagonal Tiling创作出的一些艺术图形,怎么样,非常动人吧,只用了大概不到200行的代码。

www.shadertoy.com/view/WtSfWK

www.shadertoy.com/view/4td3zj

六边形SDF

两个向量的 <math xmlns="http://www.w3.org/1998/Math/MathML"> d o t dot </math>dot用来表示一个向量在另外一个向量上的投影大小,同时点积的正负号可以表示两个向量的方向。

glsl 复制代码
    float d = dot(uv, vec2(1.));
    if (d < 0.25 + sin(iTime*3.0) * 0.25) {
        col.bg = vec2(0.8);
    } else {
        col = vec3(0.0);
    }

下图中的白线表示vec2(1.0)向量的方向,也是 y = x 的函数图像, 可以看出颜色块都是垂直与白线的。

这个时候我们在加上 abs 就可以获得一个正方形了。

同样的原理如果我们花一个六边形的斜线,只需要找到下图的 <math xmlns="http://www.w3.org/1998/Math/MathML"> u ⃗ \vec{u} </math>u 向量即可。 从图中不难得出向量 <math xmlns="http://www.w3.org/1998/Math/MathML"> u ⃗ = ( c o s ( 60 ° ) , s i n ( 60 ° ) ) \vec{u} = (cos(60\degree), sin(60\degree)) </math>u =(cos(60°),sin(60°)) 修改点积函数

glsl 复制代码
    float d = dot(uv, vec2(cos(0.3 * PI), sin(0.3 * PI)));

可以得到菱形 要的到六边形就非常简单点,只需要在x轴垂直方向切一刀,

ini 复制代码
        d = max(d, uv.x);

这里面的 d 到底是什么呢? 其实是沿着六边形边的垂直方向距离 零点的长度,所以其实我们略微修改,就可以变成一个距离边的距离场函数

glsl 复制代码
float SdfHexgon(vec2 p, float r) {
    p = abs(p);
    float d = dot(p, vec2(cos(0.3 * PI), sin(0.3 * PI))); 
    d = max(d, p.x);
    return d - r; 
}

col += step(SdfHexgon(uv, 0.24), 0.0); 

于是有

六边形镶嵌

上一节,理解如何画出一个六边形,并推倒了SDF, 但是为了做镶嵌,我们需要将六边形铺满整个屏幕,这也是为什么不直接用sdf的方式, 这里我们将采用 坐标系偏移的思路 画出六边形的镶嵌,里面最核心的 两个坐标系分界方法 就是 上节的画菱形的方法。

为了能够让六边形铺满整个平面,需要以下图的方式镶嵌

在前面很多文章中有提过制作grid的方法,

ini 复制代码
    vec2 gv = fract(uv) - .5;
    vec2 gv2 = fract(uv - .5) - .5;

假设我们在grid基础上,在做一个offset(.5)的grid 会产生如下的两个grid.

这个时候我们对两个坐标做一个比较判断

scss 复制代码
    if (length(gv) < length(gv2)) {
        col += vec3(.5);
    }

就得到了正方形

不过六边形有一个特点,就是他的高度和宽度是不一样的,高度与宽度比为 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 / 3 2/\sqrt{3} </math>2/3 . 也就是说如果我们吧六边形放到正方形中会导致高溢出或者宽度不够, 如下图所示

而为了解决溢出的问题,我们的grid不能是一个正方形了,grid应该是一个被拉长的长方形 就像下图

grid出不同长高非常简单,就是将fract 函数改为本来的 mod函数

ini 复制代码
    vec2 r = vec2(1., sqrt(3.));
    vec2 h = r * .5;
    vec2 a = mod(uv, r) - h;
    vec2 b=  mod(uv - h, r) - h; 

于是有余下的两个偏移grid

将分割与颜色补充就可以看到六边形镶嵌了

glsl 复制代码
    vec2 gv = length(a) < length(b) ? a : b;
    col.rg = gv;

属性

ID

一样的,对于这些grid类型, 我们都需要获得tile相关的坐标,以便后续的艺术创作。坐标非常简单,在 fract做的正方形grid里面,我们通过 floor获得坐标, 在这里其实一样

ini 复制代码
vec2 id = uv - gv;


// 这里抽象了一个HexCoords的函数,返回坐标与id
col.rb =  (HexCoords(uv).zw + vec2(4.0)) / vec2(8.0);

可以得到图案

Distance

目前返回都是坐标,如果我们能够返回距离六边形边缘的distance, 那么会有更多好的艺术效果可以实现。 这里就用到了我们第一节讲到的方法。

ini 复制代码
    gv.y = SdfHexgon(gv, 0.5);

    col.x =  abs(HexCoords(uv).y);

角度

ini 复制代码
    gv.x = atan(gv.x, gv.y);
    col.x =  HexCoords(uv).x/ PI;

引用

相关推荐
GIS程序媛—椰子15 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00121 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端24 分钟前
Content Security Policy (CSP)
前端·javascript·面试
木舟100928 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤439138 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安1 小时前
前端第二次作业
前端·css·css3
啦啦右一1 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习
半开半落1 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt