Солнечная система в OpenGL, положение камеры - PullRequest
0 голосов
/ 04 ноября 2018

Я хочу сделать простую солнечную систему в OpenGL, с четырьмя камерами.

То, что я хочу, это просто, просто найдите камеру с одной стороны земли.

В следующем коде я получаю MODELVIEW_MATRIX по glGetFloatv(GL_MODELVIEW_MATRIX) (строка 116)

Итак, я подумал, что { MODELVIEW_MATRIX multiple [[0],[0],[0],[1]] matrix } получит начальную точку планеты в мировой системе координат.

Но это не работает, и мне нужна помощь.

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

import math

import numpy as np

WINDOW_WIDTH = 600
WINDOW_HEIGHT = 600
WINDOW_POSITION_X = 0
WINDOW_POSITION_Y = 0

earthRevolveAngle = 180
earthRotateAngle = 0
satelliteRevolveAngle = 180
satellitePlaneAngle = 0
plutoRevolveAngle = 180
plutoRotateAngle = 0

plutoCamera = np.array([0, 0, 0])
earthPosition = np.array([0, 0, 0])


class Camera :

    def __init__(self):  #constructor
        self.loc = np.array([0.0, 50.0,  0.0])
        self.tar = np.array([0.0, 0.0, 0.0])
        self.up  = np.array([1.0, 0.0,  0.0])
        self.right = np.array([1.0, 0.0, 0.0])
        self.dir = np.array([0.0, 0.0, -1.0])
        self.asp = 1.0
        self.fov = 70
        self.near= 0.1
        self.far = 500.0


    def setCameraLoc(self, loc):
        self.loc = loc
        self.tar = self.loc + self.dir

    def setCamera(self, loc, tar, up):
        self.loc, self.tar, self.up = loc, tar, up

        self.dir = self.tar - self.loc
        l = np.linalg.norm(self.dir)
        if l > 0.0 :
            self.dir = self.dir / l

        l = np.linalg.norm(self.up)
        if l > 0.0 :
            self.up = self.up / l

        self.right = np.cross(self.dir, self.up)


    def setLens(self, fov, asp, near, far):
        self.fov, self.asp, self.near, self.far = fov, asp, near, far

    def applyCamera(self):
        gluLookAt(self.loc[0], self.loc[1], self.loc[2],
                  self.tar[0], self.tar[1], self.tar[2],
                  self.up [0], self.up [1], self.up [2])

    def applyLens(self):
        gluPerspective(self.fov, self.asp, self.near, self.far)

    def moveForward(self, step=1.0):
        self.tar += self.dir*step
        self.loc += self.dir*step

    def zoomIn(self, step=1.0):
        self.loc += self.dir*step

    def zoomOut(self, step=1.0):
        self.loc -= self.dir*step



def drawPlanet(semiMajor, semiMinor, revolveAngle, rotateAngle, shape, slope,           axisTilt) :

    global plutoCamera, earthPosition

    a = semiMajor
    b = semiMinor

    #Orbit's slope
    glRotatef(slope, 1, 0, 0)


    #Start draw orbit
    glBegin(GL_LINE_STRIP)
    for i in range(0, 361):
        theta = 2.0 * 3.141592 * i / 360.0
        x = a*math.cos(theta)
        z = b*math.sin(theta)
        glVertex3f(x, 0, z)
    glEnd()
    #End draw orbit

    theta = 2.0 * 3.141592 * (revolveAngle%360) / 360.0
    x = a * math.cos(theta)
    z = b * math.sin(theta)


    glRotatef(revolveAngle, 0, 1, 0) 
    glTranslatef( math.sqrt( x**2 + z**2 ) , 0, 0) 


    glRotatef(rotateAngle, 0, 1, 0)
    glRotatef(axisTilt, 0, 0, 1)

    t = glGetFloatv(GL_MODELVIEW_MATRIX)

    if(shape == "satellite"):
        glScalef(0.4,0.4,0.4)
        glutSolidTetrahedron()
        glScalef(2.5,2.5,2.5)

    elif(shape == "earth"):
        glutWireCube(1)
        earthPosition = t * np.matrix( [[0],[0],[0],[1]] )

    elif(shape == "pluto"):
        glScalef(0.4,0.4,0.4)
        glutWireOctahedron()
        glScalef(2.5,2.5,2.5)


def drawScene() :
    global earthRevolveAngle, earthRotateAngle, satelliteAngle, satelliteRevolveAngle, satellitePlaneAngle, plutoRevolveAngle, plutoRotateAngle


    # draw solar
    glColor3f(1,0,0)
    glutWireSphere(1.0, 20, 20)

    glPushMatrix()


    # draw earth
    glColor3f(0,0.5,1.0)
    earthRevolveAngle+=0.05 # earth's revolution
    earthRotateAngle+=0.2
    drawPlanet(5, 5, earthRevolveAngle, earthRotateAngle, "earth",0,15)


    # draw satellite

    glColor3f(0.7,0.7,0.7)
    satelliteRevolveAngle+=1.5
    satellitePlaneAngle += 0.25
    glRotatef(satellitePlaneAngle, 1, 0, 0)
    drawPlanet(1, 1, satelliteRevolveAngle, 1, "satellite",0,0)


    # draw pluto
    glPopMatrix() # comeback to solar central coordinate
    glPushMatrix()

    glColor3f(0.9,0.7,0.26)
    plutoRevolveAngle+=0.0125 # pluto's revolution
    plutoRotateAngle+=0.1 # pluto's rotation
    drawPlanet(10, 8, plutoRevolveAngle,plutoRotateAngle, "pluto",0,0)
    glPopMatrix()

Cam = Camera()

def disp() :

    global plutoCamera, earthPosition, Cam

    # reset buffer
    glClear(GL_COLOR_BUFFER_BIT)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    # Camera view setting
    Cam.setLens(30,1.0,0.1,1000)
    Cam.applyLens()


    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()



    # first quadrant
    glViewport(int(WINDOW_POSITION_X+WINDOW_WIDTH/2), int(WINDOW_POSITION_Y + WINDOW_HEIGHT/2), int(WINDOW_WIDTH/2), int(WINDOW_HEIGHT/2)) 
    glPushMatrix()
    Cam.setCamera( np.array([0,0,1]), np.array([0,0,100]), np.array([0,1,0]))
    Cam.applyCamera()

    drawScene()
    glPopMatrix()



    # second quadrant
    glViewport(int(WINDOW_POSITION_X), int(WINDOW_POSITION_Y + WINDOW_HEIGHT/2), int(WINDOW_WIDTH/2), int(WINDOW_HEIGHT/2) ) 
    glPushMatrix()
    Cam.setCamera( np.array([30,30,30]), np.array([0,0,0]), np.array([0,1,0]))
    Cam.applyCamera()

    drawScene()
    glPopMatrix()



    # third quadrant
    glViewport(WINDOW_POSITION_X, WINDOW_POSITION_Y, int(WINDOW_WIDTH/2) , int(WINDOW_HEIGHT/2) )
    glPushMatrix()
    Cam.setCamera( plutoCamera, np.array([0,0,0]), np.array([0,1,0]))
    Cam.applyCamera()
    drawScene()
    glPopMatrix()


    # fourth quadrant
    glViewport(int(WINDOW_POSITION_X+WINDOW_WIDTH/2), WINDOW_POSITION_Y, int(WINDOW_WIDTH/2), int(WINDOW_HEIGHT/2) ) 
    glPushMatrix()
    Cam.setCamera( earthPosition, np.array([0,0,0]) , np.array([0,1,0]))
    Cam.applyCamera()
    drawScene()
    glPopMatrix()

    glFlush()


def main():
    # windowing
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB)
    glutInitWindowSize(WINDOW_WIDTH,WINDOW_HEIGHT)
    glutInitWindowPosition(WINDOW_POSITION_X,WINDOW_POSITION_Y)
    glutCreateWindow(b"Simple Solar_201624489_ParkChangHae")

    glClearColor(0, 0.0, 0.0, 0)

    # register callbacks
    glutDisplayFunc(disp)
    glutIdleFunc(disp)

    # enter main infinite-loop
    glutMainLoop()

if __name__=="__main__":
    main()

1 Ответ

0 голосов
/ 04 ноября 2018

Оператор * делает не то, что вы ожидаете, это умножение массива, но не умножение матрицы. Он будет выполнять компонентное умножение элементов. См. Как отличается умножение для классов NumPy Matrix и Array? и Numerical operations on arrays.

Используйте numpy.dot или numpy.matmul для преобразования вектора в матрицу.
Результат преобразования 4-компонентного вектора ( Однородные координаты ) в матрицу 4 * 4, все еще является 4-компонентным вектором. В общем случае вам нужно будет сделать перспективное деление после преобразования. Но матрица вида модели - это Ортогональная матрица , поэтому достаточно использовать первые 3 компонента результата, поскольку 4-й компонент всегда равен 1:

pos = np.array( [0,0,0,1] )
pos = np.dot( pos, t )
earthPosition = pos[0:3]

Но обратите внимание, позиция координаты (0, 0, 0, 1) в пространстве вида является частью перевода (4-я строка) матрицы вида модели:

earthPosition = t[3][0:3]

К сожалению, это не то, что вы хотите сделать, потому что вы хотите знать мировое положение земли, но не положение взгляда. Так как glGetFloatv(GL_MODELVIEW_MATRIX) возвращает матрицу вида модели, преобразование вычисляет позицию вида, но не позицию мира. Вы должны преобразовать матрицу модели, но не матрицу вида модели. Поскольку вы не можете отделить матрицу модели от матрицы представления модели, это не так просто. То, что вы можете получить, это матрица вида. Используя матрицу вида и матрицу вида модели, вы получаете мировую позицию. Преобразование в матрицу модели аналогично преобразованию в матрицу вида модели и матрицу обратного вида:

p_world = inverse(view_matrix) * model_view_matrix * p_model

Рекомендую получить матрицу вида и рассчитать матрицу вида обратной модели в классе Cam сразу после ее установки на lookAt. Обратная матрица может быть вычислена как numpy.linalg.inv:

def applyCamera(self):
    gluLookAt(self.loc[0], self.loc[1], self.loc[2],
              self.tar[0], self.tar[1], self.tar[2],
              self.up [0], self.up [1], self.up [2])
    self.viewmat = glGetFloatv(GL_MODELVIEW_MATRIX)
    self.inv_viewmat = np.linalg.inv(self.viewmat)

Наконец, мировая позиция - это простое преобразование 4-й строки матрицы модельного вида в матрицу обратного вида:

global plutoCamera, earthPosition, Cam

.....

model_view = glGetFloatv(GL_MODELVIEW_MATRIX)

if(shape == "satellite"):
    glScalef(0.4,0.4,0.4)
    glutSolidTetrahedron()
    glScalef(2.5,2.5,2.5)

elif(shape == "earth"):
    glutWireCube(1)

    pos = np.dot( model_view[3], Cam.inv_viewmat )
    earthPosition = pos[0:3]

elif(shape == "pluto"):
    glScalef(0.4,0.4,0.4)
    glutWireOctahedron()
    glScalef(2.5,2.5,2.5)

    pos = np.dot( model_view[3], Cam.inv_viewmat )
    plutoCamera = pos[0:3]

Предварительный просмотр:

...