我的 perlin 噪声函数有什么问题?
What is wrong with my perlin noise function?
所以我目前正在研究 perlin 噪声发生器,由于某种原因我得到了不正确的输出:
柏林噪声在 "grid" 中,与传统的柏林噪声不同。
这是我的代码:
var perlinNoise = {
vectors: [{x:1,y:0},{x:1,y:-1},{x:0,y:-1},{x:-1,y:-1},{x:-1,y:0},{x:-1,y:1},{x:0,y:1},{x:1,y:1}],
fade: function(t){
return t * t * t * (t * (t * 6 - 15) + 10);
},
pointToCell: function(x, y){
cellX = Math.floor(x);
cellY = Math.floor(y);
return {x:cellX, y:cellY};
},
cellToVectors: function(cellX, cellY){
halfCell = .5;
//I use the four intercardinal directions to label the vectors.
//The random values are multiplied by 8 to map them to the 8 entries of the vectors array.
NEvector = this.vectors[Math.floor(randomFromCoords(cellX + halfCell, cellY + halfCell)*8)];
SEvector = this.vectors[Math.floor(randomFromCoords(cellX + halfCell, cellY - halfCell)*8)];
SWvector = this.vectors[Math.floor(randomFromCoords(cellX - halfCell, cellY - halfCell)*8)];
NWvector = this.vectors[Math.floor(randomFromCoords(cellX - halfCell, cellY + halfCell)*8)];
return {NE: NEvector, SE: SEvector, SW: SWvector, NW: NWvector};
},
dotProduct: function(vector1, vector2){
//Another way to calculate the dot product. This is more performance friendly than cosine calculations.
return vector1.x * vector2.x + vector1.y * vector2.y;
},
lerp: function(value1, value2, t){
return (1 - t) * value1 + t * value2;
},
perlinNoise: function(x, y){
var cellCoord = this.pointToCell(x, y);
//Get the positions of the x and y coordinants relative to the cell
var Xoffset = this.fade(x - cellCoord.x);
var Yoffset = this.fade(y - cellCoord.y);
var vectors = this.cellToVectors(cellCoord.x, cellCoord.y);
//The offset from each corner is calculated.
//Then the dotproduct between the offset vector and the random vector is calculated.
var NEoffset = {x: 1 - Xoffset, y: 1 - Yoffset};
var NEdotProduct = this.dotProduct(NEoffset, vectors.NE);
var SEoffset = {x: 1 - Xoffset, y: Yoffset};
var SEdotProduct = this.dotProduct(SEoffset, vectors.SE);
var SWoffset = {x: Xoffset, y: Yoffset};
var SWdotProduct = this.dotProduct(SWoffset, vectors.SW);
var NWoffset = {x: Xoffset, y: 1 - Yoffset};
var NWdotProduct = this.dotProduct(NWoffset, vectors.NW);
var Nlerp = this.lerp(NWdotProduct, NEdotProduct, Xoffset);
var Slerp = this.lerp(SWdotProduct, SEdotProduct, Xoffset);
var finalValue = this.lerp(Slerp, Nlerp, Yoffset);
if(finalValue < -.5 ){
console.log("less than -.5");
}
return finalValue;
},
noise: function(width, height){
var values = [];
for (var y = 0; y < height; ++y) {
for (var x = 0; x < width; ++x) {
values[x + y * width] = (this.perlinNoise(x/30,y/30)+1)/2;
}
}
return values;
},
element: document.getElementById("PerlinDisplay1"),
loop: function (){},
initialize: function(){
initCanvas("PerlinDisplay1Canvas");
var values = this.noise(canvas.width, canvas.height);
//maps values from 0 to 1 to grey scale pixels, already tested.
var valuesAsPixels = values.map(x => ValueToPixel(x));
UpdateCanvas(valuesAsPixels);
},
deinitialize: function(){}
}
函数 initCanvas、ValueToPixel 和 UpdateCanvas 有效,并且已经在其他用例中进行了测试。对于我做错了什么,我真的很感激。
编辑:根据要求,我正在添加函数 ValueToPixel 和 UpdateCanvas。
function UpdateCanvas(pixels){
var n = 0;
for (var y = 0; y < canvas.height; ++y) {
for (var x = 0; x < canvas.width; ++x) {
var index = (y * canvas.width + x) * 4;
for(var channel = 0; channel < 4; ++channel){
data[index + channel] = pixels[index/4][channel];
n++;
}
}
}
context.putImageData(imageData, 0, 0);
}
function ValueToPixel(value){
//red, green, blue, alpha
return [value*255, value*255, value*255, 255];
}
我想通了!我错误地计算了角落的偏移量。我错误地计算了到角落的距离的绝对值,而不是到角落的偏移量。下面是更正后的代码片段:
//The offset from each corner is calculated.
//Then the dotproduct between the offset vector and the random vector is calculated.
var NEoffset = {x: Xoffset - 1, y: Yoffset - 1};
var NEdotProduct = this.dotProduct(NEoffset, vectors.NE);
var SEoffset = {x: Xoffset - 1, y: Yoffset};
var SEdotProduct = this.dotProduct(SEoffset, vectors.SE);
var SWoffset = {x: Xoffset, y: Yoffset};
var SWdotProduct = this.dotProduct(SWoffset, vectors.SW);
var NWoffset = {x: Xoffset, y: Yoffset - 1};
var NWdotProduct = this.dotProduct(NWoffset, vectors.NW);
请注意,不是从 1 中减去偏移量,而是从偏移量中减去 1。
所以我目前正在研究 perlin 噪声发生器,由于某种原因我得到了不正确的输出:
柏林噪声在 "grid" 中,与传统的柏林噪声不同。 这是我的代码:
var perlinNoise = {
vectors: [{x:1,y:0},{x:1,y:-1},{x:0,y:-1},{x:-1,y:-1},{x:-1,y:0},{x:-1,y:1},{x:0,y:1},{x:1,y:1}],
fade: function(t){
return t * t * t * (t * (t * 6 - 15) + 10);
},
pointToCell: function(x, y){
cellX = Math.floor(x);
cellY = Math.floor(y);
return {x:cellX, y:cellY};
},
cellToVectors: function(cellX, cellY){
halfCell = .5;
//I use the four intercardinal directions to label the vectors.
//The random values are multiplied by 8 to map them to the 8 entries of the vectors array.
NEvector = this.vectors[Math.floor(randomFromCoords(cellX + halfCell, cellY + halfCell)*8)];
SEvector = this.vectors[Math.floor(randomFromCoords(cellX + halfCell, cellY - halfCell)*8)];
SWvector = this.vectors[Math.floor(randomFromCoords(cellX - halfCell, cellY - halfCell)*8)];
NWvector = this.vectors[Math.floor(randomFromCoords(cellX - halfCell, cellY + halfCell)*8)];
return {NE: NEvector, SE: SEvector, SW: SWvector, NW: NWvector};
},
dotProduct: function(vector1, vector2){
//Another way to calculate the dot product. This is more performance friendly than cosine calculations.
return vector1.x * vector2.x + vector1.y * vector2.y;
},
lerp: function(value1, value2, t){
return (1 - t) * value1 + t * value2;
},
perlinNoise: function(x, y){
var cellCoord = this.pointToCell(x, y);
//Get the positions of the x and y coordinants relative to the cell
var Xoffset = this.fade(x - cellCoord.x);
var Yoffset = this.fade(y - cellCoord.y);
var vectors = this.cellToVectors(cellCoord.x, cellCoord.y);
//The offset from each corner is calculated.
//Then the dotproduct between the offset vector and the random vector is calculated.
var NEoffset = {x: 1 - Xoffset, y: 1 - Yoffset};
var NEdotProduct = this.dotProduct(NEoffset, vectors.NE);
var SEoffset = {x: 1 - Xoffset, y: Yoffset};
var SEdotProduct = this.dotProduct(SEoffset, vectors.SE);
var SWoffset = {x: Xoffset, y: Yoffset};
var SWdotProduct = this.dotProduct(SWoffset, vectors.SW);
var NWoffset = {x: Xoffset, y: 1 - Yoffset};
var NWdotProduct = this.dotProduct(NWoffset, vectors.NW);
var Nlerp = this.lerp(NWdotProduct, NEdotProduct, Xoffset);
var Slerp = this.lerp(SWdotProduct, SEdotProduct, Xoffset);
var finalValue = this.lerp(Slerp, Nlerp, Yoffset);
if(finalValue < -.5 ){
console.log("less than -.5");
}
return finalValue;
},
noise: function(width, height){
var values = [];
for (var y = 0; y < height; ++y) {
for (var x = 0; x < width; ++x) {
values[x + y * width] = (this.perlinNoise(x/30,y/30)+1)/2;
}
}
return values;
},
element: document.getElementById("PerlinDisplay1"),
loop: function (){},
initialize: function(){
initCanvas("PerlinDisplay1Canvas");
var values = this.noise(canvas.width, canvas.height);
//maps values from 0 to 1 to grey scale pixels, already tested.
var valuesAsPixels = values.map(x => ValueToPixel(x));
UpdateCanvas(valuesAsPixels);
},
deinitialize: function(){}
}
函数 initCanvas、ValueToPixel 和 UpdateCanvas 有效,并且已经在其他用例中进行了测试。对于我做错了什么,我真的很感激。
编辑:根据要求,我正在添加函数 ValueToPixel 和 UpdateCanvas。
function UpdateCanvas(pixels){
var n = 0;
for (var y = 0; y < canvas.height; ++y) {
for (var x = 0; x < canvas.width; ++x) {
var index = (y * canvas.width + x) * 4;
for(var channel = 0; channel < 4; ++channel){
data[index + channel] = pixels[index/4][channel];
n++;
}
}
}
context.putImageData(imageData, 0, 0);
}
function ValueToPixel(value){
//red, green, blue, alpha
return [value*255, value*255, value*255, 255];
}
我想通了!我错误地计算了角落的偏移量。我错误地计算了到角落的距离的绝对值,而不是到角落的偏移量。下面是更正后的代码片段:
//The offset from each corner is calculated.
//Then the dotproduct between the offset vector and the random vector is calculated.
var NEoffset = {x: Xoffset - 1, y: Yoffset - 1};
var NEdotProduct = this.dotProduct(NEoffset, vectors.NE);
var SEoffset = {x: Xoffset - 1, y: Yoffset};
var SEdotProduct = this.dotProduct(SEoffset, vectors.SE);
var SWoffset = {x: Xoffset, y: Yoffset};
var SWdotProduct = this.dotProduct(SWoffset, vectors.SW);
var NWoffset = {x: Xoffset, y: Yoffset - 1};
var NWdotProduct = this.dotProduct(NWoffset, vectors.NW);
请注意,不是从 1 中减去偏移量,而是从偏移量中减去 1。