本文已参与[新人创作礼]活动,一起开启掘金创作之路
本文禁止转载,如需转载请注明出处。
图形学的数学基础(四十二):噪声-下
在上一章中,我们介绍并解释了实现一维噪声函数的大部分技术和方法。创建更高维度的噪声和一维噪声并没有本质上的区别,因为它们都是基于同样的方法和技术。之前提到过,所有的噪声函数都会返回一个浮点数,无论输入参数是浮点数,二维点还是三维点,至于一维噪声 二维或三维噪声仅与其输入值有关。二维噪声是将二维点作为输入参数的噪声函数。 对于二维噪声,我们会在网格顶点处分布随机值。噪声函数的2D版本以2D点作为输入,假设当前要求的点为。与一维版本类似,我们先得找到点在网格中的位置,如果点在网格之外,可以通过相同的取模技巧重新映射点的位置,得到网格上点的新坐标,记作。 之前的章节中讲过插值相关的很多内容。
如下图所示:点被一个单元上的四个顶点所包围。使用之前学到过的双线性插值技术可以轻松得到点的值(周围四个点的加权平均):
为此我们首先计算,它们是一维噪声中t的对应物:
代码实现如下:
function lerp(min:number, max: nmber, t: number) {
return min * (1-t) + max * t;
}
function smoothStep(t: number) {
return t * t * (3- 2 * t);
}
class vec2 {
public x: number;
public y: number;
constructor(x: number,y: number) {
this.x = x;
this.y = y;
}
}
class ValueNoise {
public K_MAX_TABLE_SIZE: number;
public K_MAX_TABLE_SIZE_MASK: number;
public vertices: number[];
constructor(){
this.K_MAX_TABLE_SIZE = 256;
this.K_MAX_TABLE_SIZE_MASK = this.K_MAX_TABLE_SIZE - 1;
this.vertices = [];
for (let i = 0; i < this.K_MAX_TABLE_SIZE * this.K_MAX_TABLE_SIZE; i++) {
this.vertices[i] = Math.random();
}
}
computeX(p: vec2) {
const xi = Math.floor(p.x);
const yi = Math.floor(p.y);
const s = p.x - xi;
const t = p.y - yi;
const rx0 = xi & this.K_MAX_TABLE_SIZE_MASK;
const rx1 = (rx0 + 1) & this.K_MAX_TABLE_SIZE_MASK;
const ry0 = yi & this.K_MAX_TABLE_SIZE_MASK;
const ry1 = (ry0 + 1) & this.K_MAX_TABLE_SIZE_MASK;
//通过置换表查找四个顶点处的随机值
const c00 = this.vertices[ry0 * this.K_MAX_TABLE_SIZE_MASK + rx0];
const c10 = this.vertices[ry0 * this.K_MAX_TABLE_SIZE_MASK + rx1];
const c01 = this.vertices[ry1 * this.K_MAX_TABLE_SIZE_MASK + rx0];
const c11 = this.vertices[ry1 * this.K_MAX_TABLE_SIZE_MASK + rx1];
//对得到的s和t做平滑处理
const ss = smoothStep(s);
const st = smoothStep(t);
//双线性插值得到p的值
const a = lerp(c00, c10, ss);
const b = lerp(c01, c11, ss);
return lerp(a, b, st);
}
}