En este post consideraremos la etapa de trabajo con vértices. Es decir, nuevamente tendremos que obtener los libros de texto sobre matemáticas y recordar álgebra lineal, matrices y trigonometría. ¡Hurra!
Descubriremos cómo se transforman los modelos 3D y se tienen en cuenta las fuentes de luz. También explicaremos en detalle la diferencia entre vértices y sombreadores geométricos, y descubrirá en qué etapa es el lugar para la teselación. Para facilitar la comprensión, utilizamos diagramas y ejemplos de código que demuestran cómo el juego realiza cálculos y procesa valores.
La captura de pantalla al comienzo de la publicación muestra el juego GTA V en modo de visualización de estructura alámbrica. Compárelo con la estructura mucho menos compleja de Half-Life 2. Las imágenes fueron creadas por thalixte con
ReShade .
Cual es el punto?
En el mundo de las matemáticas, un punto es simplemente un lugar en el espacio geométrico. No hay nada más pequeño que un punto, no tiene un tamaño, por lo que los puntos se pueden usar para especificar la ubicación exacta del comienzo y el final de los objetos, como segmentos de línea, planos y volúmenes.
Para los gráficos en 3D, dicha información es de importancia crítica, la apariencia de todo depende de ella, porque todos los objetos se muestran como conjuntos de segmentos de línea, planos, etc. La siguiente imagen muestra una captura de pantalla de Bethesda 2015
Fallout 4 :
Puede que no sea fácil para usted ver que esto es solo un gran grupo de puntos y líneas, por lo que le mostraremos cómo se ve la misma escena en modo de estructura alámbrica. En este modo, el motor de renderizado 3D omite las texturas y los efectos realizados en la etapa de píxeles y dibuja solo líneas multicolores que conectan los puntos.
Ahora todo parece completamente diferente, pero vemos cómo se combinan todas las líneas para formar varios objetos, entornos y fondos. Algunos consisten en solo docenas de líneas, por ejemplo, piedras en primer plano, mientras que otras contienen tantas líneas que parecen sólidas.
Cada punto al principio y al final de cada línea se procesa realizando un montón de cálculos. Algunos cálculos son muy simples y rápidos, otros son mucho más complicados. Al procesar puntos en grupos, especialmente en forma de triángulos, puede lograr un aumento significativo en la productividad, así que echemos un vistazo más de cerca.
¿Qué se necesita para un triángulo?
El nombre del
triángulo deja en claro que la figura tiene tres esquinas internas; Para hacer esto, necesita tres puntos de esquina y tres segmentos que los conectan. Es correcto llamar a un punto de esquina
vértice (vértice) (en plural - vértices); cada vértice está definido por un punto. Como estamos en un mundo geométrico tridimensional, el
sistema de coordenadas cartesianas se usa para los puntos. Por lo general, las coordenadas se escriben en forma de tres valores, por ejemplo, (1, 8, -3) o genéricamente (
x, y, z ).
A continuación, podemos agregar dos vértices más para formar un triángulo:
Tenga en cuenta que las líneas que se muestran son opcionales: podemos establecer los puntos y decirle al sistema que estos tres vértices forman un triángulo. Todos los datos de vértices se almacenan en un bloque contiguo de memoria llamado
búfer de vértices ; La información sobre la figura que forman se codifica directamente en el programa de representación o se almacena en otro bloque de memoria llamado
búfer de índice .
Si la información se codifica en un programa de representación, las diversas formas que pueden formarse mediante vértices se denominan
primitivas . Direct3D sugiere usar una lista para ellos, tiras y abanicos en forma de puntos, líneas y triángulos. Cuando se usan correctamente, las franjas de triángulos usan vértices para más de un triángulo, lo que mejora la productividad. En el siguiente ejemplo, vemos que para crear dos triángulos conectados entre sí, solo se necesitan cuatro vértices; si están separados, entonces necesitamos seis vértices.
De izquierda a derecha: lista de puntos, lista de líneas y franja de triángulos.Si necesitamos procesar un conjunto más grande de vértices, por ejemplo, en un modelo de NPC del juego, entonces es mejor usar un objeto llamado
malla , otro bloque de memoria, pero que consta de varios búferes (vértices, índices, etc.) y recursos de textura del modelo . La
documentación en línea de Microsoft tiene una breve explicación de cómo usar estos búferes.
Por ahora, concentrémonos en lo que les sucede a estos vértices en un juego en 3D al renderizar cada nuevo cuadro. En resumen, realizan una de dos operaciones:
- El vértice se mueve a una nueva posición.
- Cambios de color en el vértice
Listo para las matematicas? Excelente, porque lo necesitamos.
Vector aparece en el escenario.
Imagine que tiene un triángulo en la pantalla y presiona una tecla para moverlo hacia la izquierda. Naturalmente, esperamos que los números (
x, y, z ) de cada vértice cambien en consecuencia; eso es lo que sucede, pero es una forma bastante inesperada
de implementar cambios. En lugar de simplemente cambiar las coordenadas, la gran mayoría de los sistemas de representación de gráficos en 3D utilizan una herramienta matemática especial: nos referimos a
vectores .
Un vector se puede representar como una flecha que apunta a un punto específico en el espacio y que tiene la longitud deseada. Los vértices generalmente se establecen usando vectores basados en coordenadas cartesianas:
Observe que la flecha azul comienza en un lugar (en este caso, el
punto de origen ) y se extiende hasta la parte superior. Para establecer el vector, usamos un
registro en una columna , pero es bastante posible usar un
registro en una fila . Es posible que haya notado que hay otro cuarto valor, comúnmente llamado
componente w . Se utiliza para mostrar lo que significa el vector: posición del punto (
vector de posición ) o dirección general (vector de
dirección ). En el caso de un vector de dirección, se verá así:
Este vector apunta en la misma dirección y tiene la misma longitud que el vector de posición anterior, es decir, los valores (
x, y, z ) serán los mismos; sin embargo, el componente
w no es 1, sino cero. Explicaremos el uso de vectores de dirección más adelante, pero por ahora, recuerde el hecho de que todos los vértices en la escena 3D se describirán de esta manera. Por qué Porque en este formato es mucho más fácil moverlos.
Matemáticas, matemáticas y nuevamente matemáticas
Recuerde que tenemos un triángulo simple y queremos moverlo hacia la izquierda. Cada vértice se describe mediante un vector de posición, por lo tanto, las "matemáticas de movimiento" (llamadas
transformaciones ) deben funcionar con estos vectores. Aparece una nueva herramienta:
matrices (
matriz en singular). Esta es una matriz de valores escritos en un formato similar a una hoja de cálculo de Excel, con filas y columnas.
Para cada tipo de transformación hay una matriz correspondiente, y para una transformación es suficiente simplemente multiplicar la matriz de transformación y el vector de posición. No entraremos en detalles sobre cómo y por qué sucede esto, sino que veremos cómo se ve.
Mover un vértice en el espacio 3D se llama
traducción y requiere el siguiente cálculo:
Valores
x 0 , etc. representa las coordenadas originales del vector;
Los valores
delta -
x representan la cantidad en la cual el vértice necesita ser movido. La multiplicación de la matriz y el vector lleva al hecho de que simplemente resumen (tenga en cuenta que el componente
w permanece sin cambios para que la respuesta final siga siendo el vector de posición como antes).
Además de movernos, es posible que también necesitemos rotar el triángulo o cambiar su escala; para estas operaciones, también hay transformaciones.
Esta transformación gira el vértice alrededor del eje z en el plano XYY esto se usa si necesita cambiar la escala de la figuraPodemos usar la herramienta de gráficos basada en WebGL de
Renderizado en tiempo real para visualizar estos cálculos para toda la figura. Comencemos con el cuadro en posición estándar:
En esta herramienta en línea, el punto del modelo es el vector de posición, la matriz mundial es la matriz de transformación y el punto del espacio mundial es el vector de posición para el vértice transformado.
Apliquemos varias transformaciones al cuadro:
En la imagen de arriba, la figura se
movió 5 unidades a lo largo de cada eje. Estos valores se pueden ver en la última columna de la matriz grande del medio. El vector de posición original (4, 5, 3, 1) permanece igual que debería, pero el vértice transformado ahora se mueve a (9, 10, 8, 1).
En esta transformación, todo fue escalado por un factor de 2: ahora los lados de la caja se han vuelto dos veces más largos. Finalmente, mira el ejemplo de rotación:
El paralelepípedo se rotó a través de un ángulo de 45 °, pero el
seno y el
coseno de este ángulo se utilizan en la matriz. Después de verificar una calculadora científica, podemos ver que
sin (45 °) = 0.7071 ..., que se redondea al valor mostrado de 0.71. Obtenemos la misma respuesta para el valor del
coseno .
Las matrices y los vectores son opcionales; Una alternativa popular para ellos, especialmente cuando manejan giros complejos, es el uso de números complejos y
cuaterniones . Estos cálculos son muy diferentes de los vectores, por lo que no los consideraremos y continuaremos trabajando con las transformaciones.
Vertex Shader Power
En esta etapa, debemos entender que todo esto lo hacen personas que programan el código de representación. Si un desarrollador de juegos usa un motor de terceros (por ejemplo, Unity o Unreal), entonces todo esto ya se ha hecho por él; pero si alguien hace su motor desde cero, tendrá que realizar todos estos cálculos con vértices.
Pero, ¿cómo se ve todo esto en términos de código?
Para entender esto, utilizaremos ejemplos del increíble
sitio web de
Braynzar Soft . Si desea comenzar a trabajar con la programación 3D, este es el lugar adecuado para aprender los conceptos básicos, así como cosas más complejas ...
Este es un ejemplo de una transformación todo en uno. Crea las matrices de transformación apropiadas basadas en la entrada del teclado, y luego las aplica al vector de posición original en una operación. Tenga en cuenta que esto siempre se hace en el orden dado (escalado - rotación - transferencia), porque cualquier otra forma arruinará completamente el resultado.
Dichos bloques de código se denominan
sombreadores de vértices , su complejidad y tamaño pueden variar enormemente. El ejemplo anterior es simple, es
solo un sombreador de vértices que no utiliza la naturaleza programable completa de los sombreadores. Una secuencia más compleja de sombreadores podría transformar objetos en el espacio 3D, procesar su apariencia desde el punto de vista de la cámara de escena y luego transferir datos a la siguiente etapa del proceso de renderizado. Considerando el orden del procesamiento de vértices, estudiaremos otros ejemplos.
Por supuesto, se pueden usar para mucho más, así que cuando juegues un juego en 3D, no olvides que todo el movimiento que ves es realizado por una GPU que ejecuta comandos de sombreador de vértices.
Sin embargo, este no fue siempre el caso. Si regresa a mediados de la década de 1990, entonces las tarjetas gráficas de esa época no tenían la capacidad de procesar vértices y primitivas de forma independiente, solo el procesador central hizo todo esto.
Uno de los primeros procesadores con su propia aceleración de hardware de este proceso fue
Nvidia GeForce, lanzada en 2000 , y esta funcionalidad se llamó
Hardware Transform and Lighting (para abreviar, Hardware TnL). Los procesos que este equipo podía manejar eran muy limitados en términos de equipos, pero con el lanzamiento de nuevos chips, la situación cambió rápidamente. Hoy en día, no hay un equipo separado para procesar vértices, y un dispositivo hace todo a la vez: puntos, primitivas, píxeles, texturas, etc.
Por cierto, sobre la
iluminación : vale la pena señalar que vemos todo gracias a la luz, así que veamos cómo se puede procesar en la etapa de vértice. Para hacer esto, debemos aprovechar lo que hablamos antes.
¡Luz, cámara, motor!
Imagine esta imagen: el jugador está parado en una habitación oscura, iluminada por una fuente de luz a la derecha. En el medio de la habitación hay una gran tetera. Es posible que necesite ayuda con esto, así que usemos el sitio web de
representación en tiempo real y veamos cómo se ve:
No olvides que este objeto es un conjunto de triángulos planos conectados entre sí; es decir, el plano de cada triángulo se dirigirá en cierta dirección. Algunos de ellos están dirigidos hacia la cámara, algunos - al otro, algunos estarán distorsionados. La luz de la fuente cae en cada plano y se refleja en un ángulo determinado.
Dependiendo de dónde se refleje la luz, el color y el brillo del avión pueden cambiar, y para que el color del objeto se vea bien, todo esto debe calcularse y tenerse en cuenta.
Para comenzar, necesitamos descubrir hacia dónde se dirige cada plano, y para esto necesitamos
el vector normal del plano. Esta es otra flecha, pero a diferencia del vector de posición, su tamaño no es importante (de hecho, después de calcular la escala de los vectores normales siempre disminuye para que tengan una longitud de 1), y siempre se dirige
perpendicular (en ángulo recto) al plano.
La normal al plano de cada triángulo se calcula determinando el producto vectorial de dos vectores de dirección (mostrados arriba de
py q ) que forman los lados del triángulo. De hecho, es mejor calcularlos para cada vértice, y no para un triángulo, pero dado que siempre hay más de los primeros que de los últimos, será más rápido calcular las normales para los triángulos.
Habiendo recibido lo normal en la superficie, puede comenzar a considerar la fuente de luz y la cámara. En la representación 3D, las fuentes de luz pueden ser de diferentes tipos, pero en este artículo consideraremos solo fuentes
direccionales , por ejemplo, focos. Al igual que el plano del triángulo, el foco y la cámara apuntarán en cierta dirección, algo así:
El vector de fuente de luz y el vector normal pueden usarse para calcular el ángulo en el que la luz cae sobre la superficie (usando la relación entre el producto escalar de los vectores y el producto de su tamaño). Los vértices del triángulo contendrán información adicional sobre su color y material. El material describe lo que le sucede a la luz cuando golpea la superficie.
Una superficie metálica lisa reflejará casi toda la luz incidente en el ángulo en el que cayó, y apenas cambiará el color del objeto. El material mate áspero dispersa la luz de una manera menos predecible y cambia ligeramente de color. Para tener esto en cuenta, debe agregar valores adicionales a los vértices:
- Color base original
- Atributo de material ambiental: un valor que determina cuánta iluminación de "fondo" puede absorber y reflejar un vértice
- El atributo del material difuso es otro valor, pero esta vez determina la "rugosidad" del vértice, lo que, a su vez, afecta la cantidad de absorción y reflexión de la luz dispersa.
- Atributos del material especular: dos valores que especifican el brillo del vértice
Los diferentes modelos de iluminación utilizan diferentes fórmulas matemáticas para agrupar todos estos atributos, y el resultado del cálculo es el vector de iluminación saliente. En combinación con el vector de la cámara, le permite determinar la apariencia general del triángulo.
Una fuente de luz direccional ilumina muchas demostraciones diferentes de NvidiaOmitimos muchos detalles detallados, y por una buena razón: abra cualquier tutorial de renderizado 3D, y verá que capítulos completos están dedicados a este proceso. Sin embargo, en los juegos modernos, la mayor parte de todos los cálculos de iluminación y efectos de materiales se realizan en la etapa de procesamiento de píxeles, por lo que volveremos a ellos en el próximo artículo.
Código de ejemplo B. Anguelov que muestra cómo se puede procesar el modelo de reflexión de luz de Phong en el sombreador de vértices.Todo lo que examinamos arriba es hecho por sombreadores de vértices, y parece que nada es imposible para ellos; desafortunadamente esto no es así. Los sombreadores de vértices no pueden crear nuevos vértices, y cada sombreador debe procesar cada vértice individual. Sería conveniente si pudiera usar el código para crear nuevos triángulos entre los que ya tenemos (para mejorar la calidad visual), y tener un sombreador que procese una primitiva completa (para acelerar el procesamiento). Bueno, ¡en las GPU modernas podemos hacerlo!
Por favor señor, quiero más (triángulos)
Los chips gráficos modernos son extremadamente potentes y capaces de realizar millones de cálculos de vectores de matriz por segundo; se las arreglan fácilmente con una gran pila de picos a la vez. Por otro lado, la creación de modelos altamente detallados para renderizar es un proceso muy largo, y si el modelo se encuentra a cierta distancia de la escena, todos estos detalles se desperdiciarán.
Es decir, necesitamos ordenar de alguna manera al procesador que divida una primitiva grande, por ejemplo, un triángulo plano en un conjunto de triángulos más pequeños ubicados dentro del original. Tal proceso se llama
teselación, y los chips gráficos ya han aprendido a realizarlo muy bien; A lo largo de los años de desarrollo, el grado de control que los programadores tienen sobre este proceso ha aumentado.
Para ver esto en acción, utilizaremos la
herramienta de referencia Heaven del motor Unigine , porque nos permite aplicar diferentes valores de teselación a los modelos utilizados en la prueba.
Para comenzar, tomemos un lugar en el punto de referencia y estudiemos sin usar la teselación. Tenga en cuenta que los adoquines en el suelo se ven muy poco naturales: la textura utilizada es efectiva, pero parece incorrecta. Apliquemos el mosaico a la escena: el motor Unigine lo aplica solo a partes individuales, pero la diferencia será significativa.El terreno, los bordes de los edificios y la puerta se ven mucho más realistas. Podemos ver cómo se logró esto al comenzar el proceso nuevamente, pero esta vez con la selección de todas las primitivas (es decir, en modo de estructura alámbrica):Se ve claramente por qué la tierra se ve tan extraña: ¡es completamente plana! La puerta se fusiona con las paredes, y los bordes del edificio son paralelepípedos simples.En Direct3D, las primitivas se pueden dividir en un grupo de partes más pequeñas (este proceso se denomina subdivisión) realizando un proceso de tres pasos. Primero, los programadores escriben un sombreador de superficie (sombreador de casco) ; de hecho, este código crea una estructura llamada parche de geometría . Puede pensarlo como un mapa que le dice al procesador dónde aparecerán nuevos puntos y líneas dentro de la primitiva inicial.Luego, el bloque de teselador dentro de la GPU aplica este parche a la primitiva. Al final, se ejecuta un sombreador de dominio.calcular las posiciones de todos los vértices nuevos. Si es necesario, estos datos se pueden transferir nuevamente al búfer de vértices para que los cálculos de iluminación se puedan realizar nuevamente, pero esta vez con mejores resultados.¿Cómo se ve? Vamos a lanzar la versión alámbrica de la escena teselada:Honestamente, establecemos un alto grado de teselación para que la explicación del proceso sea más visible. No importa cuán buenos sean los chips gráficos modernos, esto no se debe hacer en todas las escenas; mire, por ejemplo, la lámpara al lado de la puerta.En las imágenes con la estructura alámbrica desactivada, difícilmente encontrará diferencias a esta distancia, y vemos que tal nivel de teselación agregó tantos triángulos que es difícil separarlos entre sí. Sin embargo, cuando se usa correctamente, esta función de procesamiento de vértices puede crear efectos visuales fantásticos, especialmente al simular colisiones de cuerpos blandos.Veamos cómo podría verse en términos de código Direct3D; para esto usamos el ejemplo de otro gran sitio web, RasterTek .Aquí hay un triángulo verde simple teselado en muchos triángulos pequeños ...El procesamiento de vértices se realiza mediante tres sombreadores separados (vea el ejemplo del código ): un sombreador de vértices que prepara un triángulo para la teselación, un sombreador de superficie que genera un parche y un sombreador de dominio que procesa nuevos vértices. El resultado es bastante comprensible, pero el ejemplo de Unigine demuestra tanto los beneficios potenciales como los peligros del uso generalizado de la teselación."Hierro" no puede soportarlo!
¿Recuerdas que dijimos que los sombreadores de vértices siempre procesan cada vértice de una escena? Es fácil entender que la teselación puede ser un problema serio aquí. Y hay muchos efectos visuales en los que necesita procesar diferentes versiones de una primitiva, pero sin crearlos desde el principio, por ejemplo, cabello, pelaje, hierba y partículas de explosiones.Afortunadamente, especialmente para tales cosas, hay otro sombreador: un sombreador geométrico . Esta es una versión más limitada del sombreador de vértices, pero se puede aplicar a toda la primitiva. En combinación con la teselación, les da a los programadores un mayor control sobre grandes grupos de vértices.3DMark Vantage de UL Benchmark: los sombreadores geométricos procesan partículas y banderasDirect3D, como todas las API de gráficos modernos, le permiten realizar muchos cálculos con vértices. Los datos terminados pueden transferirse a la siguiente etapa del proceso de renderizado ( rasterización ) o devolverse al grupo de memoria para que el procesador central los vuelva a procesar o leer para otros fines. Como dice la documentación de Microsoft en Direct3D , esto se puede implementar como una secuencia de datos:El paso de salida de flujo es opcional, especialmente porque solo puede transferir primitivas enteras (en lugar de vértices individuales) al ciclo de renderizado, pero es útil para efectos que requieren una gran cantidad de partículas. Se puede realizar el mismo truco usando un búfer de vértices variable o dinámico , pero es mejor mantener los búferes de entrada sin cambios, porque cuando se abren para modificación, el rendimiento se reduce.El procesamiento de vértices es una parte crítica del renderizado, porque determina cómo se verá la escena desde la perspectiva de una cámara. En los juegos modernos, se pueden usar millones de triángulos para construir mundos, y cada uno de estos vértices se transforma e ilumina de alguna manera.Triángulos Hay millones de ellos.El procesamiento de todos estos cálculos y matemáticas puede parecer una pesadilla logística, pero los procesadores gráficos (GPU) y las API están diseñados con todo esto en mente: imagine una fábrica que funciona perfectamente y que pasa un producto a la vez a través de la tubería de producción.Los programadores experimentados de renderizado de juegos en 3D tienen conocimientos fundamentales en matemáticas y física; usan todos los trucos y herramientas posibles para optimizar las operaciones, comprimiendo la etapa de procesamiento de vértices a solo unos pocos milisegundos. Pero esto es solo el comienzo de la construcción de un marco 3D: la siguiente etapa es la rasterización, luego el procesamiento extremadamente complejo de píxeles y texturas, y solo entonces la imagen llega al monitor.