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;

引用

相关推荐
腾讯TNTWeb前端团队6 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰9 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪9 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy10 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom11 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom11 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom11 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom11 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom11 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试