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;

引用

相关推荐
学不会•1 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
活宝小娜4 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow4 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o4 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā5 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年6 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder6 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727576 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架