Acerca de la creación de imágenes estéreo económicas en los dedos (estereograma, anaglifo, estereoscopio)

Lleg√≥ el pr√≥ximo fin de semana, debe escribir un par de docenas de l√≠neas de c√≥digo y dibujar una imagen, pero ninguna es mejor. Entonces, el pasado fin de semana antes del √ļltimo, mostr√© c√≥mo hacer el trazado de rayos e incluso hacer explotar todo. Esto es sorprendente para muchos, pero los gr√°ficos por computadora son muy simples, un par de cientos de l√≠neas de C ++ desnudo son suficientes para crear im√°genes interesantes.

El tema de la conversación de hoy es la visión binocular, y hoy ni siquiera podemos llegar a cien líneas de código. Ser capaz de renderizar escenas tridimensionales, sería una tontería pasar estereparaciones, hoy dibujaremos algo como esto:



La locura de los desarrolladores de Magic Carpet me persigue. Para aquellos que no lo encontraron, este juego hizo posible renderizar en 3D tanto en anaglifo como en estereogramas en la configuraci√≥n principal, ¬°solo disponible en el men√ļ! Este cerebro explot√≥ espec√≠ficamente.

Paralaje


Entonces comencemos. Para empezar, ¬Ņpor qu√© nuestro aparato visual nos permite percibir la profundidad? Hay una palabra tan inteligente "paralaje". Si est√° en los dedos, concentr√©monos en la pantalla. Todo lo que est√° en el plano de la pantalla para nuestro cerebro existe en una sola copia. Pero si de repente una mosca vuela frente a la pantalla, entonces (¬°si no cambiamos nuestros ojos!) Nuestro cerebro lo registrar√° por duplicado. Y al mismo tiempo, la ara√Īa en la pared detr√°s de la pantalla tambi√©n se bifurca, y la direcci√≥n de la bifurcaci√≥n depende de si el objeto est√° delante del punto focal o detr√°s:



Nuestro cerebro es una máquina muy eficiente para analizar imágenes ligeramente diferentes. Utiliza la disparidad para obtener información de profundidad de imágenes de retina bidimensionales para estereopsis . Bueno, Dios los bendiga, con las palabras, ¡mejor dibujemos!

Asumamos que nuestra pantalla es una ventana al mundo virtual :)



Nuestra tarea es dibujar dos im√°genes con lo que ser√° visible a trav√©s de esta "ventana". Habr√° dos im√°genes, una para cada ojo, en el diagrama de arriba las mostr√© con un "emparedado" rojo y azul. No nos molestemos a√ļn c√≥mo exactamente alimentamos estas im√°genes al aparato visual, solo necesitamos guardar dos archivos. Me interesa espec√≠ficamente c√≥mo se pueden obtener estas im√°genes con nuestro rastreador de rayos .

Bueno, supongamos que la direcci√≥n de la mirada no cambia, es un vector (0,0, -1). Supongamos que podemos mover la posici√≥n de la c√°mara por la distancia interocular, ¬Ņqu√© m√°s? Hay una peque√Īa sutileza: el cono de vista a trav√©s de nuestra "ventana" es asim√©trico. Y nuestro rastreador de rayos solo puede representar un cono de mirada sim√©trico:



Que hacer Leer :)
De hecho, podemos hacer que las im√°genes sean m√°s anchas de lo que necesitamos y simplemente recortar el exceso:



Anaglifo


Con el mecanismo de representación general, debe quedar claro, ahora es el momento de preguntarnos acerca de la entrega de la imagen a nuestro cerebro. Una de las opciones más simples son las gafas rojo-azul:



Simplemente hacemos que los dos pre-renders no sean negros sino blancos, escribamos la imagen izquierda en el canal rojo y la imagen derecha en azul. Obtienes la siguiente imagen:



El vidrio rojo cortará un canal, y el vidrio azul cortará otro, de modo que cada ojo recibirá su propia imagen y podremos ver el mundo en 3D. Estos son los cambios en la confirmación principal del primer artículo , que muestran la configuración de la cámara para ambos ojos y el ensamblaje del canal.

Las representaciones anaglíficas son una de las formas más antiguas de ver (¡computadora!) Imágenes estéreo. Tienen muchas deficiencias, por ejemplo, mala reproducción del color (por cierto, intente grabar el canal verde del ojo derecho en el canal verde de la imagen final). Una ventaja: tales gafas están hechas fácilmente de materiales improvisados.

Estereoscopio


Con la proliferaci√≥n de los tel√©fonos inteligentes, recordamos qu√© son los estereoscopios (que, por un segundo, se inventaron en el siglo XIX). Hace unos a√Īos, Google sugiri√≥ usar dos lentes de centavo (desafortunadamente, no se hacen en la rodilla), un poco de cart√≥n (por todas partes) y un tel√©fono inteligente (en el bolsillo) para obtener gafas de realidad virtual bastante tolerables:



En aliexpress, eran montones, cien rublos por pieza. En comparación con el anaglifo, no necesita hacer nada en absoluto, solo tome dos fotos y compóngalas una al lado de la otra, aquí está el compromiso .



Estrictamente hablando, dependiendo de la lente, la corrección de la distorsión de la lente puede ser necesaria, pero no me molesté en absoluto y se ve muy bien en mis lentes. Pero si realmente necesita aplicar una distorsión previa en forma de barril que compense la distorsión de la lente, entonces así es como se ve mi teléfono inteligente y mis lentes:



Estereogramas


Pero, ¬Ņqu√© pasa si no quieres usar dispositivos adicionales? Entonces solo hay una opci√≥n: adormecer. En t√©rminos generales, la imagen anterior es suficiente para ver est√©reo, solo use el truco para mirar est√©reo. Hay dos principios para ver estereogramas: mueva los ojos o separe los ojos. Entonces dibuj√© un diagrama en el que le muestro c√≥mo puede ver la imagen anterior. La imagen anterior es doble, dos barras rojas en el diagrama muestran dos im√°genes en la retina izquierda, dos azules en la derecha.



Si enfocamos nuestros ojos en la pantalla, de cuatro imágenes obtenemos dos. Si entrecerramos los ojos hacia la nariz, es muy posible mostrarle al cerebro "tres" imágenes. Y viceversa, si abre los ojos, también puede obtener "tres" imágenes. La superposición de imágenes centrales le dará al cerebro un efecto estéreo.

Estos m√©todos se administran a diferentes personas de diferentes maneras, por ejemplo, no s√© c√≥mo mover los ojos, pero me reproduzco f√°cilmente. Es importante que el estereograma construido para un m√©todo se vea de la misma manera, de lo contrario se obtiene un mapa de profundidad invertido (ver paralaje negativo y positivo). El problema con este m√©todo de visualizaci√≥n est√©reo es que es muy dif√≠cil mover los ojos fuertemente en relaci√≥n con el estado normal, por lo que debe contentarse con im√°genes peque√Īas. ¬ŅY si quieres unos grandes? Sacrifiquemos completamente el color, y queramos solo una percepci√≥n de profundidad. Mirando hacia el futuro, aqu√≠ est√° la imagen que tenemos al final de esta parte:



Este estereograma est√° dise√Īado para "diluir" los ojos (estereograma de ojos de pared). Para aquellos que prefieren la forma inversa de ver, tome una foto aqu√≠ . Si no est√° acostumbrado a los estereogramas, pruebe diferentes condiciones: una imagen de pantalla completa, una imagen peque√Īa, luz brillante, oscuridad. La tarea es abrir los ojos para que las dos tiras vorticales adyacentes coincidan. La forma m√°s f√°cil de enfocarse en la esquina superior izquierda es Ella es plana. Por ejemplo, estoy rodeado por el entorno del Habr, abro la imagen en pantalla completa. ¬°No olvides quitarle el mouse!

No se conforme con el efecto 3D defectuoso. Si solo eres vagamente consciente de las formas redondeadas en medio de puntos aleatorios junto con algunos efectos 3D tenues, ¡esta es ciertamente una ilusión incompleta! Si mira correctamente, las bolas deben salir claramente del plano de la pantalla al espectador, el efecto debe ser estable y mantenido debido al estudio constante y detallado de cada parte de la imagen, tanto el primer plano como el fondo. La estereopsis tiene histéresis: tan pronto como obtienes una imagen estable, se vuelve más clara cuanto más tiempo miras. Cuanto más lejos esté la pantalla de los ojos, mayor será el efecto de profundidad.

Este estereograma se dibujó de acuerdo con el método propuesto hace un cuarto de siglo por Thimbleby et al. En su artículo " Visualización de imágenes en 3D: algoritmos para estereogramas de puntos aleatorios de una sola imagen ".

Punto de partida


El punto de partida para renderizar estereogramas es un mapa de profundidad (nos olvidamos del color). Aquí hay una confirmación que muestra esa imagen:



Las profundidades en nuestro render est√°n cortadas por los planos cercanos y lejanos, es decir, el punto m√°s alejado de mi mapa tiene una profundidad de 0, el m√°s cercano.

Principio b√°sico


Deje que nuestros ojos est√©n a una distancia d de la pantalla. Coloque el plano lejano (imaginario) (z = 0) a la misma distancia detr√°s de la pantalla. Elegimos una constante őľ, que determina la posici√≥n del plano cercano (z = 0): estar√° a una distancia őľd de la lejana. Eleg√≠ őľ = 1/3 en mi c√≥digo. Total, nuestro mundo entero vive a una distancia de d-őľd a d detr√°s de la pantalla. Tengamos una distancia e definida entre los ojos (en p√≠xeles, en mi c√≥digo eleg√≠ 400 p√≠xeles).



Si observamos el punto de nuestro objeto marcado en rojo en el diagrama, entonces dos p√≠xeles marcados en verde deber√≠an tener el mismo color en el estereograma. ¬ŅC√≥mo encontrar la distancia entre estos p√≠xeles? Muy simple Si el punto proyectado actual tiene una profundidad de z, entonces la relaci√≥n de paralaje a la distancia entre los ojos es igual a la relaci√≥n de las profundidades correspondientes: p / e = (d-dőľz) / (2d-dőľz). Por cierto, tenga en cuenta que d se est√° reduciendo y no est√° involucrado en ning√ļn otro lugar. Es decir, p / e = (1-őľz) / (2-őľz), lo que significa que el paralaje es igual a p = e * (1-őľz) / (2-őľz) p√≠xeles.

Es decir, el principio básico de la construcción de un estereograma: revisamos todo el mapa de profundidad, para cada valor de profundidad determinamos qué píxeles deben tener el mismo color y escribimos esto en nuestro sistema de restricciones. Luego comenzamos desde una imagen arbitraria e intentamos cumplir con todas las restricciones impuestas previamente.

Prepara la foto original


En esta etapa, prepararemos una imagen en la que luego impondremos restricciones de paralaje.
Aquí, comprométete , dibuja esta imagen:



Tenga en cuenta que, en general, los colores son aleatorios, excepto que pongo rand () * sin en el canal rojo para proporcionar ondas peri√≥dicas. Estas ondas est√°n hechas con una distancia de 200 p√≠xeles, este (con őľ = 1/3 y e = 400 seleccionados) es el valor de paralaje m√°ximo en nuestro mundo, tambi√©n es un plano distante. Estas ondas son opcionales, pero facilitar√°n el enfoque de visi√≥n necesario.

Renderizar estereograma


En realidad, el código completo relacionado con el estereograma se ve así:

int parallax(const float z) { const float eye_separation = 400.; // interpupillary distance in pixels const float mu = .33; // if the far plane is a distance D behind the screen, then the near plane is a distance mu*D in front of the far plane return static_cast<int>(eye_separation*((1.-z*mu)/(2.-z*mu))+.5); } size_t uf_find(std::vector<size_t> &same, size_t x) { return same[x]==x ? x : uf_find(same, same[x]); } void uf_union(std::vector<size_t> &same, size_t x, size_t y) { if ((x=uf_find(same, x)) != (y=uf_find(same, y))) same[x] = y; } int main() { [...] for (size_t j=0; j<height; j++) { // autostereogram rendering loop std::vector<size_t> same(width); std::iota(same.begin(), same.end(), 0); // initialize the union-find data structure (same[i]=i) for (size_t i=0; i<width; i++) { // put the constraints int par = parallax(zbuffer[i+j*width]); int left = i - par/2; int right = left + par; // works better than i+par/2 for odd values of par if (left>=0 && right<(int)width) uf_union(same, left, right); // left and right pixels will have the same color } for (size_t i=0; i<width; i++) { // resolve the constraints size_t root = uf_find(same, i); for (size_t c=0; c<3; c++) framebuffer[(i+j*width)*3+c] = framebuffer[(root+j*width)*3+c]; } } [...] 

En todo caso, entonces comprom√©tete aqu√≠ . La funci√≥n int parallax (const float z) proporciona la distancia entre p√≠xeles del mismo color para el valor de profundidad actual. Representamos el estereograma l√≠nea por l√≠nea, ya que las l√≠neas son independientes entre s√≠ (no tenemos paralaje vertical). Por lo tanto, el bucle principal simplemente recorre todas las l√≠neas; Para cada uno de ellos, comenzamos con un conjunto completo e ilimitado de p√≠xeles, sobre los cuales impondremos restricciones de igualdad por pares, y al final tendremos un cierto n√ļmero de grupos de p√≠xeles (desconectados) del mismo color. Por ejemplo, un p√≠xel con √≠ndice a la izquierda y un p√≠xel con √≠ndice a la derecha deber√≠an terminar siendo los mismos.

¬ŅC√≥mo almacenar este conjunto de restricciones? La respuesta m√°s simple es la uni√≥n: encontrar la estructura de datos . No lo describir√©, son solo tres l√≠neas de c√≥digo, puedes leerlo en Wikipedia. La idea principal es que para cada grupo tendremos un cierto "responsable", tambi√©n es un p√≠xel ra√≠z, lo dejaremos del mismo color que en la imagen original y volveremos a pintar todos los dem√°s p√≠xeles en el grupo:

  for (size_t i=0; i<width; i++) { // resolve the constraints size_t root = uf_find(same, i); for (size_t c=0; c<3; c++) framebuffer[(i+j*width)*3+c] = framebuffer[(root+j*width)*3+c]; } 

Conclusión


Bueno, en realidad, eso es todo. Veinte líneas de código, ¡y nuestro estereograma está listo, rompa los ojos y la cabeza, haga dibujos! Por cierto, solo los colores aleatorios en un estereograma son generalmente un lujo, en principio, si lo intentas, también puedes transmitir parcialmente el color de nuestra imagen.

Otros sistemas de visualización estéreo, por ejemplo, relacionados con la polarización , saqué de la discusión, ya que van más allá del presupuesto de cien rublos. Si te pierdes algo, agrega y corrige!

Source: https://habr.com/ru/post/438646/


All Articles