Crear una imagen perfecta en Photoshop es fácil: recorte la imagen, tome las partes derecha e inferior recortadas, y luego péguelas hacia la izquierda y la parte superior con la herramienta Desvanecimiento. Pero para la correcta implementación de mapas de ruido sin interrupciones, debe pensar cuidadosamente.Si tiene una comprensión básica del ruido de Perlin , entonces sabe que consiste en números aleatorios interpolados. Se utiliza principalmente en dos dimensiones. Pero también es útil en una dimensión (por ejemplo, cuando se mueve), en tres dimensiones (transformación cilíndrica y esférica de objetos 3D) e incluso en cuatro o cinco dimensiones.El ruido de cuatro dimensiones se puede utilizar para crear una imagen 2D perfecta. No es muy común para nosotros pensar en cuatro dimensiones, por lo que tomaremos una dimensión a la vez.En mis ejemplos, usé ruido simplex de dos octavas. El ruido simplex es más rápido en grandes dimensiones y, debido a su naturaleza triangular, se ve mejor.Escribí una pequeña función drawNoise
para crear un lienzo y procesar una matriz de píxeles en un bucle.Ruido unidimensional sin interrupciones
En una dimensión, el ruido es una línea suave infinita (mi implementación del ruido comienza con dos, así que uso una constante como segundo parámetro). Aquí vemos que estos son solo números aleatorios interpolados.
fNoiseScale = .02;
drawNoise(function(i,x,y){
var v = Simplex.noise(
123+x*fNoiseScale
,137
);
return v*iSize>y?255:0;
}).img();
Ruido unidimensionalPuede usar esto en animación, contando el valor de ruido cada milisegundo, pero también puede crear un bucle y calcular todos los valores por adelantado. Los valores en la imagen de arriba no giran alrededor de los bordes. Pero implementar la repetibilidad es bastante simple, solo una dimensión más y un ciclo ... o círculo, para eso.Bucle unidimensional
Para la mayoría de ustedes, el ruido de Perlin se parece a la imagen de abajo.Si dibujamos un círculo aquí y contamos los valores de ruido en este círculo, obtendríamos un bucle unidimensional.Ruido con un círculo para crear un bucle unidimensional.En código, se ve así:
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);
Probablemente ya entendiste lo que vamos a hacer. Para recorrer una imagen bidimensional, necesitamos un mapa de ruido tridimensional (al menos).Tarjeta cilíndrica
Noise Perlin fue creado originalmente para texturas continuas en 3D (la película "Tron"). El mapa de imagen no es una hoja de papel envuelta alrededor de un objeto, sino que se calcula por su ubicación en un campo de ruido tridimensional. Por lo tanto, al cortar el objeto, aún podemos calcular el mapa para la superficie recién creada.Antes de alcanzar nuestro objetivo final de una imagen perfecta, primero creamos una imagen que se una perfectamente a izquierda y derecha. Esto es similar a un círculo bidimensional para un bucle unidimensional, pero con una dimensión adicional: un 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 ruido cilíndricoImagen de mapa esférico
Puede pensar que sería conveniente usar una esfera para crear una imagen perfecta, pero está equivocado.Haré una pequeña digresión y mostraré cómo se calcula el mapa de imagen esférica y cómo se ve.
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 ruido esféricoEsfera con ruidoMapa panorámico cúbico
La esfera que creamos también se puede usar como panorama si coloca una cámara en el centro de la esfera. Pero la mejor manera sería usar un panorama cúbico, porque tiene muchas menos caras. La esfera se proyecta en los seis lados del cubo, como se muestra en este boceto.Superposición de una esfera en un cuboPara cada píxel en la superficie del cubo, necesitamos calcular la intersección entre el punto de vista C en el centro y la esfera. Puede parecer complicado, pero en realidad es bastante simple.Podemos considerar la línea CA como un vector. Y los vectores pueden normalizarse para que su dirección no cambie, pero la longitud disminuye a 1. Debido a esto, todos los vectores juntos se verán como una esfera.La normalización también es bastante simple, solo necesitamos dividir los valores del vector por xyz por la longitud total del vector. La longitud del vector se puede calcular utilizando el teorema de Pitágoras.En el siguiente código, el cálculo de normalización se realiza primero en una cara. Luego, el ruido se calcula simultáneamente para los seis bordes, porque para obtener la posición de la siguiente cara, solo necesita voltear los valores a lo largo 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")});
Aquí hay seis lados en una imagen, más una captura de pantalla de cómo se ve cuando se ve desde un cubo. El código fuente tiene un ejemplo en 3D escrito en threejs .Mapa panorámico cúbicoImagen 2D perfecta
Puede parecer que una imagen 2D perfecta es fácil de implementar, pero me parece que esta es la más difícil de las descritas en el artículo, porque para comprenderla debes pensar en cuatro dimensiones. Lo más parecido a esto fue un mapa cilíndrico (con repetición horizontal), por lo que lo tomaremos como base. En el mapa cilíndrico, utilizamos la posición horizontal de la imagen para el círculo; es decir, la posición horizontal de la imagen nos da dos coordenadas x e y en el campo de ruido xyz. La posición vertical de la imagen corresponde a z en el campo de ruido.Queremos que la imagen sea transparente y vertical, por lo que si agregamos otra dimensión, podemos usarla para crear un segundo círculo y reemplazar el valor lineal del campo z. Esto es similar a crear dos cilindros en un campo de cuatro dimensiones. Traté de visualizar esto en un boceto, es inexacto, pero traté de transmitir el principio general y no dibujar un cilindro de cuatro dimensiones.Un bosquejo de dos cilindros en cuatro dimensionesEl código es bastante simple: estos son solo dos círculos en un espacio de ruido de cuatro dimensiones.
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);
Y aqui esta el resultado: