2016-11-12 4 views
0

Motiviert durch meine incomplete answer zu this Frage, ich bin ein einfaches Skybox in PyOpenGL gemäß this Tutorial Implementierung, so dass kleinere Verbesserungen wie für OpenGL 2.1/GLSL 120 und python2.7 benötigt -isms. In den meisten Fällen funktioniert es erfolgreich, aber je nachdem, welche sechs Bilder ich an meine Cubemap weitergebe, werden die Bilder entweder zwischen einem einzelnen Paar gegenüberliegender Gesichter ausgetauscht oder zufällig rotiert! Im Folgenden finden Sie die Hauptklasse dieser Demo:Uneinheitliche Skybox Rendering verschiedene Texturen in Pygame + mit PyOpenGL

import pygame 
import sys 
import time 
import glob 
import numpy as np 
from ctypes import * 
from OpenGL.GL import * 
from OpenGL.GL import shaders 
from OpenGL.GLU import * 

def load_shaders(vert_url, frag_url): 
    vert_str = "\n".join(open(vert_url).readlines()) 
    frag_str = "\n".join(open(frag_url).readlines()) 
    vert_shader = shaders.compileShader(vert_str, GL_VERTEX_SHADER) 
    frag_shader = shaders.compileShader(frag_str, GL_FRAGMENT_SHADER) 
    program = shaders.compileProgram(vert_shader, frag_shader) 
    return program 

def load_cubemap(folder_url): 
    tex_id = glGenTextures(1) 
    face_order = ["right", "left", "top", "bottom", "back", "front"] 
    """ 
    #hack that fixes issues for ./images1/ 
    face_order = ["right", "left", "top", "bottom", "front", "back"] 
    """ 
    face_urls = sorted(glob.glob(folder_url + "*")) 
    glActiveTexture(GL_TEXTURE0) 
    glBindTexture(GL_TEXTURE_CUBE_MAP, tex_id) 
    for i, face in enumerate(face_order): 
     face_url = [face_url for face_url in face_urls if face in face_url.lower()][0] 
     face_image = pygame.image.load(face_url).convert() 
     """ 
     #hack that fixes issues for ./images2/ 
     if face == "bottom": 
      face_image = pygame.transform.rotate(face_image, 270) 
     if face == "top": 
      face_image = pygame.transform.rotate(face_image, 90) 
     """ 
     """ 
     #hack that fixes issues for ./images3/ 
     if face == "bottom" or face == "top": 
      face_image = pygame.transform.rotate(face_image, 180) 
     """ 
     face_surface = pygame.image.tostring(face_image, 'RGB') 
     face_width, face_height = face_image.get_size() 
     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, face_width, face_height, 0, GL_RGB, GL_UNSIGNED_BYTE, face_surface) 
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE) 
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0) 
    return tex_id 

def render(): 
    global width, height, program 
    global rotation, cubemap 

    glEnable(GL_DEPTH_TEST) 
    glEnable(GL_TEXTURE_2D) 
    glEnable(GL_TEXTURE_CUBE_MAP) 

    skybox_right = [1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1] 
    skybox_left = [-1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1] 
    skybox_top = [-1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1] 
    skybox_bottom = [-1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1] 
    skybox_back = [-1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1] 
    skybox_front = [-1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1] 

    skybox_vertices = np.array([skybox_right, skybox_left, skybox_top, skybox_bottom, skybox_back, skybox_front], dtype=np.float32).flatten() 
    skybox_vbo = glGenBuffers(1) 
    glBindBuffer(GL_ARRAY_BUFFER, skybox_vbo) 
    glBufferData(GL_ARRAY_BUFFER, skybox_vertices.nbytes, skybox_vertices, GL_STATIC_DRAW) 
    glBindBuffer(GL_ARRAY_BUFFER, 0) 

    glClear(GL_COLOR_BUFFER_BIT) 
    glClear(GL_DEPTH_BUFFER_BIT) 

    glMatrixMode(GL_PROJECTION) 
    glLoadIdentity() 
    gluPerspective(60, float(width)/height, 0.1, 1000) 
    glMatrixMode(GL_MODELVIEW) 
    glLoadIdentity() 
    #glRotate(rotation, 0, 1, 0)#spin around y axis 
    #glRotate(rotation, 1, 0, 0)#spin around x axis 
    glRotate(rotation, 1, 1, 1)#rotate around x, y, and z axes 

    glUseProgram(program) 
    glDepthMask(GL_FALSE) 
    glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap) 
    glEnableClientState(GL_VERTEX_ARRAY) 
    glBindBuffer(GL_ARRAY_BUFFER, skybox_vbo) 
    glVertexPointer(3, GL_FLOAT, 0, None) 
    glDrawArrays(GL_TRIANGLES, 0, 36) 
    glBindBuffer(GL_ARRAY_BUFFER, 0) 
    glDisableClientState(GL_VERTEX_ARRAY) 
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0) 
    glDepthMask(GL_TRUE) 
    glUseProgram(0) 

    pygame.display.flip() 

if __name__ == "__main__": 
    title = "Skybox" 
    target_fps = 60 
    (width, height) = (800, 600) 
    flags = pygame.DOUBLEBUF|pygame.OPENGL 
    screen = pygame.display.set_mode((width, height), flags) 
    prev_time = time.time() 
    rotation = 0 
    cubemap = load_cubemap("./images1/")#front and back images appear swapped 
    #cubemap = load_cubemap("./images2/")#top and bottom images appear rotated by 90 and 270 degrees respectively 
    #cubemap = load_cubemap("./images3/")#top and bottom images appear rotated by 180 degrees 
    program = load_shaders("./shaders/skybox.vert", "./shaders/skybox.frag") 
    pause = False 

    while True: 
     #Handle the events 
     for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
       sys.exit() 
      elif event.type == pygame.KEYDOWN: 
       if event.key == pygame.K_SPACE: 
        pause = not pause 

     #Do computations and render stuff on screen 
     if not pause: 
      rotation += 1 
     render() 

     #Handle timing code for desired FPS 
     curr_time = time.time() 
     diff = curr_time - prev_time 
     delay = max(1.0/target_fps - diff, 0) 
     time.sleep(delay) 
     fps = 1.0/(delay + diff) 
     prev_time = curr_time 
     pygame.display.set_caption("{0}: {1:.2f}".format(title, fps)) 

ich die Cubemaps für die Skybox folgende Vertex- und Fragment-Shader verwenden für die Anzeige:

./shaders/skybox.vert

#version 120 
varying vec3 tex_coords; 

void main() 
{ 
    gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; 
    tex_coords = vec3(gl_Vertex); 
} 

./shaders/skybox.frag

#version 120 
varying vec3 tex_coords; 
uniform samplerCube skybox; 

void main() 
{ 
    gl_FragColor = textureCube(skybox, tex_coords); 
} 

Ich glaube nach viel herumspielen, dass die e rror ist in pygames Laden der Skybox Bilder. Ich habe drei Sets von Skybox-Bildern getestet. Jeder hat einen anderen visuellen Fehler und Hack, um sie zu beheben, die ich in dem obigen Code notiert habe. Hier sind die Quellen für die drei Skyboxen zum Testen (vergesse nicht, die Bilder so umzubenennen, dass sie right, left, top, , back oder front in ihren jeweiligen Dateinamen enthalten).

Alle diese drei Skyboxes verwenden unterschiedliche Bildformate (BMP (die "Strahlen" Bilder in diesem zip verwenden), tga bzw. png). Wie kann ich all diese und zukünftige Bildbearbeitungen zuverlässig und robust handhaben, ohne auf scheinbar zufällige Rotationen oder Image-Swaps angewiesen zu sein? Jede Hilfe oder Einsicht würde sehr geschätzt werden.

Update: Ich habe erstellt ein github repository wo Sie den Code testen können, ohne eine main.py und Shadern erstellen zu müssen, die Bilder herunterzuladen und zu umbenennen und die Inhalte selbst zu organisieren. Dies sollte den Code viel einfacher zu laufen, wenn Sie daran interessiert sind, es zu testen.

Hier sind die Versionen von allem, was ich verwende:

  • Python 2.7.12
  • pygame 1.9.2b1
  • PyOpenGL 3.1.0 (unter Verwendung von OpenGL 2.1 und GLSL 120)

Lassen Sie mich wissen, wenn Sie weitere Informationen benötigen!

Antwort

0

Also, es stellt sich heraus, dass alle Probleme, die ich Rendering der Skybox hatte, auf zwei Ursachen, von denen keine aufgrund von Inkonsistenzen in wie Pygame geladen Bilder von verschiedenen Dateiformaten!

  1. Die Skybox-Bilder stimmten nicht miteinander überein, wie die Nähte zwischen zwei Seiten der Würfel befestigt waren. Dies erklärt, warum jedes Skybox-Bild-Testergebnis unterschiedliche Probleme hatte. Nach der Konvention, innerhalb des Würfels in this Frage zu sein, blätterte ich und rettete die Bilder in Farbe.
  2. Das allein war jedoch nicht genug. Es stellt sich heraus, dass die OpenGL Konvention für die Z-Achse in "Cubemap-Land" ist umgedreht. Dies führte dazu, dass die Vorder- und Rückseite miteinander ausgetauscht wurden. Die einfachste Lösung, die ich mir vorstellen konnte, ist das Vertauschen der Texturkoordinaten im Vertex-Shader. Hier ist der korrigierte Vertex-Shader.

    #version 120 
    varying vec3 tex_coords; 
    
    void main() 
    { 
        gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; 
        tex_coords = vec3(gl_Vertex) * vec3(1, 1, -1); 
    } 
    

Ich habe den Code in der Github in der Frage erwähnt um diese Änderungen zu verbessern sowie um die Kamera zu reflektieren für manuell suchen.

Hier ist ein animiertes Gif des Endergebnisses für jeden, der interessiert ist!

Animated GIF image