噪波纹理分析(1):噪波函数
一、伪随机函数
随机函数用于生成随机值,通常希望给定同样的随机种子得到相同的随机值,称为伪随机函数。
1.1 1D噪声
输入1D参数生成1D的伪随机值
#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
#define mix(a, b, c) \
{ \
a -= c; \
a ^= rot(c, 4); \
c += b; \
b -= a; \
b ^= rot(a, 6); \
a += c; \
c -= b; \
c ^= rot(b, 8); \
b += a; \
a -= c; \
a ^= rot(c, 16); \
c += b; \
b -= a; \
b ^= rot(a, 19); \
a += c; \
c -= b; \
c ^= rot(b, 4); \
b += a; \
}
#define final(a, b, c) \
{ \
c ^= b; \
c -= rot(b, 14); \
a ^= c; \
a -= rot(c, 11); \
b ^= a; \
b -= rot(a, 25); \
c ^= b; \
c -= rot(b, 16); \
a ^= c; \
a -= rot(c, 4); \
b ^= a; \
b -= rot(a, 14); \
c ^= b; \
c -= rot(b, 24); \
}
uint hash_uint(uint kx)
{
uint a, b, c;
a = b = c = 0xdeadbeefu + (1u << 2u) + 13u;
a += kx;
final(a, b, c);
return c;
}
float hash_uint_to_float(uint kx)
{
return float(hash_uint(kx)) / float(0xFFFFFFFFu);
}
输入2D参数生成1D的伪随机值
uint hash_uint2(uint kx, uint ky)
{
uint a, b, c;
a = b = c = 0xdeadbeefu + (2u << 2u) + 13u;
b += ky;
a += kx;
final(a, b, c);
return c;
}
float hash_uint2_to_float(uint kx, uint ky)
{
return float(hash_uint2(kx, ky)) / float(0xFFFFFFFFu);
}
输入3D参数生成1D的伪随机值
uint hash_uint3(uint kx, uint ky, uint kz)
{
uint a, b, c;
a = b = c = 0xdeadbeefu + (3u << 2u) + 13u;
c += kz;
b += ky;
a += kx;
final(a, b, c);
return c;
}
float hash_uint3_to_float(uint kx, uint ky, uint kz)
{
return float(hash_uint3(kx, ky, kz)) / float(0xFFFFFFFFu);
}
1.2 2D噪声
输入1D参数生成2D的伪随机值
float hash_float_to_float(float k)
{
return hash_uint_to_float(floatBitsToUint(k));
}
float hash_vec2_to_float(float2 k)
{
return hash_uint2_to_float(floatBitsToUint(k.x), floatBitsToUint(k.y));
}
vector2 hash_float_to_vector2(float k)
{
return vector2(hash_float_to_float(k), hash_vector2_to_float(vector2(k, 1.0)));
}
输入2D参数生成2D的伪随机值
vector2 hash_int2_to_vector2(int2 k)
{
int2 v = k * 1664525 + 1013904223;
v.x += v.y * 1664525;
v.y += v.x * 1664525;
v = v ^ (v >> 16);
v.x += v.y * 1664525;
v.y += v.x * 1664525;
vector2 f = int2_to_vec2(v & 0x7FFFFFFF);
return f * (1.0 / (float)0x7FFFFFFF);
}
float hash_vec3_to_float(float3 k)
{
return hash_uint3_to_float(floatBitsToUint(k.x), floatBitsToUint(k.y), floatBitsToUint(k.z));
}
vector2 hash_vector2_to_vector2(vector2 k)
{
return vector2(hash_vector2_to_float(k), hash_vector3_to_float(vector3(k.x, k.y, 1.0)));
}
输入3D参数生成2D的伪随机值
vector2 hash_vector3_to_vector2(vector3 k)
{
return vector2(hash_vector3_to_float(vector3(k.x, k.y, k.z)),
hash_vector3_to_float(vector3(k.z, k.x, k.y)));
}
1.3 3D噪声
输入1D参数生成3D的伪随机值
vector3 hash_float_to_vector3(float k)
{
return vector3(hash_float_to_float(k),
hash_vector2_to_float(vector2(k, 1.0)),
hash_vector2_to_float(vector2(k, 2.0)));
}
输入2D参数生成3D的伪随机值
vector3 hash_vector2_to_vector3(vector2 k)
{
return vector3(hash_vector2_to_float(k),
hash_vector3_to_float(vector3(k.x, k.y, 1.0)),
hash_vector3_to_float(vector3(k.x, k.y, 2.0)));
}
输入3D参数生成3D的伪随机值
vector3 hash_int3_to_vector3(int3 k)
{
int3 v = k * 1664525 + 1013904223;
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;
v = v ^ (v >> 16);
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;
vector3 f = int3_to_vec3(v & 0x7FFFFFFF);
return f * (1.0 / (float)0x7FFFFFFF);
}
vector3 hash_vector3_to_vector3(vector3 k)
{
return vector3(hash_vector3_to_float(k),
hash_vector4_to_float(vector4(k[0], k[1], k[2], 1.0)),
hash_vector4_to_float(vector4(k[0], k[1], k[2], 2.0)));
}
二、噪声函数
自然界存在各种各样的噪声,因此渲染材质中添加噪声会使得模型表现得更加自然、更加柔和。噪声函数可以分为两类:一是基于晶格的方法,包括梯度噪声、值噪声;二是基于点的方法,包括worley噪声。
2.1 Perlin噪声
Perlin噪声属于梯度噪声的一种。梯度噪声可能是生成噪声(一种主要能量集中在低频的随机平滑信号)最便捷的方法,适用于程序化纹理/着色、建模和动画。它比数值噪声更平滑,质量更高,但当然成本也略高。其原理是在整个平面上创建一个虚拟网格/格子,并为网格中的每个顶点分配一个随机向量。当在平面上的任意一点查询/请求噪声值时,将确定执行查询的网格单元,确定网格的四个顶点,并获取它们的随机向量。然后,用该顶点的随机向量对当前评估点相对于每个顶点的位置进行点缀(投影),并使用平滑插值对结果进行双线性插值。
2.1.1 1D Perlin噪声
输入1D参数,输出噪声值
#define FLOORFRAC(x, x_int, x_fract) { float x_floor = floor(x); x_int = int(x_floor); x_fract = x - x_floor; }
float bi_mix(float v0, float v1, float v2, float v3, float x, float y)
{
float x1 = 1.0f - x;
return (1.0f - y) * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x);
}
float negate_if(float value, uint condition)
{
return (condition != 0u) ? -value : value;
}
float fade(float t)
{
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
}
float noise_grad(uint hash, float x)
{
uint h = hash & 15u;
float g = 1u + (h & 7u);
return negate_if(g, h & 8u) * x;
}
float noise_perlin(float2 vec)
{
int X, Y;
float fx, fy;
FLOORFRAC(vec.x, X, fx);
FLOORFRAC(vec.y, Y, fy);
float u = fade(fx);
float v = fade(fy);
float r = bi_mix(noise_grad(hash_int2(X, Y), fx, fy),
noise_grad(hash_int2(X + 1, Y), fx - 1.0f, fy),
noise_grad(hash_int2(X, Y + 1), fx, fy - 1.0f),
noise_grad(hash_int2(X + 1, Y + 1), fx - 1.0f, fy - 1.0f),
u,
v);
return r;
}
2.1.2 2D Perlin噪声
输入2D参数,输出噪声值
float noise_grad(uint hash, float x, float y)
{
uint h = hash & 7u;
float u = h < 4u ? x : y;
float v = 2.0f * (h < 4u ? y : x);
return negate_if(u, h & 1u) + negate_if(v, h & 2u);
}
float noise_perlin(float2 vec)
{
int X, Y;
float fx, fy;
FLOORFRAC(vec.x, X, fx);
FLOORFRAC(vec.y, Y, fy);
float u = fade(fx);
float v = fade(fy);
float r = bi_mix(noise_grad(hash_int2(X, Y), fx, fy),
noise_grad(hash_int2(X + 1, Y), fx - 1.0f, fy),
noise_grad(hash_int2(X, Y + 1), fx, fy - 1.0f),
noise_grad(hash_int2(X + 1, Y + 1), fx - 1.0f, fy - 1.0f),
u,
v);
return r;
}

2.1.3 3D Perlin噪声
输入3D参数,输出噪声值
float tri_mix(float v0,
float v1,
float v2,
float v3,
float v4,
float v5,
float v6,
float v7,
float x,
float y,
float z)
{
float x1 = 1.0f - x;
float y1 = 1.0f - y;
float z1 = 1.0f - z;
return z1 * (y1 * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x)) +
z * (y1 * (v4 * x1 + v5 * x) + y * (v6 * x1 + v7 * x));
}
float noise_perlin(float3 vec)
{
int X, Y, Z;
float fx, fy, fz;
FLOORFRAC(vec.x, X, fx);
FLOORFRAC(vec.y, Y, fy);
FLOORFRAC(vec.z, Z, fz);
float u = fade(fx);
float v = fade(fy);
float w = fade(fz);
float r = tri_mix(noise_grad(hash_int3(X, Y, Z), fx, fy, fz),
noise_grad(hash_int3(X + 1, Y, Z), fx - 1, fy, fz),
noise_grad(hash_int3(X, Y + 1, Z), fx, fy - 1, fz),
noise_grad(hash_int3(X + 1, Y + 1, Z), fx - 1, fy - 1, fz),
noise_grad(hash_int3(X, Y, Z + 1), fx, fy, fz - 1),
noise_grad(hash_int3(X + 1, Y, Z + 1), fx - 1, fy, fz - 1),
noise_grad(hash_int3(X, Y + 1, Z + 1), fx, fy - 1, fz - 1),
noise_grad(hash_int3(X + 1, Y + 1, Z + 1), fx - 1, fy - 1, fz - 1),
u,
v,
w);
return r;
}
2.2 Voronoi噪声
1996年,Worley在其论文中提出了一种基于点的噪波生成算法。首先将像素划分为相同大小的网格,然后在网格中布置随机位置的特征点,每个格子的像素点依次遍历周围9个格子的特征点计算得到最短距离,最后将最短距离根据网格大小归一化输出。
2.2.1 F1模式
-
算法原理:F1计算输入点到最近的特征点(即Voronoi种子点)的距离,并输出该距离值(Distance)、特征点的位置(Position)以及随机颜色(Color)。其核心是通过遍历所有种子点,找到距离输入点最近的一个,并计算欧几里得(或其他距离度量)距离。
-
数学表达:
F1(x)=minpi∈Pd(x,pi),其中 P是种子点集合,d是距离函数(如欧几里得距离)。
-
应用场景:生成标准的Voronoi细胞结构,适用于模拟晶体、细胞等硬边界效果。
#define float4 vec4 #define int2 ivec2 #define int3 ivec3 #define FLT_MAX 1e20 struct VoronoiParams { float scale; float detail; float roughness; float lacunarity; float smoothness; float exponent; float randomness; float max_distance; bool normalize; int feature; int metric; }; struct VoronoiOutput { float Distance; float3 Color; float4 Position; }; float voronoi_distance(float2 a, float2 b, VoronoiParams params) { if (params.metric==0) { return length(a- b); } else if (params.metric==1) { return abs(a.x - b.x) + abs(a.y - b.y); } else if (params.metric==2) { return max(abs(a.x - b.x), abs(a.y - b.y)); } else if (params.metric==3) { return pow(pow(abs(a.x - b.x), params.exponent) + pow(abs(a.y - b.y), params.exponent), 1.0f / params.exponent); } else { return 0.0f; } } int2 hash_pcg2d_i(int2 v) { v = v * 1664525 + 1013904223; v.x += v.y * 1664525; v.y += v.x * 1664525; v = v ^ (v >> 16); v.x += v.y * 1664525; v.y += v.x * 1664525; return v; } float2 hash_int2_to_vec2(int2 k) { int2 h = hash_pcg2d_i(k); return float2(h & 0x7fffffff) * (1.0 / float(0x7fffffff)); } int3 hash_pcg3d_i(int3 v) { v = v * 1664525 + 1013904223; v.x += v.y * v.z; v.y += v.z * v.x; v.z += v.x * v.y; v = v ^ (v >> 16); v.x += v.y * v.z; v.y += v.z * v.x; v.z += v.x * v.y; return v; } float3 hash_int3_to_vec3(int3 k) { int3 h = hash_pcg3d_i(k); return float3(h & 0x7fffffff) * (1.0 / float(0x7fffffff)); } float4 voronoi_position(float2 coord) { return float4(coord.x, coord.y, 0.0f, 0.0f); } VoronoiOutput voronoi_f1(VoronoiParams params, float2 coord) { float2 cellPosition_f = floor(coord); float2 localPosition = coord - cellPosition_f; int2 cellPosition = int2(cellPosition_f); float minDistance = FLT_MAX; int2 targetOffset = int2(0); float2 targetPosition = float2(0.0f); for (int j = -1; j <= 1; j++) { for (int i = -1; i <= 1; i++) { int2 cellOffset = int2(i, j); float2 pointPosition = float2(cellOffset) + hash_int2_to_vec2(cellPosition + cellOffset) * params.randomness; float distanceToPoint = voronoi_distance(pointPosition, localPosition, params); if (distanceToPoint < minDistance) { targetOffset = cellOffset; minDistance = distanceToPoint; targetPosition = pointPosition; } } } VoronoiOutput octave; octave.Distance = minDistance; octave.Color = hash_int3_to_vec3(int3(cellPosition + targetOffset,0)); octave.Position = voronoi_position(targetPosition + cellPosition_f); return octave; } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord/iResolution.xy; VoronoiParams param1; param1.randomness=1.0; param1.scale=clamp(10.0,0.1,150.0); param1.detail=clamp(0.5,0.0,16.0); param1.roughness=clamp(0.5,0.0,1.0); param1.lacunarity=clamp(2.0,1.0,3.0); param1.max_distance=clamp(1.0,0.1,10.0); param1.normalize=false; param1.metric=0; float col=voronoi_f1(param1,uv*param1.scale).Distance; // Output to screen fragColor = vec4(vec3(col),1.0); }

