>  기사  >  웹 프론트엔드  >  Perlin 노이즈를 사용하여 CSS에서 멋진 그래픽을 그리는 방법에 대해 이야기해 보겠습니다!

Perlin 노이즈를 사용하여 CSS에서 멋진 그래픽을 그리는 방법에 대해 이야기해 보겠습니다!

青灯夜游
青灯夜游앞으로
2022-07-26 19:09:582364검색

백색소음이란 무엇인가요? Perlin 노이즈를 사용하여 CSS에서 멋진 그래픽을 그리는 방법은 무엇입니까? 다음 기사에서는 노이즈를 사용하여 아름다운 CSS 그래픽을 만드는 방법을 소개합니다. 도움이 되기를 바랍니다.

Perlin 노이즈를 사용하여 CSS에서 멋진 그래픽을 그리는 방법에 대해 이야기해 보겠습니다!

평소에는 CSS를 사용하여 흥미로운 그래픽을 만드는 것을 좋아합니다. [추천 학습: css 동영상 튜토리얼]

먼저 간단한 예를 살펴보겠습니다. 먼저 10x10 그리드를 구현한다고 가정해 보겠습니다.

이때 몇 가지 무작위 효과를 사용하여 이 패턴을 최적화할 수 있습니다. 예를 들어, 무작위로 다른 색상을 추가합니다.

각 그리드의 색상을 무작위로 채우는 데 무작위성을 사용하기는 하지만 이는 다소 흥미로워 보이지만 이는 단지 지저분한 그래픽일 뿐이며 예술적인 느낌은 없습니다. .

이게 왜죠? 왜냐하면 여기서 무작위성은 완전히 무작위적이고 일종의 백색 소음이기 때문입니다.

백색소음이란 무엇인가요?

Noise는 실제로 난수 생성기입니다.

그럼 백색소음이란 무엇일까요? 프로그래머의 입장에서 이해한다면, 우리가 자바스크립트에서 사용하는 random() 함수는 생성되는 숫자가 대략 0~1 범위 내에서 완전히 랜덤하다는 것을 알 수 있습니다.

노이즈의 기본은 임의의 숫자입니다. 예를 들어 위 그래픽의 각 그리드에 임의의 색상을 추가하면 전혀 아름다움이 없는 지저분한 그래픽 블록이 됩니다.

백색 잡음 또는 백색 잡음은 전력 스펙트럼 밀도가 일정한 무작위 신호입니다. 즉, 이 신호의 전력 스펙트럼 밀도는 각 주파수 대역에서 동일합니다. 백색광은 다양한 주파수(색상)의 단색광과 혼합되므로 이 신호는 평평한 전력 스펙트럼을 갖는 특성을 갖습니다. "백색", 이 신호를 백색 잡음이라고도 합니다.

백색 잡음을 사용하여 생성된 그래픽이 부자연스럽고 심미적이지 않기 때문입니다.

실생활에서 자연 소음을 관찰해 보세요. 위의 소음과는 다르게 보입니다. 예를 들어, 나무의 질감과 산의 기복은 모양이 프랙탈인 경향이 있습니다. 즉, 서로 다른 수준의 세부 사항을 포함하고 있습니다. 이러한 무작위 구성 요소는 완전히 독립적이지 않으며 둘 사이에는 일정한 상관 관계가 있습니다. 그리고 분명히 백색소음은 그런 일을 하지 않습니다.

Perlin Noise

이렇게 해서 자연스럽게 Perlin Noise를 소개합니다.

펄린 노이즈(Perlin Noise)는 Ken Perlin이 발명한 자연적인 노이즈 생성 알고리즘을 말합니다.

소개하기 전에 위의 그래프를 살펴보겠습니다. 화이트 노이즈(완전 랜덤)를 사용하지 않고 Perlin 노이즈를 사용하면 어떤 모습일까요?

다음과 같이 보일 수 있습니다.

여기서 애니메이션 그림을 만들었습니다. 각 클릭이 Perlin 노이즈 무작위화를 사용하여 각 그리드에 서로 다른 무작위 색상을 부여한 결과임을 느낄 수 있습니다.

할 수 있습니다. Perlin Noise의 무작위 효과로 생성된 그래픽은 서로 관련이 없으며 그 사이의 변화는 연속적이며 그 사이에는 점프가 없습니다. 이 무작위 효과는 위에서 언급한 나무 질감과 산맥의 변화와 같은 자연의 무작위 효과와 유사합니다.

위에서 언급했듯이 노이즈는 실제로 난수 생성기입니다. 그리고 여기:

  • 백색 잡음의 문제는 너무 무작위적이고 규칙이 전혀 없다는 것입니다

  • 그리고 Perlin 잡음은 무작위성을 기반으로 하며 이징 곡선을 사용하여 이에 대한 부드러운 보간을 수행합니다. 기초, 최종 노이즈 효과를 더 자연스럽게 만들기

구체적인 구현 방법은 여기에 있습니다향상된 노이즈 참조 구현, 한 번 살펴보세요. 실제로는 소스 코드가 많지 않습니다.

// This code implements the algorithm I describe in a corresponding SIGGRAPH 2002 paper.
// JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.

public final class ImprovedNoise {
   static public double noise(double x, double y, double z) {
      int X = (int)Math.floor(x) & 255,                  // FIND UNIT CUBE THAT
          Y = (int)Math.floor(y) & 255,                  // CONTAINS POINT.
          Z = (int)Math.floor(z) & 255;
      x -= Math.floor(x);                                // FIND RELATIVE X,Y,Z
      y -= Math.floor(y);                                // OF POINT IN CUBE.
      z -= Math.floor(z);
      double u = fade(x),                                // COMPUTE FADE CURVES
             v = fade(y),                                // FOR EACH OF X,Y,Z.
             w = fade(z);
      int A = p[X  ]+Y, AA = p[A]+Z, AB = p[A+1]+Z,      // HASH COORDINATES OF
          B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z;      // THE 8 CUBE CORNERS,

      return lerp(w, lerp(v, lerp(u, grad(p[AA  ], x  , y  , z   ),  // AND ADD
                                     grad(p[BA  ], x-1, y  , z   )), // BLENDED
                             lerp(u, grad(p[AB  ], x  , y-1, z   ),  // RESULTS
                                     grad(p[BB  ], x-1, y-1, z   ))),// FROM  8
                     lerp(v, lerp(u, grad(p[AA+1], x  , y  , z-1 ),  // CORNERS
                                     grad(p[BA+1], x-1, y  , z-1 )), // OF CUBE
                             lerp(u, grad(p[AB+1], x  , y-1, z-1 ),
                                     grad(p[BB+1], x-1, y-1, z-1 ))));
   }
   static double fade(double t) { return t * t * t * (t * (t * 6 - 15) + 10); }
   static double lerp(double t, double a, double b) { return a + t * (b - a); }
   static double grad(int hash, double x, double y, double z) {
      int h = hash & 15;                      // CONVERT LO 4 BITS OF HASH CODE
      double u = h<8 ? x : y,                 // INTO 12 GRADIENT DIRECTIONS.
             v = h<4 ? y : h==12||h==14 ? x : z;
      return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
   }
   static final int p[] = new int[512], permutation[] = { 151,160,137,91,90,15,
   131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
   190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
   88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
   77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
   102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
   135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
   5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
   223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
   129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
   251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
   49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
   138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
   };
   static { for (int i=0; i < 256 ; i++) p[256+i] = p[i] = permutation[i]; }
}

물론 이 기사는 다음과 같습니다. 는 베를린 소음에 관한 것이 아닙니다 어떻게 구현해야 할까요? 위 코드를 읽는 사람이라면 누구나 혼란스러울 것입니다. Perlin 노이즈를 사용하여 보다 일반적인 그래픽 효과를 만들 수 있다는 것만 알면 됩니다. 그래픽을 더욱 아름답게 만드십시오.

CSS-doodle을 사용하여 CSS에서 Perlin 노이즈를 사용하세요

그럼 CSS에서

Perlin 노이즈를 어떻게 사용하나요?

一种方式是找一些现成的库,譬如 p5.js 里面的 noise 函数。

当然,这里,我习惯使用 CSS-doodle,这个 CSS 图形构建库我在多篇文章中已经都有介绍过。

简单而言,CSS-doodle 它是一个基于 Web-Component 的库。允许我们快速的创建基于 CSS Grid 布局的页面,并且提供各种便捷的指令及函数(随机、循环等等),让我们能通过一套规则,得到不同 CSS 效果。可以简单看看它的主页 -- Home Page of CSS-doodle,只需要 5min 也许就能快速上手。

