Inicialmente iba a tener la misma respuesta que todos los demás y atribuir esto a los problemas rand()
. Sin embargo, pensé mejor en hacerlo y en su lugar analicé la distribución que sus matemáticas realmente están produciendo.
TL; DR: El patrón que ve no tiene nada que ver con el generador de números aleatorios subyacente y, en cambio, se debe simplemente a la forma en que su programa está manipulando los números.
Me atendré a tu función azul ya que todas son similares.
uint8_t blue(uint32_t x, uint32_t y) {
return (rand() % 2) ? (x + y) % rand() :
((x * y % 1024) % rand()) % 2 ? (x - y) % rand() :
rand();
}
Cada valor de píxel se selecciona de entre una de las tres funciones: (x + y) % rand()
, (x - y) % rand()
y rand()
;
Veamos las imágenes producidas por cada uno de estos solo.
Esto es lo que esperarías, solo ruido. Llame a esto "Imagen C"
Aquí está agregando las coordenadas de píxeles juntas y eliminando el resto de la división por un número aleatorio. Si la imagen es 1024x1024, entonces la suma está en el rango [0-2046]. El número aleatorio por el que está buceando está en el rango [0, RAND_MAX], donde RAND_MAX es al menos 32k y en algunos sistemas es de 2 mil millones. En otras palabras, en el mejor de los casos hay una probabilidad de 1 en 16 de que el resto no sea solo (x + y)
. Entonces, en su mayor parte, esta función solo producirá un gradiente de azul creciente hacia la dirección + x + y.
Sin embargo, solo está utilizando los 8 bits más bajos, porque devuelve un uint8_t
, por lo que tendrá franjas de gradientes de 256 píxeles de ancho.
Llame a esto "Imagen A"
Aquí haces algo similar, pero con resta. Siempre que x sea mayor que y, tendrá algo similar a la imagen anterior. Pero donde y es mayor, el resultado es un número muy grande porque x
y y
no tiene signo (los resultados negativos se envuelven en la parte superior del rango del tipo sin signo), y luego se % rand()
activa y realmente se obtiene ruido.
Llame a esto "Imagen B"
Cada píxel en su imagen final se toma de una de estas tres imágenes usando las funciones rand() % 2
y ((x * y % 1024) % rand()) % 2
. El primero de estos puede leerse como elegir con un 50% de probabilidad (ignorando los problemas con rand()
y sus bits de bajo orden).
Aquí hay un primer plano de dónde rand() % 2
es verdadero (píxeles blancos) para que se seleccione la Imagen A.
La segunda función ((x * y % 1024) % rand()) % 2
nuevamente tiene el problema de que rand()
generalmente es mayor que la cosa que está dividiendo (x * y % 1024)
, que es como máximo 1023. Luego (x*y%1024)%2
no produce 0 y 1 con la misma frecuencia. Cualquier número impar multiplicado por cualquier número par es par. Cualquier número par multiplicado por cualquier número par también es par. Solo un número impar multiplicado por un número impar es impar, y así sucesivamente los %2
valores que son pares las tres cuartas partes del tiempo producirán 0 tres cuartos de las veces.
Aquí hay un primer plano de dónde ((x * y % 1024) % rand()) % 2
es cierto para que se pueda seleccionar la Imagen B. Está seleccionando exactamente dónde ambas coordenadas son impares.
Y aquí hay un primer plano de dónde se podría seleccionar la imagen C:
Finalmente, combinando las condiciones aquí es donde se selecciona la Imagen B:
Y donde se selecciona la imagen C:
La combinación resultante se puede leer como:
Con una probabilidad del 50%, use el píxel de la Imagen A. El resto del tiempo elija entre la Imagen B y la Imagen C, B donde ambas coordenadas son impares, C donde cualquiera de las dos es par.
Finalmente, dado que está haciendo lo mismo para tres colores diferentes, pero con diferentes orientaciones, los patrones están orientados de manera diferente en cada color y producen las tiras cruzadas o el patrón de cuadrícula que está viendo.