2016-11-12 2 views
0

Движимого моей incomplete answer к this вопросу, я реализующий простой скайбокс в PyOpenGL в соответствии с this учебником, делая мелкие недочеты по мере необходимости для OpenGL 2.1/GLSL 120 и python2.7 -измы. По большей части он работает успешно, но в зависимости от того, какие шесть изображений я перехожу к моему кубкету, изображения либо заканчиваются обменом между одной парой противоположных граней или случайным образом повернуты! Ниже основной класс этого примера:Противоречивый скайбокс рендеринга с использованием различных текстур в Pygame + 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)) 

Я использую следующие вершины и фрагмент шейдеры для отображения cubemaps для скайбоксе:

./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); 
} 

Я верю, что после многого, rror находится в загрузке загрузочных изображений skybox в pygame. Я проверил три набора изображений skybox. У каждого из них есть другая визуальная ошибка и взломать их, что я заметил в приведенном выше коде. Вот источники для трех skyboxes для тестирования (обязательно переименуйте изображения, чтобы они включали right, left, top, bottom, back или front в их соответствующие имена файлов).

  • ./images1/: here
  • ./images2/: here
  • ./images3/: here (с использованием "лучей" изображения в этой ZIP)

Все эти три скайбоксов используют различные форматы изображений (BMP, tga и png соответственно). Как я могу последовательно обрабатывать все эти и будущие образы изображений, не полагаясь на кажущиеся случайными вращениями или свопами изображений? Любая помощь или понимание будут оценены.

Update: Я создал github repository, где вы можете проверить код без необходимости создавать main.py и шейдеры, загружать изображения, переименовывать и организовывать содержимое самостоятельно. Это должно сделать код намного легче запускать, если вы заинтересованы в его тестировании.

Вот версии всего, что я использую:

  • питона 2.7.12
  • Pygame 1.9.2b1
  • PyOpenGL 3.1.0 (с использованием OpenGL 2.1 и GLSL 120)

Дайте мне знать, если вам нужна какая-либо другая информация!

ответ

0

Итак, выяснилось, что все проблемы, которые я получал от skybox, можно свести к двум причинам, ни одна из которых не была связана с несогласованностью в том, как pygame загружает изображения различных форматов файлов!

  1. Изображения skybox несовместимы друг с другом в том, как были прикреплены швы между двумя гранями кубов. Это объясняет, почему каждый результат теста изображения Skybox имел разные проблемы. Следуя соглашению о том, что внутри куба описывается в вопросе this, я перевернул и сохранил изображения в краске.
  2. Однако этого было недостаточно. Оказывается, что соглашение OpenGL для оси z в «cubemap-land» равно перевернуто. Это привело к тому, что передняя и задняя поверхности были заменены на друг на друга. Самое простое исправление, которое я мог бы придумать, - это обмен текстурными координатами в вершинном шейдере. Вот скопированный вершинный шейдер.

    #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); 
    } 
    

У меня есть код в GitHub, упомянутых в вопросе, чтобы отразить эти изменения, а также улучшить камеру для ручного озираясь.

Анимированный gif итогового результата для всех, кто заинтересован!

Animated GIF image

Смежные вопросы