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;

引用

相关推荐
abc800211703434 分钟前
前端Bug 修复手册
前端·bug
Best_Liu~37 分钟前
el-table实现固定列,及解决固定列导致部分滚动条无法拖动的问题
前端·javascript·vue.js
_斯洛伐克2 小时前
下降npm版本
前端·vue.js
苏十八3 小时前
前端进阶:Vue.js
前端·javascript·vue.js·前端框架·npm·node.js·ecmascript
st紫月3 小时前
用MySQL+node+vue做一个学生信息管理系统(四):制作增加、删除、修改的组件和对应的路由
前端·vue.js·mysql
乐容4 小时前
vue3使用pinia中的actions,需要调用接口的话
前端·javascript·vue.js
似水明俊德4 小时前
ASP.NET Core Blazor 5:Blazor表单和数据
java·前端·javascript·html·asp.net
至天5 小时前
UniApp 中 Web/H5 正确使用反向代理解决跨域问题
前端·uni-app·vue3·vue2·vite·反向代理
与墨学长5 小时前
Rust破界:前端革新与Vite重构的深度透视(中)
开发语言·前端·rust·前端框架·wasm
H-J-L6 小时前
Web基础与HTTP协议
前端·http·php