2.2.2 F2模式
-
算法原理:F2计算输入点到第二近的特征点的距离,同时也会输出该点的位置和颜色。算法过程类似于F1,但需维护两个最小距离值,最终返回次小值。
-
数学表达:
F2(x)=minpi∈P∖{pF1}d(x,pi),其中 pF1是F1模式找到的最近点。
-
应用场景:用于生成更复杂的纹理效果,如双重边缘或混合层次的结构。
VoronoiOutput voronoi_f2(VoronoiParams params, float2 coord) { float2 cellPosition_f = floor(coord); float2 localPosition = coord - cellPosition_f; int2 cellPosition = int2(cellPosition_f); float distanceF1 = FLT_MAX; float distanceF2 = FLT_MAX; int2 offsetF1 = int2(0); float2 positionF1 = float2(0.0f); int2 offsetF2 = int2(0); float2 positionF2 = float2(0.0f); for (int j = -1; j <= 1; j++) { for (int i = -1; i <= 1; i++) { int2 cellOffset = int2(i, j); float2 pointPosition = float2(cellOffset) + hash_int2_to_vec2(cellPosition + cellOffset) * params.randomness; float distanceToPoint = voronoi_distance(pointPosition, localPosition, params); if (distanceToPoint < distanceF1) { distanceF2 = distanceF1; distanceF1 = distanceToPoint; offsetF2 = offsetF1; offsetF1 = cellOffset; positionF2 = positionF1; positionF1 = pointPosition; } else if (distanceToPoint < distanceF2) { distanceF2 = distanceToPoint; offsetF2 = cellOffset; positionF2 = pointPosition; } } } VoronoiOutput octave; octave.Distance = distanceF2; octave.Color = hash_int2_to_vec3(cellPosition + offsetF2); octave.Position = voronoi_position(positionF2 + cellPosition_f); return octave; }

