【译】创意编码之噪音
原文:https://varun.ca/noise/
作者:Ahmad Shadeed
译者:NSGUF
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bGmGeXsB-1658998821080)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e111af665402499ab06e1335579b5196~tplv-k3u1fbpfcp-watermark.image?)]
噪音是一种创意编码中不可或缺的工具。我们使用它来生成各种各样的仿自然形态的效果,如云,风景以及轮廓等;或者以更逼真的行为移动和扭曲对象;
表面上看,噪音似乎很容易使用,但是它有非常多层次,这篇文章深入研究了什么是噪音,它的变种有哪些,如何在网络和应用程序上使用;也会展示很多的例子,非常多的例子!
噪音是什么?
假设你想在屏幕上移动一个物体。动画师会使用关键帧和补帧来描述这个确切的运动。生成类似这个动画师的艺术家依赖于算法;
那么,math.random()
可以实现吗?
不行,随机就太不自然了,看下面这个粉红色的球,到处弹跳,这就很恶心了🥴;
我们需要更平滑,更自然的随机性,这个(黄色的球)就是噪音函数生成的,美观多了;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fmfZkUIL-1658998821081)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e51d0659c5da4f348bf5180b58db3b8c~tplv-k3u1fbpfcp-watermark.image?)]
自然随机的想法在创意编码中出现了一遍又一遍。例如:我们经常使用噪音函数生成纹理,移动或扭曲物体;
在网络上生成噪音
两种类型噪音——Perlin和Simplex
在1980年初期,Ken Perlin在做Tron时开发了Perlin噪音并因这个特效获得奥斯卡奖。然后他在此这种特效上改进开发了Simplex,这种新算法运行速度更快。我将会专注于后者,并在我的例子中使simplex-noise库;
噪音函数接受两个输入(用于生成输出)并且返回一个-1到1的值。这个输出结合了Math.sin()
和Math.random()
,随机波就是我描述他的方式;
import SimplexNoise from 'simplex-noise';
const simplex = new SimplexNoise();
const y = simplex.noise2D(x * frequency, 0) * amplitude;
基础力学和波浪的工作原理相似,你可以通过调整频率和振幅来控制波浪的速度和幅度;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X1z42LKQ-1658998821081)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/64d5e407a3354523ad44ab44b875c718~tplv-k3u1fbpfcp-watermark.image?)]
emm,二维噪音,发生了什么?
噪音维度
噪音算法可以实现多维度。这里是将维度视为噪音生成器的输入数量,2个输入为2D,三个是3D,以此类推;
选择哪种维度,取决于使用的生成器的变量,让我们看几个例子:
💡 从这里开始,每个例子都会有个通过指向实际源码嵌入的源码卡片,在某些情况下,你可以调整变量;(翻译我是使用的gif)
一维噪音 —— 波动
从技术上讲,是没有一维噪音的。你可以通过将0作为第二个参数给到simplex noise2D得到1维噪音;架设你想让这个移动的球看起来比较自然,可以增加x坐标并生成y坐标;
x += 0.1;
y = simplex.noise2D(x * frequency, 0) * amplitude;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wVdb6brW-1658998821081)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8859eaadd15d411dbc8995bef6bc7719~tplv-k3u1fbpfcp-watermark.image?)]
二维噪音 —— 地形生成器
我们可以通过移动z轴方向的顶点,将一个水平2D平面转换成丘陵地形,使用x,y轴坐标来生成z坐标位置;
就这样,我们有了一个地形生成器🏔️;
z = simplex.noise2D(x * frequency, y * frequency) * amplitude;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WLrok4pO-1658998821082)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1d73ae19d6ce45979cd2286bd37df285~tplv-k3u1fbpfcp-watermark.image?)]
三维噪音——顶点位移
你可能已经见过这种自然扭曲的球体,它们通过移动球体顶点生成;使用顶点坐标(x,y,z)
来生成扭曲的位置变量,然后通过移动顶点生成放射的形状;
const distortion =
simplex.noise3D(x * frequency, y * frequency, z * frequency) * amplitude;
newPosition = position.normalize().multiplyScalar(distortion);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jWRUe0VR-1658998821082)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1e808e90e3c542d2b7a0692daec83c2d~tplv-k3u1fbpfcp-watermark.image?)]
四维噪音—— 动态扭曲效果
我们可以通过4维噪音来生成一个不停动态扭曲的球,输入分别是顶点坐标(x,y,z)
和时间
。这项技术常用于创造类似火球的效果;
const distortion =
simplex.noise4D(
x * frequency,
y * frequency,
z * frequency,
time * frequency
) * amplitude;
newPosition = position.normalize().multiplyScalar(distortion);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qUcIcRLh-1658998821082)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/99d17ff2ca714953b15cb278e9023a24~tplv-k3u1fbpfcp-watermark.image?)]
在上面的大多数例子里我们都使用了振幅,这是程序中缩放噪音输出的方式,你还可以使用插值将噪音输出映射到选择的指定范围;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UzilE5mk-1658998821083)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5942aad65bad41a8b4f079109ba366c6~tplv-k3u1fbpfcp-watermark.image?)]
现在我们已经掌握了基础知识,让我们看一些噪音的其他应用;
纹理
使用(x,y)
坐标,生成二维的表格格式的噪音,看起来像这样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-32VSSpUj-1658998821083)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/edf582b085f04a8788982ad7cc006a95~tplv-k3u1fbpfcp-watermark.image?)]
使用方式:
for (let y = 0; y < gridSize[1]; y++) {
for (let x = 0; x < gridSize[0]; x++) {
const n = simplex.noise2D(
x / (gridSize[0] * 0.75),
y / (gridSize[1] * 0.75)
);
}
}
这是我们的出发点。
我们可以使用Marching Squares算法将2D数据转换成等高线。使用Canvas,SVG,WebGL或者其他你喜欢的工具,我最近使用SVG来生成带有轮廓的卡片,这方面相关的完整教程,请查看https://storybook.js.org/blog/generative-image-service/📸;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b8Tgu6cx-1658998821084)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a31d87968fc1452884116e2ae9ce4e12~tplv-k3u1fbpfcp-watermark.image?)]
关于噪音,最酷的事情是你可以添加时间维度,来给你的图片添加动画;
simplex.noise3D(x / (gridSize[0] * 0.75), y / (gridSize[1] * 0.75), time);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P1npnNEe-1658998821084)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/56869babab2b40f8b7fd696d85e6c717~tplv-k3u1fbpfcp-watermark.image?)]
场
老实讲,噪音的二维数据是非常实用的,这可以做很多的事情,其中一个是我特别喜欢——噪音领域;
让我们回到最初的灰度输出,这里可以映射出一个更有趣的色阶,你可以获取到等离子;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YvFdMnYn-1658998821085)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/afe83871233e436ba4dbd8a9cef24fa6~tplv-k3u1fbpfcp-watermark.image?)]
或者从矩形网格开始,通过使用噪音来控制颜色和角度,将其变成向量场;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TT1qsUjB-1658998821085)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/21e84885f83c4671a3856c3417107517~tplv-k3u1fbpfcp-watermark.image?)]
噪音粒子
向量场很酷,但是流场更让人兴奋。这是我去年画的图;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NYiqx5Ek-1658998821086)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1ec31486a00040759f9dfc6283936435~tplv-k3u1fbpfcp-watermark.image?)]
注意笔的轨迹。每一划都是通过将粒子放到向量场上来生成的。然后追踪它的路径。💫
好吧,让我们分解这个过程。
创建流场
第一步,创建一个向量场。和之前一样。但这一次,我们不会为向量场设置动画。
第 2 步,将一堆粒子放到画布上。它们的运动方向基于底层向量场。向前迈出一步,找到新的方向并重复。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OciwyyEC-1658998821086)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/056cfea019b548dd9caf68c83d4fec76~tplv-k3u1fbpfcp-watermark.image?)]
function moveParticle(particle) {
// Calculate direction from noise
const angle =
simplex.noise2D(particle.x * FREQUENCY, particle.y * FREQUENCY) * AMPLITUDE;
// Update the velocity of the particle
// based on the direction
particle.vx += Math.cos(angle) * STEP;
particle.vy += Math.sin(angle) * STEP;
// Move the particle
particle.x += particle.vx;
particle.y += particle.vy;
// Use damping to slow down the particle (think friction)
particle.vx *= DAMPING;
particle.vy *= DAMPING;
particle.line.push([particle.x, particle.y]);
}
第三步:追踪粒子的位置,以追踪其路径;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GhdgB1fI-1658998821087)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4a2afcbc342d4a0abd42264146c7058a~tplv-k3u1fbpfcp-watermark.image?)]
添加更多的粒子和颜色,我们就可以获得流场;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UxY17WUH-1658998821087)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2b4c339650924f69a14ccd2b38e0ce0b~tplv-k3u1fbpfcp-watermark.image?)]
粒子系统
说到粒子,噪声也出现在粒子系统中。对于像雨、雪或五彩纸屑等效果,你希望模拟一个看起来很自然的运动。想象一个五彩纸屑颗粒飘向地面。粒子不会直线移动。由于空气阻力,它们会漂浮和摆动。噪声是添加仿自然形态非常好的工具。
const wiggle = {
x: simplex.noise2D(particle.x * frequency, time) * amplitude,
y: simplex.noise2D(particle.y * frequency, time) * amplitude,
};
particle.x += wiggle.x;
particle.y += wiggle.y;
想深入了解,详情请看:https://varun.ca/confetti/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7vvQ7R0l-1658998821087)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d18b835a4c3d4dd48af6f971f56be623~tplv-k3u1fbpfcp-watermark.image?)]
着色器
让我们回到那些动画斑点。使用着色器操作和设置3D动画几何图形的效率要高得多。但是着色器本身是一个完全不同的世界。对于初学者来说,有一个特殊的 WebGL 版本的噪音,gsl-noise。我们需要使用 glslify 将它导入到我们的着色器中。
我们会慢慢来,先从 2D 开始。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KZ6BwuB2-1658998821088)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d365e65cb4f4a61b61a4eec4b30768f~tplv-k3u1fbpfcp-watermark.image?)]
上面的动画与我们之前看到的轮廓非常相似。它是通过片段着色器实现的。这意味着我们为canvas的每个像素运行相同的程序。
注意到pragma
行了吗?那是我们导入 webgl-noise。然后用它为每个像素位置 vUv
生成噪声数据。
这部分现在看起来应该很熟悉了。
fragment.glsl
precision highp float;
uniform float time;
uniform float density;
varying vec2 vUv;
#define PI 3.141592653589793
#pragma glslify: noise = require(glsl-noise/simplex/3d);
float patternZebra(float v){
float d = 1.0 / density;
float s = -cos(v / d * PI * 2.);
return smoothstep(.0, .1 * d, .1 * s / fwidth(s));
}
void main() {
// Generate noise data float amplitude = 1.0; float frequency = 1.5; float noiseValue = noise(vec3(vUv * frequency, time)) * amplitude; // Convert noise data to rings
float t = patternZebra(noiseValue);
vec3 color = mix(vec3(1.,0.4,0.369), vec3(0.824,0.318,0.369), t);
// Clip the rings to a circle
float dist = length(vUv - vec2(0.5, 0.5));
float alpha = smoothstep(0.250, 0.2482, dist);
gl_FragColor = vec4(color, alpha);
}
片段着色器具有用于绘制形状的距离函数的概念。 patternZebra 就是一个将噪声数据转换为环的示例。本质上,它返回 0 或 1。然后我们用它来选择混合的颜色。最后,使用另一个距离函数来剪裁圆形边界。
看起来很基础?
好吧,着色器的惊人之处在于,你可以将它们变成一种材质并将其应用于更复杂的几何体。
噪声材质
我们可以从上面获取相同的 2D mars 着色器。将其转换为 ThreeJS 着色器材质。然后使用顶点位置 vPosition 代替 vUv 生成噪声。 瞧,我们有一个 3D mars!
const material = new THREE.ShaderMaterial({
extensions: {
derivatives: true,
},
uniforms: {
time: { value: 0 },
density: { value: 1.0 },
},
vertexShader: /*glsl*/ ` varying vec3 vPosition; void main () { vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `,
fragmentShader: glslify(/* glsl */ ` precision highp float; varying vec3 vPosition; uniform float time; uniform float density; #pragma glslify: noise = require(glsl-noise/simplex/4d); #define PI 3.141592653589793 float patternZebra(float v){ float d = 1.0 / density; float s = -cos(v / d * PI * 2.); return smoothstep(.0, .1 * d, .1 * s / fwidth(s)); } void main () { float frequency = .6; float amplitude = 1.5; float v = noise(vec4(vPosition * frequency, sin(PI * time))) * amplitude; float t = patternZebra(v); vec3 fragColor = mix(vec3(1.,0.4,0.369), vec3(0.824,0.318,0.369), t); gl_FragColor = vec4(fragColor, 1.); } `),
});
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iplAlFUi-1658998821088)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/35281e84fe85477292ad62c73187d207~tplv-k3u1fbpfcp-watermark.image?)]
老实说,这只是一个开始。着色器开辟了许多可能性。就像下面的例子
不仅是噪声映射到颜色,还可以是透明度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DyH2dBpc-1658998821088)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/891b024e400f47409da1ce0b7744afbb~tplv-k3u1fbpfcp-watermark.image?)]
光泽斑点
与顶点置换背后的想法非常相似。我们编写了一个顶点着色器来转换球体的每个顶点,而不是片段着色器。同样,使用着色器材质应用。
vertex.glsl
precision highp float;
varying vec3 vNormal;
#pragma glslify: snoise4 = require(glsl-noise/simplex/4d)
uniform float u_time;
uniform float u_amplitude;
uniform float u_frequency;
void main () {
vNormal = normalMatrix * normalize(normal); float distortion = snoise4(vec4(normal * u_frequency, u_time)) * u_amplitude; vec3 newPosition = position + (normal * distortion);
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
加上一些 matcap,你就会得到一些有光泽的动画斑点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vVFKC4s2-1658998821089)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8b71c2607dce4f929ab924f6225d434d~tplv-k3u1fbpfcp-watermark.image?)]
轮到你创造噪音了
那真是一段旅程。运动、纹理、场、粒子系统和置换——噪声都能做。它确实是创意编码世界的主力军。
很明显,你想玩噪音😁
我已经为你准备了一个小小的入门 CodePen。它旋转了一个噪声网格并将其映射到一个字形数组。只需从修改数组开始,看看你能走多远。
const glyphs = ['¤', '✳', '●', '◔', '○', '◕', '◐', '◑', '◒'];
或者,如果您正在寻找更高级的东西,请查看noisedeck.app。它就像一个只用于视觉效果的合成器。
原文:https://varun.ca/noise/
作者:Ahmad Shadeed
译者:NSGUF
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2W3b0xPg-1658998815215)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e111af665402499ab06e1335579b5196~tplv-k3u1fbpfcp-watermark.image?)]
噪音是一种创意编码中不可或缺的工具。我们使用它来生成各种各样的仿自然形态的效果,如云,风景以及轮廓等;或者以更逼真的行为移动和扭曲对象;
表面上看,噪音似乎很容易使用,但是它有非常多层次,这篇文章深入研究了什么是噪音,它的变种有哪些,如何在网络和应用程序上使用;也会展示很多的例子,非常多的例子!
噪音是什么?
假设你想在屏幕上移动一个物体。动画师会使用关键帧和补帧来描述这个确切的运动。生成类似这个动画师的艺术家依赖于算法;
那么,math.random()
可以实现吗?
不行,随机就太不自然了,看下面这个粉红色的球,到处弹跳,这就很恶心了🥴;
我们需要更平滑,更自然的随机性,这个(黄色的球)就是噪音函数生成的,美观多了;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EaB58jTV-1658998815216)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e51d0659c5da4f348bf5180b58db3b8c~tplv-k3u1fbpfcp-watermark.image?)]
自然随机的想法在创意编码中出现了一遍又一遍。例如:我们经常使用噪音函数生成纹理,移动或扭曲物体;
在网络上生成噪音
两种类型噪音——Perlin和Simplex
在1980年初期,Ken Perlin在做Tron时开发了Perlin噪音并因这个特效获得奥斯卡奖。然后他在此这种特效上改进开发了Simplex,这种新算法运行速度更快。我将会专注于后者,并在我的例子中使simplex-noise库;
噪音函数接受两个输入(用于生成输出)并且返回一个-1到1的值。这个输出结合了Math.sin()
和Math.random()
,随机波就是我描述他的方式;
import SimplexNoise from 'simplex-noise';
const simplex = new SimplexNoise();
const y = simplex.noise2D(x * frequency, 0) * amplitude;
基础力学和波浪的工作原理相似,你可以通过调整频率和振幅来控制波浪的速度和幅度;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-olxqSIDO-1658998815216)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/64d5e407a3354523ad44ab44b875c718~tplv-k3u1fbpfcp-watermark.image?)]
emm,二维噪音,发生了什么?
噪音维度
噪音算法可以实现多维度。这里是将维度视为噪音生成器的输入数量,2个输入为2D,三个是3D,以此类推;
选择哪种维度,取决于使用的生成器的变量,让我们看几个例子:
💡 从这里开始,每个例子都会有个通过指向实际源码嵌入的源码卡片,在某些情况下,你可以调整变量;(翻译我是使用的gif)
一维噪音 —— 波动
从技术上讲,是没有一维噪音的。你可以通过将0作为第二个参数给到simplex noise2D得到1维噪音;架设你想让这个移动的球看起来比较自然,可以增加x坐标并生成y坐标;
x += 0.1;
y = simplex.noise2D(x * frequency, 0) * amplitude;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KDKwSLfQ-1658998815217)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8859eaadd15d411dbc8995bef6bc7719~tplv-k3u1fbpfcp-watermark.image?)]
二维噪音 —— 地形生成器
我们可以通过移动z轴方向的顶点,将一个水平2D平面转换成丘陵地形,使用x,y轴坐标来生成z坐标位置;
就这样,我们有了一个地形生成器🏔️;
z = simplex.noise2D(x * frequency, y * frequency) * amplitude;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-utWGuuIB-1658998815217)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1d73ae19d6ce45979cd2286bd37df285~tplv-k3u1fbpfcp-watermark.image?)]
三维噪音——顶点位移
你可能已经见过这种自然扭曲的球体,它们通过移动球体顶点生成;使用顶点坐标(x,y,z)
来生成扭曲的位置变量,然后通过移动顶点生成放射的形状;
const distortion =
simplex.noise3D(x * frequency, y * frequency, z * frequency) * amplitude;
newPosition = position.normalize().multiplyScalar(distortion);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dhYIK1zz-1658998815218)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1e808e90e3c542d2b7a0692daec83c2d~tplv-k3u1fbpfcp-watermark.image?)]
四维噪音—— 动态扭曲效果
我们可以通过4维噪音来生成一个不停动态扭曲的球,输入分别是顶点坐标(x,y,z)
和时间
。这项技术常用于创造类似火球的效果;
const distortion =
simplex.noise4D(
x * frequency,
y * frequency,
z * frequency,
time * frequency
) * amplitude;
newPosition = position.normalize().multiplyScalar(distortion);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OkQSI1Cu-1658998815218)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/99d17ff2ca714953b15cb278e9023a24~tplv-k3u1fbpfcp-watermark.image?)]
在上面的大多数例子里我们都使用了振幅,这是程序中缩放噪音输出的方式,你还可以使用插值将噪音输出映射到选择的指定范围;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPcyn2Bp-1658998815219)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5942aad65bad41a8b4f079109ba366c6~tplv-k3u1fbpfcp-watermark.image?)]
现在我们已经掌握了基础知识,让我们看一些噪音的其他应用;
纹理
使用(x,y)
坐标,生成二维的表格格式的噪音,看起来像这样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qiTBxcA4-1658998815220)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/edf582b085f04a8788982ad7cc006a95~tplv-k3u1fbpfcp-watermark.image?)]
使用方式:
for (let y = 0; y < gridSize[1]; y++) {
for (let x = 0; x < gridSize[0]; x++) {
const n = simplex.noise2D(
x / (gridSize[0] * 0.75),
y / (gridSize[1] * 0.75)
);
}
}
这是我们的出发点。
我们可以使用Marching Squares算法将2D数据转换成等高线。使用Canvas,SVG,WebGL或者其他你喜欢的工具,我最近使用SVG来生成带有轮廓的卡片,这方面相关的完整教程,请查看https://storybook.js.org/blog/generative-image-service/📸;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PkDlIj33-1658998815220)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a31d87968fc1452884116e2ae9ce4e12~tplv-k3u1fbpfcp-watermark.image?)]
关于噪音,最酷的事情是你可以添加时间维度,来给你的图片添加动画;
simplex.noise3D(x / (gridSize[0] * 0.75), y / (gridSize[1] * 0.75), time);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V4ih7BQX-1658998815221)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/56869babab2b40f8b7fd696d85e6c717~tplv-k3u1fbpfcp-watermark.image?)]
场
老实讲,噪音的二维数据是非常实用的,这可以做很多的事情,其中一个是我特别喜欢——噪音领域;
让我们回到最初的灰度输出,这里可以映射出一个更有趣的色阶,你可以获取到等离子;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4qr5JfT5-1658998815221)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/afe83871233e436ba4dbd8a9cef24fa6~tplv-k3u1fbpfcp-watermark.image?)]
或者从矩形网格开始,通过使用噪音来控制颜色和角度,将其变成向量场;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vf4cq6A8-1658998815221)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/21e84885f83c4671a3856c3417107517~tplv-k3u1fbpfcp-watermark.image?)]
噪音粒子
向量场很酷,但是流场更让人兴奋。这是我去年画的图;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NqzHXmf3-1658998815222)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1ec31486a00040759f9dfc6283936435~tplv-k3u1fbpfcp-watermark.image?)]
注意笔的轨迹。每一划都是通过将粒子放到向量场上来生成的。然后追踪它的路径。💫
好吧,让我们分解这个过程。
创建流场
第一步,创建一个向量场。和之前一样。但这一次,我们不会为向量场设置动画。
第 2 步,将一堆粒子放到画布上。它们的运动方向基于底层向量场。向前迈出一步,找到新的方向并重复。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eFIPhLA9-1658998815223)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/056cfea019b548dd9caf68c83d4fec76~tplv-k3u1fbpfcp-watermark.image?)]
function moveParticle(particle) {
// Calculate direction from noise
const angle =
simplex.noise2D(particle.x * FREQUENCY, particle.y * FREQUENCY) * AMPLITUDE;
// Update the velocity of the particle
// based on the direction
particle.vx += Math.cos(angle) * STEP;
particle.vy += Math.sin(angle) * STEP;
// Move the particle
particle.x += particle.vx;
particle.y += particle.vy;
// Use damping to slow down the particle (think friction)
particle.vx *= DAMPING;
particle.vy *= DAMPING;
particle.line.push([particle.x, particle.y]);
}
第三步:追踪粒子的位置,以追踪其路径;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lxzMUEid-1658998815223)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4a2afcbc342d4a0abd42264146c7058a~tplv-k3u1fbpfcp-watermark.image?)]
添加更多的粒子和颜色,我们就可以获得流场;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wvp7CNao-1658998815224)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2b4c339650924f69a14ccd2b38e0ce0b~tplv-k3u1fbpfcp-watermark.image?)]
粒子系统
说到粒子,噪声也出现在粒子系统中。对于像雨、雪或五彩纸屑等效果,你希望模拟一个看起来很自然的运动。想象一个五彩纸屑颗粒飘向地面。粒子不会直线移动。由于空气阻力,它们会漂浮和摆动。噪声是添加仿自然形态非常好的工具。
const wiggle = {
x: simplex.noise2D(particle.x * frequency, time) * amplitude,
y: simplex.noise2D(particle.y * frequency, time) * amplitude,
};
particle.x += wiggle.x;
particle.y += wiggle.y;
想深入了解,详情请看:https://varun.ca/confetti/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zy2aBcCM-1658998815224)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d18b835a4c3d4dd48af6f971f56be623~tplv-k3u1fbpfcp-watermark.image?)]
着色器
让我们回到那些动画斑点。使用着色器操作和设置3D动画几何图形的效率要高得多。但是着色器本身是一个完全不同的世界。对于初学者来说,有一个特殊的 WebGL 版本的噪音,gsl-noise。我们需要使用 glslify 将它导入到我们的着色器中。
我们会慢慢来,先从 2D 开始。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VNsYzxfD-1658998815225)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d365e65cb4f4a61b61a4eec4b30768f~tplv-k3u1fbpfcp-watermark.image?)]
上面的动画与我们之前看到的轮廓非常相似。它是通过片段着色器实现的。这意味着我们为canvas的每个像素运行相同的程序。
注意到pragma
行了吗?那是我们导入 webgl-noise。然后用它为每个像素位置 vUv
生成噪声数据。
这部分现在看起来应该很熟悉了。
fragment.glsl
precision highp float;
uniform float time;
uniform float density;
varying vec2 vUv;
#define PI 3.141592653589793
#pragma glslify: noise = require(glsl-noise/simplex/3d);
float patternZebra(float v){
float d = 1.0 / density;
float s = -cos(v / d * PI * 2.);
return smoothstep(.0, .1 * d, .1 * s / fwidth(s));
}
void main() {
// Generate noise data float amplitude = 1.0; float frequency = 1.5; float noiseValue = noise(vec3(vUv * frequency, time)) * amplitude; // Convert noise data to rings
float t = patternZebra(noiseValue);
vec3 color = mix(vec3(1.,0.4,0.369), vec3(0.824,0.318,0.369), t);
// Clip the rings to a circle
float dist = length(vUv - vec2(0.5, 0.5));
float alpha = smoothstep(0.250, 0.2482, dist);
gl_FragColor = vec4(color, alpha);
}
片段着色器具有用于绘制形状的距离函数的概念。 patternZebra 就是一个将噪声数据转换为环的示例。本质上,它返回 0 或 1。然后我们用它来选择混合的颜色。最后,使用另一个距离函数来剪裁圆形边界。
看起来很基础?
好吧,着色器的惊人之处在于,你可以将它们变成一种材质并将其应用于更复杂的几何体。
噪声材质
我们可以从上面获取相同的 2D mars 着色器。将其转换为 ThreeJS 着色器材质。然后使用顶点位置 vPosition 代替 vUv 生成噪声。 瞧,我们有一个 3D mars!
const material = new THREE.ShaderMaterial({
extensions: {
derivatives: true,
},
uniforms: {
time: { value: 0 },
density: { value: 1.0 },
},
vertexShader: /*glsl*/ ` varying vec3 vPosition; void main () { vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `,
fragmentShader: glslify(/* glsl */ ` precision highp float; varying vec3 vPosition; uniform float time; uniform float density; #pragma glslify: noise = require(glsl-noise/simplex/4d); #define PI 3.141592653589793 float patternZebra(float v){ float d = 1.0 / density; float s = -cos(v / d * PI * 2.); return smoothstep(.0, .1 * d, .1 * s / fwidth(s)); } void main () { float frequency = .6; float amplitude = 1.5; float v = noise(vec4(vPosition * frequency, sin(PI * time))) * amplitude; float t = patternZebra(v); vec3 fragColor = mix(vec3(1.,0.4,0.369), vec3(0.824,0.318,0.369), t); gl_FragColor = vec4(fragColor, 1.); } `),
});
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ftgOt9Bn-1658998815225)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/35281e84fe85477292ad62c73187d207~tplv-k3u1fbpfcp-watermark.image?)]
老实说,这只是一个开始。着色器开辟了许多可能性。就像下面的例子
不仅是噪声映射到颜色,还可以是透明度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQOqQDy2-1658998815225)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/891b024e400f47409da1ce0b7744afbb~tplv-k3u1fbpfcp-watermark.image?)]
光泽斑点
与顶点置换背后的想法非常相似。我们编写了一个顶点着色器来转换球体的每个顶点,而不是片段着色器。同样,使用着色器材质应用。
vertex.glsl
precision highp float;
varying vec3 vNormal;
#pragma glslify: snoise4 = require(glsl-noise/simplex/4d)
uniform float u_time;
uniform float u_amplitude;
uniform float u_frequency;
void main () {
vNormal = normalMatrix * normalize(normal); float distortion = snoise4(vec4(normal * u_frequency, u_time)) * u_amplitude; vec3 newPosition = position + (normal * distortion);
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
加上一些 matcap,你就会得到一些有光泽的动画斑点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMCV3GZg-1658998815226)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8b71c2607dce4f929ab924f6225d434d~tplv-k3u1fbpfcp-watermark.image?)]
轮到你创造噪音了
那真是一段旅程。运动、纹理、场、粒子系统和置换——噪声都能做。它确实是创意编码世界的主力军。
很明显,你想玩噪音😁
我已经为你准备了一个小小的入门 CodePen。它旋转了一个噪声网格并将其映射到一个字形数组。只需从修改数组开始,看看你能走多远。
const glyphs = ['¤', '✳', '●', '◔', '○', '◕', '◐', '◑', '◒'];
或者,如果您正在寻找更高级的东西,请查看noisedeck.app。它就像一个只用于视觉效果的合成器。