first try
This commit is contained in:
199
np.py
Normal file
199
np.py
Normal file
@@ -0,0 +1,199 @@
|
||||
import numpy as np
|
||||
import time
|
||||
import math
|
||||
|
||||
# --- Hilfsfunktionen ---
|
||||
|
||||
def normalize(v):
|
||||
""" Normalisiert einen Vektor (macht seine Länge zu 1). """
|
||||
norm = np.linalg.norm(v)
|
||||
if norm == 0:
|
||||
return v
|
||||
return v / norm
|
||||
|
||||
def look_at_matrix(eye, target, up):
|
||||
"""
|
||||
Erzeugt eine View-Matrix, die Weltkoordinaten in Kamerakoordinaten transformiert.
|
||||
:param eye: Position der Kamera (np.array).
|
||||
:param target: Punkt, auf den die Kamera schaut (np.array).
|
||||
:param up: Up-Vektor der Kamera (zeigt, wo "oben" für die Kamera ist, np.array).
|
||||
:return: 4x4 View-Matrix (NumPy-Array).
|
||||
"""
|
||||
# Kamerakoordinatensystem berechnen
|
||||
# z-Achse der Kamera (schaut oft *entgegen* der Blickrichtung)
|
||||
forward = normalize(eye - target) # Konvention: Kamera schaut entlang -Z
|
||||
if np.linalg.norm(np.cross(up, forward)) < 1e-6:
|
||||
# Fallback, wenn 'up' und 'forward' (fast) parallel sind
|
||||
# Wähle einen anderen temporären Up-Vektor
|
||||
if abs(forward[1]) < 0.99: # Wenn forward nicht fast vertikal ist
|
||||
temp_up = np.array([0.0, 1.0, 0.0])
|
||||
else: # Wenn forward fast vertikal ist
|
||||
temp_up = np.array([0.0, 0.0, -1.0 if forward[1] > 0 else 1.0])
|
||||
right = normalize(np.cross(temp_up, forward))
|
||||
else:
|
||||
right = normalize(np.cross(up, forward))
|
||||
|
||||
# Den tatsächlichen Up-Vektor der Kamera neu berechnen, damit er orthogonal ist
|
||||
camera_up = normalize(np.cross(forward, right))
|
||||
|
||||
# Rotationsmatrix erstellen (transformiert von Welt zu Kamera-Ausrichtung)
|
||||
# Dies ist die Inverse (oder Transponierte, da Rotationsmatrix) der Matrix,
|
||||
# die die Kamera-Basisvektoren in Weltkoordinaten ausdrückt.
|
||||
rotation = np.identity(4)
|
||||
rotation[0, 0:3] = right
|
||||
rotation[1, 0:3] = camera_up
|
||||
rotation[2, 0:3] = forward # Beachte: forward zeigt von target zu eye
|
||||
|
||||
# Translationsmatrix erstellen (verschiebt die Welt, sodass die Kamera am Ursprung ist)
|
||||
translation = np.identity(4)
|
||||
translation[0:3, 3] = -eye
|
||||
|
||||
# View-Matrix: Zuerst Translation, dann Rotation
|
||||
view_mat = np.dot(rotation, translation)
|
||||
return view_mat
|
||||
|
||||
def perspective_projection(point_cam_space, fov_deg, aspect_ratio, near, far):
|
||||
"""
|
||||
Projiziert einen Punkt aus dem Kamera-Koordinatenraum auf eine 2D-Ebene.
|
||||
Gibt normalisierte Gerätekoordinaten (NDC) zurück (-1 bis +1).
|
||||
:param point_cam_space: Punkt in Kamera-Koordinaten (3D NumPy-Array).
|
||||
:param fov_deg: Field of View (vertikal) in Grad.
|
||||
:param aspect_ratio: Seitenverhältnis (Breite / Höhe) der Bildebene.
|
||||
:param near: Nahe Clipping-Ebene.
|
||||
:param far: Ferne Clipping-Ebene.
|
||||
:return: Projizierter 2D-Punkt in NDC (NumPy-Array) oder None, wenn außerhalb.
|
||||
"""
|
||||
# Überprüfen, ob der Punkt vor der Kamera und innerhalb der Clipping-Ebenen liegt
|
||||
# Kamera schaut entlang -Z, also muss Z negativ sein.
|
||||
if point_cam_space[2] > -near or point_cam_space[2] < -far:
|
||||
return None # Außerhalb des Sichtfelds (vor near oder hinter far)
|
||||
|
||||
# Einfache Perspektivprojektion (vereinfachte Formel)
|
||||
# Skalierungsfaktor basierend auf FOV
|
||||
f = 1.0 / math.tan(math.radians(fov_deg) / 2.0)
|
||||
|
||||
# Projizierte Koordinaten auf der Bildebene (vor Normalisierung)
|
||||
# Annahme: Bildebene bei z = -near (oder -1, je nach Konvention)
|
||||
# Hier verwenden wir die gebräuchlichere Projektionsmatrix-Formel-Basis:
|
||||
x_proj = (f / aspect_ratio) * point_cam_space[0] / -point_cam_space[2]
|
||||
y_proj = f * point_cam_space[1] / -point_cam_space[2]
|
||||
|
||||
# Normalisierte Gerätekoordinaten (NDC) sind typischerweise im Bereich [-1, 1]
|
||||
# Hier geben wir die projizierten Koordinaten zurück, wie sie auf einer Bildebene
|
||||
# bei z=-1 erscheinen würden, skaliert durch FOV und Aspekt.
|
||||
# Eine vollständige Projektionsmatrix würde auch Z für Tiefentests umwandeln.
|
||||
return np.array([x_proj, y_proj])
|
||||
|
||||
|
||||
# --- Simulationsparameter ---
|
||||
object_start_pos = np.array([0.0, 0.0, 10.0]) # Startposition des Objekts (x, y, z)
|
||||
# Einfache lineare Bewegung
|
||||
# object_velocity = np.array([1.0, 0.5, -0.8]) # Bewegung pro Sekunde
|
||||
# Alternative: Kreisbewegung in der XZ-Ebene um (0, 0, 10) mit Radius 5
|
||||
radius = 5.0
|
||||
angular_speed = math.radians(45) # 45 Grad pro Sekunde
|
||||
|
||||
# Kameraeinstellungen
|
||||
# Jede Kamera hat eine Position ('pos'), einen Punkt, auf den sie schaut ('target'),
|
||||
# und einen 'up'-Vektor, der die Orientierung definiert.
|
||||
cameras = [
|
||||
{
|
||||
"name": "Kamera 1 (Frontal)",
|
||||
"pos": np.array([0.0, 0.0, 0.0]),
|
||||
"target": object_start_pos, # Schaut initial auf den Startpunkt
|
||||
"up": np.array([0.0, 1.0, 0.0]), # Standard-Ausrichtung (Y ist oben)
|
||||
"fov_deg": 60, # Field of View in Grad
|
||||
"aspect_ratio": 16.0 / 9.0 # Typisches Breitbild
|
||||
},
|
||||
{
|
||||
"name": "Kamera 2 (Seitlich Links)",
|
||||
"pos": np.array([-15.0, 0.0, 5.0]),
|
||||
"target": object_start_pos,
|
||||
"up": np.array([0.0, 1.0, 0.0]),
|
||||
"fov_deg": 45,
|
||||
"aspect_ratio": 1.0 # Quadratisch
|
||||
},
|
||||
{
|
||||
"name": "Kamera 3 (Von Oben)",
|
||||
"pos": np.array([0.0, 15.0, 10.0]),
|
||||
"target": object_start_pos,
|
||||
"up": np.array([0.0, 0.0, -1.0]), # Z zeigt nach unten, da Y oben war
|
||||
"fov_deg": 70,
|
||||
"aspect_ratio": 4.0 / 3.0
|
||||
}
|
||||
]
|
||||
|
||||
# Clipping-Ebenen (gemeinsam für alle Kameras hier, könnte pro Kamera sein)
|
||||
near_plane = 0.1
|
||||
far_plane = 100.0
|
||||
|
||||
simulation_duration = 10 # Sekunden
|
||||
time_step = 1 # Zeitintervall für "Aufnahmen" in Sekunden
|
||||
|
||||
# --- Simulationsschleife ---
|
||||
current_object_pos = object_start_pos.copy()
|
||||
current_time = 0.0
|
||||
angle = 0.0 # Für Kreisbewegung
|
||||
|
||||
print("Starte Simulation...")
|
||||
|
||||
while current_time <= simulation_duration:
|
||||
print(f"\n--- Zeitpunkt: {current_time:.1f}s ---")
|
||||
|
||||
# Objektposition aktualisieren (Beispiel: Kreisbewegung)
|
||||
angle = angular_speed * current_time
|
||||
current_object_pos[0] = radius * math.cos(angle) + 0 # Kreiszentrum X=0
|
||||
current_object_pos[2] = radius * math.sin(angle) + 10 # Kreiszentrum Z=10
|
||||
# Alternative: Lineare Bewegung
|
||||
# current_object_pos = object_start_pos + object_velocity * current_time
|
||||
|
||||
print(f"Objekt Welt-Position: ({current_object_pos[0]:.2f}, {current_object_pos[1]:.2f}, {current_object_pos[2]:.2f})")
|
||||
|
||||
# Objektposition in homogene Koordinaten umwandeln (für Matrixmultiplikation)
|
||||
object_pos_h = np.append(current_object_pos, 1.0)
|
||||
|
||||
# Für jede Kamera die Ansicht berechnen
|
||||
for i, cam in enumerate(cameras):
|
||||
print(f"\n Kamera {i+1}: {cam['name']}")
|
||||
print(f" Position: ({cam['pos'][0]:.2f}, {cam['pos'][1]:.2f}, {cam['pos'][2]:.2f})")
|
||||
# Kamera schaut immer auf die aktuelle Objektposition
|
||||
current_target = current_object_pos
|
||||
print(f" Schaut auf (Target): ({current_target[0]:.2f}, {current_target[1]:.2f}, {current_target[2]:.2f})")
|
||||
print(f" Up-Vektor: ({cam['up'][0]:.2f}, {cam['up'][1]:.2f}, {cam['up'][2]:.2f})")
|
||||
print(f" FOV: {cam['fov_deg']} Grad, Aspekt: {cam['aspect_ratio']:.2f}")
|
||||
|
||||
|
||||
# View-Matrix berechnen
|
||||
view_mat = look_at_matrix(cam['pos'], current_target, cam['up'])
|
||||
|
||||
# Objektposition in Kamera-Koordinaten transformieren
|
||||
# point_in_cam_space_h = view_mat @ object_pos_h # Python 3.5+ für @ Operator
|
||||
point_in_cam_space_h = np.dot(view_mat, object_pos_h)
|
||||
|
||||
# De-homogenisieren (falls w nicht 1 ist, hier sollte es 1 sein nach View-Transform)
|
||||
point_in_cam_space = point_in_cam_space_h[:3] / point_in_cam_space_h[3]
|
||||
|
||||
print(f" Objekt in Kamera-Koordinaten: ({point_in_cam_space[0]:.2f}, {point_in_cam_space[1]:.2f}, {point_in_cam_space[2]:.2f})")
|
||||
|
||||
# In 2D projizieren
|
||||
projected_point_ndc = perspective_projection(
|
||||
point_in_cam_space,
|
||||
cam['fov_deg'],
|
||||
cam['aspect_ratio'],
|
||||
near_plane,
|
||||
far_plane
|
||||
)
|
||||
|
||||
if projected_point_ndc is not None:
|
||||
print(f" Projizierte 2D-Koordinaten (NDC): ({projected_point_ndc[0]:.3f}, {projected_point_ndc[1]:.3f})")
|
||||
# NDC-Koordinaten (-1 bis 1) müssten noch auf Pixelkoordinaten umgerechnet werden,
|
||||
# wenn man ein echtes Bild rendern wollte (z.B. X_pixel = (X_ndc + 1) * width / 2).
|
||||
else:
|
||||
print(" Objekt befindet sich ausserhalb des Sichtbereichs (Clipping).")
|
||||
|
||||
# Zum nächsten Zeitschritt gehen
|
||||
current_time += time_step
|
||||
if current_time <= simulation_duration:
|
||||
time.sleep(1) # Kurze Pause zur besseren Lesbarkeit der Ausgabe
|
||||
|
||||
print("\nSimulation beendet.")
|
||||
Reference in New Issue
Block a user