2.2.3 Smooth F1模式
-
算法原理:Smooth F1是对F1的平滑化改进,通过加权平均多个邻近点的距离值,消除F1的硬边界不连续性。具体实现可能采用类似“平滑最小值”(smooth minimum)的函数(如指数或幂次加权),使得过渡更自然。
-
数学方法:
例如,使用指数衰减加权:
Smooth F1(x)=−k1log∑pi∈Pe−k⋅d(x,pi),其中 k控制平滑强度。
-
应用场景:适合需要柔和过渡的效果,如生物组织或流体模拟。
VoronoiOutput voronoi_smooth_f1(VoronoiParams params, float2 coord) { float2 cellPosition_f = floor(coord); float2 localPosition = coord - cellPosition_f; int2 cellPosition = int2(cellPosition_f); float smoothDistance = 0.0f; float3 smoothColor = float3(0.0f); float2 smoothPosition = float2(0.0f); float h = -1.0f; for (int j = -2; j <= 2; j++) { for (int i = -2; i <= 2; i++) { int2 cellOffset = int2(i, j); float2 pointPosition = float2(cellOffset) + hash_int2_to_vec2(cellPosition + cellOffset) * params.randomness; float distanceToPoint = voronoi_distance(pointPosition, localPosition, params); h = h == -1.0f ? 1.0f : smoothstep(0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / params.smoothness); float correctionFactor = params.smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; correctionFactor /= 1.0f + 3.0f * params.smoothness; float3 cellColor = hash_int3_to_vec3(int3(cellPosition + cellOffset,0)); smoothColor = mix(smoothColor, cellColor, h) - correctionFactor; smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor; } } VoronoiOutput octave; octave.Distance = smoothDistance; octave.Color = smoothColor; octave.Position = voronoi_position(cellPosition_f + smoothPosition); return octave; }

