É fácil criar uma imagem perfeita no Photoshop: corte a imagem, pegue as partes direita e inferior cortadas e cole-as na esquerda e na parte superior usando a ferramenta Fade. Mas, para a implementação adequada de mapas de ruído contínuos, é preciso pensar com cuidado.Se você tem um entendimento básico do ruído Perlin , sabe que ele consiste em números aleatórios interpolados. É usado principalmente em duas dimensões. Mas também é útil em uma dimensão (por exemplo, ao mover), em três dimensões (transformação cilíndrica e esférica de objetos 3D) e até em quatro ou cinco dimensões.O ruído quadridimensional pode ser usado para criar uma imagem 2D perfeita. Não é muito comum pensarmos em quatro dimensões; portanto, teremos uma dimensão de cada vez.Nos meus exemplos, usei ruído simplex de duas oitavas. O ruído simplex é mais rápido em grandes dimensões e, devido à sua natureza triangular, parece melhor.Eu escrevi uma pequena função drawNoise
para criar uma tela e processar uma matriz de pixels em um loop.Ruído contínuo unidimensional
Em uma dimensão, o ruído é uma linha suave infinita (minha implementação do ruído começa com duas, então eu uso uma constante como o segundo parâmetro). Aqui vemos que esses são apenas números aleatórios interpolados.
fNoiseScale = .02;
drawNoise(function(i,x,y){
var v = Simplex.noise(
123+x*fNoiseScale
,137
);
return v*iSize>y?255:0;
}).img();
Ruído unidimensionalVocê pode usar isso na animação, recalculando o valor do ruído a cada milissegundo, mas também pode criar um loop e calcular todos os valores antecipadamente. Os valores na imagem acima não passam pelas bordas. Mas implementar a repetibilidade é bastante simples, apenas mais uma dimensão e loop ... ou círculo, para isso.Loop unidimensional
Para a maioria de vocês, o ruído de Perlin se parece com a imagem abaixo.Se desenhávamos um círculo aqui e contássemos os valores de ruído nesse círculo, obteríamos um loop unidimensional.Ruído com um círculo para criar um loop unidimensional.No código, fica assim:
drawNoise(function(i,x,y){
var fNX = x/iSize
,fRdx = fNX*2*Math.PI
,a = fRdsSin*Math.sin(fRdx)
,b = fRdsSin*Math.cos(fRdx)
,v = Simplex.noise(
123+a*fNoiseScale
,132+b*fNoiseScale
)
;
return v*iSize>y?255:0;
}).img().div(2);
Você provavelmente já entendeu o que vamos fazer. Para fazer um loop em uma imagem bidimensional, precisamos de um mapa de ruído tridimensional (pelo menos).Cartão cilíndrico
Noise Perlin foi originalmente criado para texturas 3D contínuas (o filme "Tron"). O mapa da imagem não é uma folha de papel enrolada em torno de um objeto, mas é calculado por sua localização em um campo de ruído tridimensional. Portanto, ao cortar o objeto, ainda podemos calcular o mapa para a superfície recém-criada.Antes de atingirmos o objetivo final de uma imagem perfeita, primeiro criamos uma imagem que se junta perfeitamente à esquerda e à direita. Isso é semelhante a um círculo bidimensional para um loop unidimensional, mas com uma dimensão adicional: um cilindro.
drawNoise(function(i,x,y){
var fNX = x/iSize
,fRdx = fNX*2*Math.PI
,a = fRdsSin*Math.sin(fRdx)
,b = fRdsSin*Math.cos(fRdx)
,v = Simplex.noise(
123+a*fNoiseScale
,132+b*fNoiseScale
,312+y*fNoiseScale
)
;
return v*255<<0;
}).img().div(2);
Mapa de ruído cilíndricoImagem de mapa esférico
Você pode pensar que seria conveniente usar uma esfera para criar uma imagem perfeita, mas você está enganado.Farei uma pequena digressão e mostrarei como o mapa de imagem esférico é calculado e como ele é.
document.body.addChild('h2').innerText = 'three dimensional spherical map';
fNoiseScale = .1;
var oSpherical = drawNoise(function(i,x,y){
var fNX = (x+.5)/iSize
,fNY = (y+.5)/iSize
,fRdx = fNX*2*Math.PI
,fRdy = fNY*Math.PI
,fYSin = Math.sin(fRdy+Math.PI)
,a = fRdsSin*Math.sin(fRdx)*fYSin
,b = fRdsSin*Math.cos(fRdx)*fYSin
,c = fRdsSin*Math.cos(fRdy)
,v = Simplex.noise(
123+a*fNoiseScale
,132+b*fNoiseScale
,312+c*fNoiseScale
)
;
return v*255<<0;
}).img();
Mapa de ruído esféricoEsfera com ruídoMapa panorâmico cúbico
A esfera que criamos também pode ser usada como panorama se você colocar uma câmera no centro da esfera. Mas a melhor maneira seria usar um panorama cúbico, porque ele tem muito menos rostos. A esfera é projetada nos seis lados do cubo, como mostrado neste esboço.Aplicando uma esfera a um cuboPara cada pixel na superfície do cubo, precisamos calcular a interseção entre o ponto de vista C no centro e a esfera. Pode parecer complicado, mas na verdade é bem simples.Podemos considerar a linha CA como um vetor. E os vetores podem ser normalizados para que sua direção não mude, mas o comprimento diminui para 1. Devido a isso, todos os vetores juntos parecerão uma esfera.A normalização também é bastante simples, basta dividir os valores do vetor por xyz pelo comprimento total do vetor. O comprimento do vetor pode ser calculado usando o teorema de Pitágoras.No código abaixo, o cálculo da normalização é realizado primeiro em uma face. Em seguida, o ruído é calculado simultaneamente para todas as seis arestas, porque para obter a posição da próxima face, basta girar os valores ao longo de xyz.
document.body.addChild('h2').innerText = '3D panoramical cube map';
var mCubemap = document.createElement('canvas')
,iW = 6*iSize;
mCubemap.width = iW;
mCubemap.height = iSize;
var iHSize = iSize/2
,oCtx = mCubemap.getContext('2d')
,oImgData = oCtx.getImageData(0,0,iW,iSize)
,aPixels = oImgData.data
,aa = 123
,bb = 231
,cc = 321
;
for (var i=0,l=iSize*iSize;i<l;i++) {
var x = i%iSize
,y = (i/iSize)<<0
,a = -iHSize + x+.5
,b = -iHSize + y+.5
,c = -iHSize
,fDistanceAB = Math.sqrt(a*a+b*b)
,fDistanceABC = Math.sqrt(fDistanceAB*fDistanceAB+c*c)
,fDrds = .5*fDistanceABC
,v = 1
;
a /= fDrds;
b /= fDrds;
c /= fDrds;
var aNoisePositions = [
[a,b,c]
,[-c,b,a]
,[-a,b,-c]
,[c,b,-a]
,[a,c,-b]
,[a,-c,b]
];
for (var j=0;j<6;j++) {
v = Simplex.noise(
aa + aNoisePositions[j][0]
,bb + aNoisePositions[j][1]
,cc + aNoisePositions[j][2]
);
var pos = 4*(y*iW+j*iSize+x);
aPixels[pos] = aPixels[pos+1] = aPixels[pos+2] = v*255<<0;
aPixels[pos+3] = 255;
}
}
oCtx.putImageData(oImgData,0,0);
document.body.addChild('img',{src:mCubemap.toDataURL("image/jpeg")});
Aqui estão seis lados em uma imagem, além de uma captura de tela de sua aparência quando vista de um cubo. O código fonte tem um exemplo 3D escrito em threejs .Mapa panorâmico cúbicoImagem 2D sem costura
Pode parecer que a imagem 2D sem costura é fácil de implementar, mas me parece que esse é o mais difícil do descrito no artigo, porque para entendê-lo, você precisa pensar em quatro dimensões. O mais próximo disso foi um mapa cilíndrico (com repetição horizontal), então vamos tomá-lo como base. No mapa cilíndrico, usamos a posição horizontal da imagem para o círculo; isto é, a posição horizontal da imagem nos dá duas coordenadas x e y no campo de ruído xyz. A posição vertical da imagem corresponde a z no campo de ruído.Queremos que a imagem seja perfeita e vertical; portanto, se adicionarmos outra dimensão, podemos usá-la para criar um segundo círculo e substituir o valor linear do campo z. Isso é semelhante à criação de dois cilindros em um campo quadridimensional. Tentei visualizar isso em um esboço, é impreciso, mas tentei transmitir o princípio geral e não desenhar um cilindro quadridimensional.Um esboço de dois cilindros em quatro dimensõesO código é bastante simples: são apenas dois círculos em um espaço de ruído quadridimensional.
fNoiseScale = .003;
drawNoise(function(i,x,y){
var fNX = x/iSize
,fNY = y/iSize
,fRdx = fNX*2*Math.PI
,fRdy = fNY*2*Math.PI
,a = fRds*Math.sin(fRdx)
,b = fRds*Math.cos(fRdx)
,c = fRds*Math.sin(fRdy)
,d = fRds*Math.cos(fRdy)
,v = Simplex.noise(
123+a*fNoiseScale
,231+b*fNoiseScale
,312+c*fNoiseScale
,273+d*fNoiseScale
)
;
return (Math.min(Math.max(2*(v -.5)+.5,0),1)*255)<<0;
}).img().div(2,2);
E aqui está o resultado: