Cuando trabaje con activos de polígono, puede dibujar solo un objeto a la vez (si no tiene en cuenta técnicas como el procesamiento por lotes y la creación de instancias), pero si usa campos de distancia con un signo (campos de distancia firmados, SDF), entonces no estamos limitados a esto. Si dos posiciones tienen la misma coordenada, las funciones de distancia con signo devolverán el mismo valor, y en un cálculo podemos obtener varias cifras. Para comprender cómo transformar el espacio utilizado para generar campos de distancia con signo, le recomiendo que
descubra cómo
crear formas utilizando las funciones de distancia con signo y
combinar formas sdf .
Configuracion
Para este tutorial, modifico el emparejamiento entre el cuadrado y el círculo, pero puede usarlo para cualquier otra forma. Esto es similar a la configuración del
tutorial anterior .
Aquí es importante que la parte modificable sea antes de usar posiciones para generar figuras.
Shader "Tutorial/036_SDF_Space_Manpulation/Type"{ Properties{ _InsideColor("Inside Color", Color) = (.5, 0, 0, 1) _OutsideColor("Outside Color", Color) = (0, .5, 0, 1) _LineDistance("Mayor Line Distance", Range(0, 2)) = 1 _LineThickness("Mayor Line Thickness", Range(0, 0.1)) = 0.05 [IntRange]_SubLines("Lines between major lines", Range(1, 10)) = 4 _SubLineThickness("Thickness of inbetween lines", Range(0, 0.05)) = 0.01 } SubShader{
Y la función 2D_SDF.cginc ubicada en la misma carpeta con el sombreador, que expandiremos, al principio se ve así:
#ifndef SDF_2D #define SDF_2D
Repetición del espacio
Reflejo del espejo
Una de las operaciones más simples es reflejar el mundo sobre un eje. Para reflejarlo alrededor del eje y, tomamos el valor absoluto del componente x de nuestra posición. Por lo tanto, las coordenadas a la derecha e izquierda del eje serán las mismas.
(-1, 1)
convierte en
(1, 1)
y resulta estar dentro de un círculo usando
(1, 1)
como origen de coordenadas y con un radio mayor que 0.
Muy a menudo, el código que usa esta función se verá como
position = mirror(position);
para que podamos simplificarlo un poco. Simplemente declararemos el argumento de posición como inout. Por lo tanto, al escribir en el argumento, también cambiará la variable que pasamos a la función. El valor de retorno puede ser de tipo nulo, porque todavía no usamos el valor de retorno.
Resultó bastante bien, pero de esta manera solo obtenemos un eje para la duplicación. Podemos expandir la función girando el espacio como lo hicimos al girar las figuras. Primero debe rotar el espacio, luego reflejarlo y luego volverlo. De esta forma podemos realizar reflejos con respecto a cualquier ángulo. Lo mismo es posible cuando se transfiere espacio y se realiza una transferencia inversa después de la duplicación. (Si realiza ambas operaciones, antes de duplicar, no olvide realizar primero la transferencia, y luego gire, después de lo cual el turno va primero).
Células
Si sabe cómo funciona la
generación de ruido , entonces comprende que para la generación de procedimientos a menudo repetimos la posición y obtenemos celdas pequeñas que son esencialmente las mismas, que difieren solo en parámetros insignificantes. Podemos hacer lo mismo para los campos de distancia.
Dado que la función
fmod
(además de usar% para dividir con el resto) nos da el resto, no la definición del resto, tendremos que usar un truco. Primero, tomamos el resto de la división de enteros por la función fmod. Para números positivos, esto es exactamente lo que necesitamos, y para números negativos, este es el resultado que necesitamos menos el período. Puede arreglar esto agregando un punto y nuevamente tomando el resto de la división. Agregar un período dará el resultado deseado para valores de entrada negativos, y para valores de entrada positivos, el valor es un período más alto. El segundo resto de la división no hará nada con los valores para los valores de entrada negativos, porque ya están en el rango de 0 al período, y para los valores de entrada positivos, esencialmente restamos un período.

El problema con las células es que perdemos la continuidad por la que amamos los campos de distancia. Esto no es malo si las formas están solo en el medio de las celdas, pero en el ejemplo que se muestra arriba esto puede conducir a artefactos significativos que deben evitarse cuando los campos de distancia se usan para muchas tareas en las que los campos de distancia generalmente se pueden aplicar.
Hay una solución que no funciona en todos los casos, pero cuando funciona, es maravilloso: reflejar todas las demás celdas. Para hacer esto, necesitamos un índice de celda de píxel, pero aún no tenemos un valor de retorno en la función, por lo que podemos usarlo para devolver el índice de celda.
Para calcular el índice de celda, dividimos la posición por el período. Por lo tanto, 0-1 es la primera celda, 1-2 es la segunda, y así sucesivamente ... y podemos discretizar fácilmente. Para obtener el índice de la celda, simplemente redondeamos el valor hacia abajo y devolvemos el resultado. Lo importante es que calculemos el índice de la celda antes de dividirla con el resto para repetir las celdas; de lo contrario, obtendríamos el índice 0 en todas partes, porque la posición no puede exceder el período.
Con esta información, podemos voltear las celdas. Para entender si voltear o no, dividimos el módulo de índice de celda 2. El resultado de esta operación es alternativamente 0 y 1 o -1 por cada segunda celda. Para que el cambio sea más permanente, tomamos el valor absoluto y obtenemos un valor que cambia entre 0 y 1.
Para usar este valor para cambiar entre una posición normal y otra invertida, necesitamos una función que no haga nada por el valor 0, y reste la posición del período en el que la inversión es 1. Es decir, realizamos una interpolación lineal de la posición normal a la invertida usando la variable flip . Dado que la variable flip es un vector 2d, sus componentes se voltean individualmente.
Células radiales
Otra gran característica es la repetición del espacio en un patrón radial.
Para obtener este efecto, primero calculamos la posición radial. Para hacer esto, codificamos el ángulo relativo al centro del eje xy la distancia desde el centro a lo largo del eje y.
float2 radialPosition = float2(atan2(position.x, position.y), length(position));
Luego repetimos la esquina. Dado que transmitir el número de repeticiones es mucho más fácil que el ángulo de cada pieza, primero calculamos el tamaño de cada pieza. Todo el círculo es 2 * pi, por lo que para obtener la parte correcta, dividimos 2 * pi por el tamaño de la celda.
const float PI = 3.14159; float cellSize = PI * 2 / cells;
Con esta información, podemos repetir el componente x de la posición radial en cada unidad cellSize. Realizamos la repetición por división con el resto, por lo tanto, como antes, tenemos problemas con los números negativos, que pueden eliminarse con la ayuda de dos funciones de división con el resto.
radialPosition.x = fmod(fmod(radialPosition.x, cellSize) + cellSize, cellSize);
Luego debe mover la nueva posición a las coordenadas xy habituales. Aquí usamos la función sincos con el componente x de la posición radial como ángulo para escribir el seno en la coordenada x de la posición y el coseno en la coordenada y. Con este paso conseguimos una posición normalizada. Para obtener la dirección correcta desde el centro, debe multiplicarla por el componente y de la posición radial, lo que significa la longitud.
Luego también podemos agregar el índice de la celda y la duplicación, como lo hicimos con las celdas regulares.
Es necesario calcular el índice de la celda después de calcular la posición radial, pero antes de recibir el resto de la división. Lo obtenemos dividiendo el componente x de la posición radial y redondeando el resultado hacia abajo. En este caso, el índice también puede ser negativo, y esto es un problema si el número de celdas es impar. Por ejemplo, con 3 celdas, obtenemos 1 celda con un índice de 0, 1 celda con un índice de -1 y 2 medias celdas con los índices 1 y -2. Para solucionar este problema, agregamos el número de celdas a la variable redondeada hacia abajo y luego dividimos por el tamaño de la celda con el resto.
Para reflejar esto, necesitamos que las coordenadas se especifiquen en radianes, por lo que para evitar recalcular las coordenadas radiales fuera de la función, agregaremos una opción usando el argumento bool. Por lo general, en los sombreadores, la ramificación (si se trata de construcciones) no es bienvenida, pero en este caso todos los píxeles en la pantalla irán por el mismo camino, por lo que esto es normal.
La duplicación debe ocurrir después de que la coordenada radial se repita, pero antes de que se convierta nuevamente a su posición normal. Descubriremos si necesitamos voltear la celda actual dividiendo el índice de la celda por el resto. Por lo general, esto debería darnos ceros y unos, pero en mi caso aparecen varios dos, lo cual es extraño, y aún así podemos manejarlo. Para eliminar deuces, simplemente restamos 1 de la variable flip, y luego tomamos el valor absoluto. Por lo tanto, los ceros y los deuces se convierten en unos, y las unidades se convierten en ceros, según sea necesario, solo en el orden inverso.
Como los ceros y unos están en el orden incorrecto, realizamos una interpolación lineal desde la versión invertida a la versión invertida, y no al revés, como antes. Para voltear la coordenada, simplemente restamos la posición del tamaño de la celda.
Espacio oscilante
Pero para cambiar el espacio no es necesario repetirlo. Por ejemplo, en el tutorial sobre lo básico, lo rotamos, movimos y escalamos. También puede hacer lo siguiente: mover cada eje sobre la base del otro con una onda sinusoidal. Esto hará que las distancias de la distancia firmada funcionen con menos precisión, pero hasta que oscilen demasiado, todo estará bien.
Primero, calculamos la magnitud del cambio de posición volteando los componentes x e y, y luego multiplicándolos por la frecuencia de oscilación. Luego tomamos el seno de este valor y lo multiplicamos por la cantidad de bamboleo que queremos agregar. Después de eso, simplemente agregamos este factor de oscilación a la posición y nuevamente aplicamos el resultado a la posición.
También podemos animar este movimiento, cambiando su posición, aplicando el movimiento en la posición de desplazamiento y devolviendo el espacio. Para que los números de coma flotante no sean demasiado grandes, hago la división con el resto pi * 2 por la frecuencia de oscilación, esto se correlaciona con la oscilación (la sinusoide se repite cada unidad pi * 2), por lo que evitamos saltos y compensaciones demasiado grandes.
Código fuente
2D SDF Library
#ifndef SDF_2D #define SDF_2D
Shader demo básico
Shader "Tutorial/036_SDF_Space_Manpulation/Mirror"{ Properties{ _InsideColor("Inside Color", Color) = (.5, 0, 0, 1) _OutsideColor("Outside Color", Color) = (0, .5, 0, 1) _LineDistance("Mayor Line Distance", Range(0, 2)) = 1 _LineThickness("Mayor Line Thickness", Range(0, 0.1)) = 0.05 [IntRange]_SubLines("Lines between major lines", Range(1, 10)) = 4 _SubLineThickness("Thickness of inbetween lines", Range(0, 0.05)) = 0.01 } SubShader{
Ahora conoces todos los conceptos básicos de las funciones de distancia de signos que puedo recordar. En el próximo tutorial, intentaré hacer algo interesante con ellos.