2.2.4 Distance to Edge模式
- 算法原理:计算输入点到Voronoi细胞边界的最近距离。算法需先找到最近(F1)和第二近(F2)的种子点,然后计算到两者中垂线(即Voronoi边)的距离。对于欧几里得距离,公式为:
Distance to Edge(x)=2d(x,pF1)−d(x,pF2)。
-
特性:结果为正值表示点在细胞内部,负值表示超出边界(需结合绝对值使用)。
-
应用场景:用于生成边缘高光或描边效果,如金属锤击纹理或卡通水面的波纹。
float voronoi_distance_to_edge(VoronoiParams params, float2 coord) { float2 cellPosition_f = floor(coord); float2 localPosition = coord - cellPosition_f; int2 cellPosition = int2(cellPosition_f); float2 vectorToClosest = float2(0.0f); float minDistance = FLT_MAX; for (int j = -1; j <= 1; j++) { for (int i = -1; i <= 1; i++) { int2 cellOffset = int2(i, j); float2 vectorToPoint = float2(cellOffset) + hash_int2_to_vec2(cellPosition + cellOffset) * params.randomness - localPosition; float distanceToPoint = dot(vectorToPoint, vectorToPoint); if (distanceToPoint < minDistance) { minDistance = distanceToPoint; vectorToClosest = vectorToPoint; } } } minDistance = FLT_MAX; for (int j = -1; j <= 1; j++) { for (int i = -1; i <= 1; i++) { int2 cellOffset = int2(i, j); float2 vectorToPoint = float2(cellOffset) + hash_int2_to_vec2(cellPosition + cellOffset) * params.randomness - localPosition; float2 perpendicularToEdge = vectorToPoint - vectorToClosest; if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0f, normalize(perpendicularToEdge)); minDistance = min(minDistance, distanceToEdge); } } } return minDistance; }

2.2.4 N-Sphere Radius模式
- 算法原理:N-Sphere Radius表示Voronoi单元内可容纳的最大n维球体的半径。对于3D空间,它是一个内切球体;对于2D空间,则是一个内切圆。
该半径等于最近种子点(F1)与次近种子点(F2)之间距离的一半,即:
sphere_radius=2d(pF1,pF2),其中 pF1和 pF2分别是输入点的最近和第二近的Voronoi种子点。
-
特性:该值描述了Voronoi单元的“紧密程度”,半径越大,说明单元内部空间越宽松。。
-
应用场景:通过N-Sphere Radius可以生成紧密排列的球体或气泡状纹理,适用于模拟生物细胞、泡沫或金属颗粒等结构。
float voronoi_n_sphere_radius(VoronoiParams params, float2 coord)
{
float2 cellPosition_f = floor(coord);
float2 localPosition = coord - cellPosition_f;
int2 cellPosition = int2(cellPosition_f);
float2 closestPoint = float2(0.0f);
int2 closestPointOffset = int2(0);
float minDistance = FLT_MAX;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
int2 cellOffset = int2(i, j);
float2 pointPosition = float2(cellOffset) +
hash_int2_to_vec2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
minDistance = FLT_MAX;
float2 closestPointToClosestPoint = float2(0.0f);
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
if (i == 0 && j == 0) {
continue;
}
int2 cellOffset = int2(i, j) + closestPointOffset;
float2 pointPosition = float2(cellOffset) +
hash_int2_to_vec2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0f;
}


浙公网安备 33010602011771号