Ici, nous nous intéressons au calcul d'une transformation de coordonnées de caméra (CC) en coordonnées normalisées de périphériques (NDC).
Pensez à E
comme la position du plan de projection dans les coordonnées de la caméra, au lieu de la position du point de vue en fonction du plan de projection. Dans les Coordonnées Caméra, le point de vue est par définition situé à l'origine, au moins dans mon interprétation de ce que signifie "Caméra Coordonnée": un cadre de coordonnées centré à partir de l'endroit où vous regardez la scène. (Vous pouvez définir mathématiquement une transformation de perspective centrée de n'importe où, mais cela signifie que votre espace d'entrée n'est pas l'espace de la caméra, comme vous le verrez dans chapter 6)
Résumé :
- vous êtes dans l'espace de la caméra, d'où votre point d'oeil est situé à (0,0,0)
- vous êtes à la recherche vers l'axe Z négatif
- votre plan de projection est parallèle à la xOy avion, avec une taille de [-1,1] dans les deux sens
Ceci est l'image ici (chaque tique est 0,5 unité):
Dans ce tableau, on peut voir que le plan de projection (côté inférieur du trapèze gris) est centré dans (0 , 0, -1), avec une taille de [-1,1] dans les deux directions X et Y.Maintenant, ce qui est demandé est de choisir (0,0, -1) pour le centre de ce plan, de choisir une position arbitraire (E.x, E.y, E.z) (suppose que E.z est négatif). Mais l'avion doit encore être parallèle à l'axe xOy et de la même taille.
Vous pouvez voir que la dimension E.xy joue un rôle très différent de E.z, par ce que E.xy sera impliqué dans une soustraction, tandis que E.z sera impliqué dans une division. Cela est facile à voir avec un exemple:
- supposer zNear = -ez (pas nécessairement le cas, mais vous pouvez en effet changer toujours frustumScale d'avoir une perspective équivalente satisfaisant cette)
- considèrent le point E (qui est le centre du plan de projection).
Quelle est sa coordonnée dans l'espace NDC? C'est (0,0, -1) par définition. Ce que vous avez fait est de soustraire E.xy, mais en divisant par -E_z.
Votre code a cette idée, mais certaines choses sont mal:
- Tout d'abord, vous avez défini
uniform vec2 E;
au lieu de uniform vec3 E;
(juste une faute de frappe, pas une grosse affaire)
- La ligne
clipPos.xy = ... ;
est sur vec2
arithmétique. Par conséquent, vous ne pouvez multiplier que par des valeurs scalaires (c'est-à-dire, un flottant) ou ajouter/soustraire des valeurs vec2
. Par conséquent, vec4(E.x, E.y, 0.0, 0.0)
est de type incorrect, vous devez utiliser E.xy
à la place, qui a le type correct vec2
. Vous devez en fait soustraire E.xy
au lieu de l'ajouter. C'est facile à voir dans mon exemple ci-dessus.
- Enfin, les choses sont plus subtiles ;-)
J'ai fait une photo pour illustrer les modifications:
Chaque tick est 1 unité dans cette image. En haut à gauche se trouve l'espace de coordonnées de votre caméra, avec zNear, zFar et deux plans de projection possibles. En bleu est celui utilisé dans l'explication et le shader here, et le rouge est celui que vous voulez maintenant utiliser. Les zones colorées correspondent à ce qui devrait être visible dans votre écran final, par ex. ce qui devrait être dans le cube [-1,1]^3 dans l'espace NDC. Par conséquent, si vous utilisez le plan de projection bleu, vous voulez obtenir l'espace en haut à droite, et si vous utilisez le plan de projection rouge, vous voulez obtenir l'espace en bas. Pour ce faire, vous pouvez observer que vous devez effectuer la mise à l'échelle et la traduction dans l'espace NDC, par ex. après la division de perspective! (Je pense que ce qui est écrit dans le livre est soit incorrect, ou interprète la question différemment).
Par conséquent, vous voulez faire, dans coordonnée euclidienne (c'est-à-dire, coordonnée non homogène, par ex.sans W coordonnées):
clipPosEuclideanRed.xy = clipPosEuclideanBlue.xy * (-E.z) - E.xy;
clipPosEuclideanRed.z = clipPosEuclideanBlue.z;
Cependant, parce que vous êtes en coordonnées homogènes, ces valeurs sont en fait:
clipPosEuclidean.xyz = clipPos.xyz/clipPos.w; // with clipPos.w = -cameraPos.z;
Par conséquent, vous devez composate par écrit:
clipPosRed.xy = clipPosBlue.xy * (-E.z) - E.xy * (-cameraPos.z);
clipPosRed.z = clipPosBlue.z;
Donc, ma solution à ce problème serait d'ajouter une seule ligne:
void main()
{
vec4 cameraPos = position + vec4(offset.x, offset.y, 0.0, 0.0);
vec4 clipPos;
clipPos.xy = cameraPos.xy * frustumScale;
// only add this line
clipPos.xy = - clipPos.xy * E.z + E.xy * cameraPos.z;
clipPos.z = cameraPos.z * (zNear + zFar)/(zNear - zFar);
clipPos.z += 2 * zNear * zFar/(zNear - zFar);
clipPos.w = -cameraPos.z;
gl_Position = clipPos;
theColor = color;
}
J'ai édité ma réponse, en améliorant ce que j'ai écrit à l'origine, et en ajoutant une explication avec du vrai code et une figure illustrant ce que je pense est demandé dans le livre. – Boris
Heureux que cela a aidé. Je multiplie puis soustrait car c'est le résultat correct: -P Souvenez-vous que dans votre NDC, la zone colorée est le cube unité: les limites X de la zone rouge dans le chiffre en haut à droite sont 1/5 et 3/5 (ces proportions sont où les lignes grises croisent le plan de projection bleu dans la figure en haut à gauche). Ensuite, si vous faites ce que vous suggérez d'appliquer à ces deux valeurs {1/5 | 3/5}, vous obtenez: ({1/5 | 3/5} - 2) * 5 = {-9; -7}. Et si vous le faites dans le bon ordre: ({1/5 | 3/5} * 5) - 2 = {-1; 1} :-) – Boris
Par ailleurs, le plan de projection ne doit pas - être dimensionné [-1; 1]. C'est juste la convention prise dans le livre: l'auteur préfère mettre à l'échelle l'espace avant (scaleFrustrum), ce qui équivaut à mettre à l'échelle le plan de projection (sauf si E.xy! = 0, je n'en ai pas vraiment tenu compte dans mon code...). Notez que Ez et frustrumScale sont redondants: déplacer le plan de projection plus loin l'oeil équivaut à réduire sa taille, ce qui équivaut à augmenter frustrumScale (équivalent à zoomer) – Boris