譬如上述的图形,它的全部代码

<css-doodle grid="10x10">
    :doodle {
        @size: 50vmin;
        gap: 1px;
    }
   
    background: hsl(@rn(255, 1, 2), @rn(10%, 90%), @rn(10%, 90%));
</css-doodle>

没错,只需要这么寥寥几句,就可以勾勒出这样一幅图案:

CSS Pattern -- CSS Doodle

https://codepen.io/Chokcoco/pen/eYMNWNq

简单解释下:

  • css-doodle 是基于 Web-Component 封装的,基本所有的代码都写在 <css-doodle> 标签内,当然也可以写一些原生 CSS/JavaScript 辅助

  • 使用 grid="10x10" 即可生成一个 10x10 的 Grid 网格,再配合 @size: 50vmin,表示生成一个宽高大小为 50vmin 的 10x10 Grid 网格布局,其中 gap: 1px 表示 Gird 网格布局的 gap

  • 最后,整个代码的核心部分即是 background: hsl(@rn(255, 1, 2), @rn(10%, 90%), @rn(10%, 90%)),这里即表示对每个 grid item 赋予背景色,其中 @rn(),就是最核心的部分,利用了柏林噪声算法,有规律的将背景色 map 到每一个 grid 上

当然,最新的 CSS-doodle 文档上暂时还查不到 @rn() function 的用法。为此我特意请教了下该库的作者袁川老师。

得到的回复是,官网近期会重构,所以目前没有更新最新的语法。同时,@rn() 的实现使用的就是柏林噪声的实现。同时,函数相当于是类似 p5.js 里面的 noise 函数同时做了 map,map 到前面函数参数设定的 from 到 to 范围内。

这里的 @rn() 柏林噪声随机会根据 Grid 网格,Map 到每一个网格上,使之相邻的 Grid item 之间的值,存在一定的关联。

举个栗子,我们有个 10x10 的 Grid 布局,给其每个 Grid item,添加一个伪元素,伪元素的内容,使用 @r(100) 进行填充,注意,@r() 函数是没有规律的完全随机,那么生成的数字大概是这样的:

可以看到,它们每个各自之间的数字,是完全随机毫无关联的。

如果我们使用有关联的柏林噪声随机呢?使用 @rn(100) 填充每个格子的话,大概是这样:

观察一下,很容易发现,相邻的盒子之间,或者多个连续的格子之间,存在一定的关联性,这就使得,我们利用它创造出来的图形,会具备一定的规律。

可以简单看看源码的实现,当前,前提是你需要对 CSS-doodle 的用法有一定的了解:

    rn({ x, y, context, position, grid, extra, shuffle }) {
      let counter = 'noise-2d' + position;
      let [ni, nx, ny, nm, NX, NY] = last(extra) || [];
      let isSeqContext = (ni && nm);
      return (...args) => {
        let {from = 0, to = from, frequency = 1, amplitude = 1} = get_named_arguments(args, [
          'from', 'to', 'frequency', 'amplitude'
        ]);

        if (args.length == 1) {
          [from, to] = [0, from];
        }
        if (!context[counter]) {
          context[counter] = new Perlin(shuffle);
        }
        frequency = clamp(frequency, 0, Infinity);
        amplitude = clamp(amplitude, 0, Infinity);
        let transform = [from, to].every(is_letter) ? by_charcode : by_unit;
        let t = isSeqContext
          ? context[counter].noise((nx - 1)/NX * frequency, (ny - 1)/NY * frequency, 0)
          : context[counter].noise((x - 1)/grid.x * frequency, (y - 1)/grid.y * frequency, 0);
        let fn = transform((from, to) => map2d(t * amplitude, from, to, amplitude));
        let value = fn(from, to);
        return push_stack(context, 'last_rand', value);
      };
    },

语法大概是 @rn(from, to, frequency, amplitude),其中 fromto 表示随机范围,而 frequency 表示噪声的频率,amplitude 表示噪声的振幅。这两个参数可以理解为控制随机效果的频率和幅度。

其中 new Perlin(shuffle) 即运用到了柏林噪声算法。

Show Time

OK,上文介绍了很多与噪声和 CSS-doodle 相关的知识,下面我们回归 CSS,回归本文的主体。

在上述图形的基础上,我们可以再添加上随机的 scale()、以及 skew()。如果是完全随机的话,代码是这样的:

<css-doodle grid="20">
    :doodle {
        grid-gap: 1px;
        width: 600px; height: 600px;
    }
    background: hsl(@r(360), 80%, 80%);
    transform: 
        scale(@r(1.1, .3, 3)) 
        skew(@r(-45deg, 45deg, 3));
</css-doodle>
html,
body {
    width: 100%;
    height: 100%;
    background-color: #000;
}

上述代码表示的是一个 20x20 的 Grid 网格,每个 Grid item 都设置了完全随机的背景色、scale() 以及 skew()。当然,这里我们用的是 @r()而不是 @rn(),每个格子的每个属性的随机,没有任何的关联,那么我们会得到这样一幅图案:

好吧,这是什么鬼,毫无美感可言。我们只需要在上述代码的基础上,将普通的完全随机,改为柏林噪声随机 @rn()

<css-doodle grid="20">
    :doodle {
        grid-gap: 1px;
        width: 600px; height: 600px;
    }
    background: hsl(@rn(360), 80%, 80%);
    transform: 
        scale(@rn(1.1, .3, 3)) 
        skew(@rn(-45deg, 45deg, 3));
</css-doodle>

此时,就能得到完全不一样的效果:

这是由于,每个 Grid item 的随机效果,都基于它们在 Grid 布局中的位置,彼此存在关联,这就是柏林噪声随机的效果。

我可以再添加上 hue-rotate 动画:

html,
body {
    width: 100%;
    height: 100%;
    background-color: #000;
    animation: change 10s linear infinite;
}
@keyframes change {
    10% {
        filter: hue-rotate(360deg);
    }
}

看看效果,并且,在 CSS-doodle 中,由于随机效果,每次刷新,都可以得到不一样的图案:

CSS Doodle - CSS Pattern2

https://codepen.io/Chokcoco/pen/mdxJrGR

当然,这个样式还可以搭配各式各样其他的 idea,像是这样:

CSS Doodle - CSS Pattern 3

https://codepen.io/Chokcoco/pen/wvmazOy

又或者是这样:

CSS Doodle - CSS Pattern 4

https://codepen.io/Chokcoco/pen/dymoOGN

emmm,又或者这样:

CSS Doodle - CSS Pattern 5

https://codepen.io/Chokcoco/pen/PoRqdYP

是的,我们可以把柏林噪声随机应用在各种属性上,我们可以放飞想象,去尝试各种不一样的搭配。下面这个, 就是把柏林噪声运用在点阵定位上:

<css-doodle grid="30x30">
    :doodle {
        @size: 90vmin;
        perspective: 10px;
    }
    position: absolute;
    top: 0;
    left: 0;
    width: 2px;
    height: 2px;
    border-radius: 50%;
    top: @rn(1%, 100%, 1.5);
    left: @rn(1%, 100%, 1.5);
    transform: scale(@rn(.1, 5, 2));
    background: hsl(@rn(1, 255, 3), @rn(10%, 90%), @rn(10%, 90%));
</css-doodle>

CodePen Demo -- CSS Doodle - CSS Pattern6

https://codepen.io/Chokcoco/pen/GRxJXVE

亦或者配合运用在 transform: rotate() 上:

<css-doodle grid="20x5">
    @place-cell: center;
    @size: calc(@i * 1.5%);
    :doodle {
        width: 60vmin; 
        height: 60vmin;
    }
    z-index: calc(999 - @i);
    border-radius: 50%;
    border: 1px @p(dashed, solid, double) hsl(@rn(255), 70%, @rn(60, 90%));
    border-bottom-color: transparent;
    border-left-color: transparent;
    transform: 
        rotate(@rn(-720deg, 720deg))
        scale(@rn(.8, 1.2, 3));
</css-doodle>

效果如下:

当然,每一次随机,都会是不一样的结果:

CodePen Demo -- CSS doodle - CSS Pattern7

https://codepen.io/Chokcoco/pen/ZExGjoy

好吧,我个人想象力有限,大家可以自行找到任一 DEMO,Fork 后自己去尝试碰撞出不一样的火花。

原文地址:https://segmentfault.com/a/1190000042103702

作者:chokcoco

(学习视频分享:web前端入门

위 내용은 Perlin 노이즈를 사용하여 CSS에서 멋진 그래픽을 그리는 방법에 대해 이야기해 보겠습니다!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제