Esta es una respuesta larga, pero en realidad la premisa básica de dividir por cámara-z es muy simple: cuanto más lejos está algo de ti, más pequeño parece. Además, aparecen las distancias más pequeñas entre dos cosas.
Posiciones (¡No es necesario leer si está usando Unity!)
En primer lugar, debe representar las posiciones / puntos con la perspectiva correcta.
Las posiciones se encuentran en un plano plano. Desea algo como la imagen de la derecha ... considere las esquinas de los mosaicos como puntos / posiciones de muestra.
Así es como aborda la transformación de puntos:
- Su sistema de coordenadas es el siguiente: el positivo
z
corre hacia la pantalla, mientras que x
corre de izquierda a derecha y y
baja. Camera z es world z. Ese es el atajo que hace que esto sea mucho más fácil que escribir un motor 3D completo. ¿Abajo? La cámara no puede cambiar la orientación (aunque puede cambiar la posición).
- Almacene la posición 3D inicial de su cámara. Ponlo un poco atrás (menos
z
) desde el origen mundial.
- Almacene una colección de puntos 3D en el plano xz (entréguelos
y=0
). Trate de centrarlos en todo el mundo en el origen x
, (0,0,0)
, es decir, de negativo n
a positivo n
. Esto es para centrarlos en la ventana gráfica, cuando comienza la representación.
- Considere el punto de disminución / origen de trazado de píxeles como el centro de la pantalla.
- Decida una distancia de la cámara en la que 1 unidad espacial mundial = 1 píxel. Esto significa que si mueve la cámara solo 1 unidad espacial mundial, cualquier objeto a 10 unidades de distancia se desplazaría solo 1 píxel, ¡bastante lejos! Almacene esta distancia como una constante
K
.
Ahora, para cada punto, renderice en una posición usando la siguiente fórmula: screenPosition(x,y) = screenOrigin + (worldPosition(x,y) - cameraPosition(x,y)) / ((worldPosition(z) - cameraPosition(z)) * K)
... como puede ver, basamos la posición de renderizado en la z
distancia entre el punto actual y la cámara.
Juega con la posición z de la cámara hasta que veas puntos renderizados. Pero lo que verá es que todos los puntos se mostrarán en la línea central de la pantalla. Entonces tenemos que remediar eso. Intenta K=1
vs K=10
ver la diferencia.
Ahora puede mover la cámara y
para ver cómo va su cámara por encima y por debajo del plano de puntos (es decir, los puntos se renderizarán, con perspectiva correcta, por debajo o por encima de la línea media de la pantalla, respectivamente, a medida que mueve la cámara hacia arriba y hacia abajo )
Estas son pautas muy aproximadas. Hay varios detalles de implementación que dependerán de usted. El primer paso es simplemente mostrar algo, luego enmendar desde allí. Un detalle que viene a la mente es que si desea que la cámara se vea más como si estuviera mirando hacia el suelo, entonces debe cambiar su origen de representación hacia arriba, más cerca de la parte superior de la ventana gráfica. Otro detalle es que su distancia entre la cámara y el punto puede necesitar incluir una relación trigonométrica ... Creo que usar tan
ofrece una perspectiva más realista. No recuerde claramente sobre esto, pero verá rápidamente si la perspectiva parece extraña y puede adaptarse en consecuencia. No puedo ser más específico sin reescribir una muestra.
Deformación y escalado por cartelera (requerido)
Ahora que puede ver la perspectiva entre su conjunto de posiciones de puntos, y puede agregar, eliminar o mover (como con los personajes) posiciones a voluntad, también debe aplicar la perspectiva a los sprites individuales que estarán enraizados en esas posiciones.
En D2, siempre me pareció una simple función de deformación lateral que se aplica más a las vallas publicitarias que están en la parte inferior de la pantalla, que a las de la parte superior, y también más a medida que te alejas de la línea media la pantalla.
Puede haber también una escala vertical aplicada a las vallas publicitarias, por ejemplo. los árboles se acortan en comparación con su escala esperada, más cerca de la parte inferior de la pantalla (para que parezca que la cámara mira hacia los árboles; descubrí que los árboles de Tristram son la mejor manera de explorar esto de forma segura, de vuelta en el día;)).
Lo que haría es:
- Aborde la función de escala básica basada en la distancia desde la cámara al suelo en diferentes puntos. Por lo tanto, tendría una escala similar para cada línea de exploración.
- Solo después de haber hecho eso, miraría la urdimbre lateral, primero en función de la distancia desde la línea media que baja por la pantalla.
- Por último, investigaría cómo esa deformación lateral se ve afectada por la distancia a lo largo de la pantalla (y tengo la sensación de que una razón trigonométrica simple estaría en el corazón de esto).
Mosaico con perspectiva correcta (¡No es necesario leer si está utilizando Unity!)
Con suerte, lo anterior le dará sprites "de pie" deformados y correctamente posicionados (es decir, objetos que se sientan perpendiculares al plano del suelo, como personajes, árboles, casas).
Sin embargo, también debe considerar cómo hacer que las baldosas del suelo se deformen correctamente y sin problemas. Y creo que encontrarás que esa es la parte que, en particular, requería una GPU en D2. Recuerdo que en sistemas sin GPU, la opción de perspectiva estaba deshabilitada. La razón de esto seguramente habría sido que la GPU puede tomar una superficie de textura y aplicarle una corrección de perspectiva muy rápidamente, sin fallas entre los mosaicos y sin preocuparse por realizar transformaciones no afines en el código de la aplicación, lo que implica algunas matemáticas de matriz y puede ser un poco costoso:
Tengo algunas sugerencias para que lidies con esto:
- (Unidad) Use una cámara Unity para proporcionar el renderizado del plano plano con textura, luego maneje las distorsiones de la cartelera por separado según las posiciones del espacio de pantalla.
- Haga esta lógica (o incluso toda la lógica de renderizado) en sombreadores GPU.
- No use baldosas molidas en absoluto. En cambio, solo use sprites de puntos, al igual que los propios personajes, en un plano de color uniforme (por ejemplo, verde para hierba) para proporcionar detalles para que ese avión no se vea aburrido. Esto aumentará sus costos de renderizado, pero sin duda es la forma más fácil de abordar este problema.