shader-绘图-图案

156 阅读6分钟

如何根据一定的策越生成图案。

1.网格策略

使用先放大网格再使用fract的技巧生成网格。

#ifdef GL_ES
precision mediump float;  // 为GL_ES环境设定中等精度浮点数
#endif

uniform vec2 u_resolution;  // 全局变量,用于存储屏幕的分辨率
uniform float u_time;  // 全局变量,存储从程序开始运行到现在的时间

// 定义一个圆形函数,输入为一个二维向量(屏幕的一个点)和一个半径,输出一个浮点数,表示该点是否在半径为_radius的圆内
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);  // 计算该点到原点的距离是否在半径范围内,是的话返回1,否则返回0
}

void main() {
	vec2 st = gl_FragCoord.xy/u_resolution;  // 将片元的坐标转换为以屏幕左下角为原点的坐标,范围是[0,1]
    vec3 color = vec3(0.0);  // 初始化颜色为黑色

    st *= 3.0;      // 将屏幕的坐标范围放大3倍,变为[0,3]
    st = fract(st); // 计算坐标的小数部分,从而将坐标范围变为[0,1],但每1/3部分为一个循环

    // 现在我们有9个范围从0-1的空间

    color = vec3(st,0.0);  // 计算颜色,颜色的红绿分量由坐标决定,蓝色分量为0
    // color = vec3(circle(st,0.5));  // 或者你也可以将颜色设定为圆形函数的输出,圆形的半径为0.5

	gl_FragColor = vec4(color,1.0);  // 将计算得到的颜色赋给片元,颜色的alpha通道设定为1
}

结果为。

image.png(图1)

若是采用circle结果为。

image.png(图2)

当然如果修改第20行的参数 st *= vec2(4,4);

image.png(图3)

2.图案变换

有了前面的基础,在上面图1的矩形网格基础上修改代码。

// 设置浮点数运算的精度
#ifdef GL_ES
precision mediump float;
#endif
// Uniform变量声明
uniform vec2 u_resolution;
uniform float u_time;

// 定义PI的值
#define PI 3.14159265358979323846

// 将一个2D点绕原点旋转的函数
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) {
    // 将大小转换为范围在0到1之间的值
    _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);

    // 将空间划分为4个瓦片
    st = tile(st, 4.0);

    // 使用矩阵将空间旋转45度
    // st = rotate2D(st, PI * 0.25);

    // 绘制方形
    color = vec3(box(st, vec2(0.7), 0.01));
    // color = vec3(st, 0.0);

    // 输出最终颜色
    gl_FragColor = vec4(color, 1.0);
}

结果是这样的。

image.png(图4)

如果打开第65行的注释,可以得到矩形旋转后的样式。

image.png(图5)

3.偏移图案

有了上面的基础下面对图案进行偏移。

image.png(图6)

如图6所示砖在不同行的偏移不同,也就是不同奇偶行的偏移以不同。
#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

// 瓦片化函数,用于创建砖块效果
vec2 brickTile(vec2 _st, float _zoom){
    _st *= _zoom;

    // 在这里进行偏移

    // 使用 step 函数来判断是否需要在 x 方向上进行偏移
    // step(a, b) 在 b 处返回 0,否则返回 1
    // mod(_st.y, 2.0) 取 _st.y 对 2.0 的模
    // 如果 _st.y 对 2.0 的模小于 1.0,step 的结果为 1,进行偏移
    // 如果 _st.y 对 2.0 的模大于等于 1.0,step 的结果为 0,不进行偏移
    _st.x += step(1., mod(_st.y, 2.0)) * 0.5;

    // 返回经过瓦片化后的点的小数部分
    return fract(_st);
}

// 绘制带有平滑边缘的方形的函数
float box(vec2 _st, vec2 _size){
    // 将大小转换为范围在0到1之间的值
    _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);

    // 现代度量砖块尺寸为 215mm x 102.5mm x 65mm
    
    // 取消下面的注释以应用砖块尺寸的缩放
    st /= vec2(2.15, 0.65) / 1.5;

    // 应用砖块瓦片化
    st = brickTile(st, 5.0); // 将坐标进行砖块瓦片化,缩放因子为 5.0

    // 绘制方形
    // color = vec3(box(st, vec2(0.9)));

    // 取消下面的注释以查看空间坐标
    color = vec3(st, 0.0);

    gl_FragColor = vec4(color, 1.0);
}

效果如下。

image.png(图7)

打开53行的注释可以看见效果是这样的。

image.png(图8)

加入动画向左偏移 稍加需改

// 瓦片化函数,用于创建砖块效果
vec2 brickTile(vec2 _st, float _zoom){
    _st *= _zoom;

    // 计算偏移量,偏移行的偏移距离随时间变化
    float yOffset = step(1., mod(_st.y, 2.0)) * (u_time * 0.1); // 这里的 0.1 是用于调整偏移量的因子
    _st.x += yOffset;

    // 返回经过瓦片化后的点的小数部分
    return fract(_st);
}

奇数行向左移动偶数行向右移动 floor取整 mod取模 step分解函数小于为0大于等于为1

// 瓦片化函数,用于创建砖块效果
vec2 brickTile(vec2 _st, float _zoom){
    _st *= _zoom;

    // 计算偏移量,奇数行向左移动,偶数行向右移动,移动距离随时间变化
    float yOffset = (mod(floor(_st.y), 2.0) - 0.5) * (u_time * 0.1);
    _st.x += yOffset;

    // 返回经过瓦片化后的点的小数部分
    return fract(_st);
}

4.瓷砖技巧

有了前面的网格砖块设计,现在对每个网格变换一分为四生成瓷砖效果。利用rotateTilePattern函数。

#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;
}

// 瓦片函数,将坐标进行瓦片化,缩放因子为 _zoom
vec2 tile(vec2 _st, float _zoom) {
    _st *= _zoom;
    return fract(_st);
}

// 旋转瓦片模式函数,根据位置对每个瓦片进行旋转
vec2 rotateTilePattern(vec2 _st) {

    // 将坐标系统放大为2x2
    _st *= 2.0;

    // 根据位置给每个单元格分配索引号
    float index = 0.0;
    index += step(1.0, mod(_st.x, 2.0));
    index += step(1.0, mod(_st.y, 2.0)) * 2.0;

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

    // 将每个单元格的值限制在0.0到1.0之间
    _st = fract(_st);

    // 根据索引号旋转每个单元格
    if (index == 1.0) {
        // 旋转单元格1 90度
        _st = rotate2D(_st, PI * 0.5);
    } else if (index == 2.0) {
        // 旋转单元格2 -90度
        _st = rotate2D(_st, PI * -0.5);
    } else if (index == 3.0) {
        // 旋转单元格3 180度
        _st = rotate2D(_st, PI);
    }

    return _st;
}

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

    st = tile(st, 3.0); // 对坐标进行瓦片化,缩放因子为3.0
    st = rotateTilePattern(st); // 根据位置旋转每个单元格

    // 创建更有趣的组合效果
    // st = tile(st, 2.0);
    // st = rotate2D(st, -PI * u_time * 0.25);
    // st = rotateTilePattern(st * 2.0);
    // st = rotate2D(st, PI * u_time * 0.25);

    // step(st.x, st.y) 生成了一个黑白三角形
    // 你可以根据需要使用任何设计
    gl_FragColor = vec4(vec3(step(st.x, st.y)), 1.0);
}

结果为。

image.png(图9)