在Pygame + PyOpenGL中使用不同纹理的天空盒渲染不一致 [英] Inconsistent skybox rendering using different textures in Pygame + PyOpenGL

查看:161
本文介绍了在Pygame + PyOpenGL中使用不同纹理的天空盒渲染不一致的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

受我答案不完整 this在PyOpenGL中实现一个简单的skybox 教程,对OpenGL 2.1/GLSL 120和python2.7-isms进行必要的微调.在大多数情况下,它可以成功运行,但是根据我传递给立方体贴图的六张图像,这些图像要么最终在一对相对的表面之间交换,要么被随机旋转!以下是此演示的主要类别:

Motivated by my incomplete answer to this question, I am implementing a simple skybox in PyOpenGL in accordance with this tutorial, making minor tweaks as needed for OpenGL 2.1/GLSL 120 and python2.7-isms. For the most part, it works successfully, but depending on what six images I pass to my cubemap, the images either end up swapped between a single pair of opposite faces or are randomly rotated! Below is the main class of this demo:

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))

我使用以下顶点和片段着色器显示天空盒的立方体贴图:

I use the following vertex and fragment shaders for displaying the cubemaps for the skybox:

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

我相信经过反复尝试后,该错误是由于pygame加载了Skybox图片.我已经测试了三组Skybox图像.每个人都有一个不同的视觉错误和破解方法来修复它们,我在上面的代码中已经指出.以下是用于测试的三个天空盒的来源(请确保将图像重命名,以便它们在各自的文件名中包含rightlefttopbottombackfront ).

I believe after much playing around that the error is in pygame's loading of the skybox images. I have tested three sets of skybox images. Each one has a different visual error and hack to fix them, which I have noted in the above code. Here are the sources for the three skyboxes for testing (be sure to rename the images so that they include right, left, top, bottom, back, or front in their respective file names).

  • ./images1/: here
  • ./images2/: here
  • ./images3/: here (using the "rays" images in this zip)

这三个天空盒均使用不同的图像格式(分别为bmp,tga和png).如何在不依赖看似随机旋转或图像交换的情况下,始终如一地稳健地处理所有这些和将来的图像情况?任何帮助或见识将不胜感激.

All of these three skyboxes use different image formats (bmp, tga, and png respectively). How can I consistently handle all of these and future image cases robustly without relying on seemingly random rotations or image swaps? Any help or insight would be greatly appreciated.

更新: 我创建了一个 github存储库,您可以在其中测试代码而不必创建main.py和着色器,下载图像,然后自己重​​命名和组织内容.如果您有兴趣对其进行测试,这将使代码更容易运行.

Update: I have created a github repository where you can test out the code without having to create a main.py and shaders, download the images, and rename and organize the contents yourself. This should make the code a lot easier to run in case you are interested in testing it out.

以下是我正在使用的所有内容的版本:

Here are the versions of everything that I am using:

  • python 2.7.12
  • pygame 1.9.2b1
  • pyopengl 3.1.0(使用opengl 2.1和GLSL 120)

让我知道您是否需要其他信息!

Let me know if you need any other information!

推荐答案

因此,事实证明,我渲染天空盒时遇到的所有问题都可以归结为两个原因,这些原因都不是由于pygame如何加载各种文件格式的图像!

So, it turns out that all of the issues that I was having rendering the skybox could be boiled down to two causes, none of which were due to inconsistencies in how pygame loads images of various file formats!

  1. 在如何附加立方体的两个面之间的接缝方面,天空盒图像彼此不一致.这就解释了为什么每个skybox图像测试结果都有不同的问题.遵循问题中描述的位于立方体内部的惯例,我翻转并重新绘制了绘画中的图像.
  2. 但是,仅凭这一点还不够.事实证明,立方体贴图"中z轴的 OpenGL 约定被翻转.这导致正面和背面相互交换.我能想到的最简单的解决方法是在顶点着色器中交换纹理坐标.这是校正后的顶点着色器.

  1. The skybox images were inconsistent with one another in how the seams between two faces of the cubes were attached. This explained why each skybox image test result had different issues. Following the convention of being inside the cube describe in this question, I flipped and resaved the images in paint.
  2. That alone was not enough, however. It turns out that the OpenGL convention for the z-axis in "cubemap-land" is flipped. This caused the front and back faces to be swapped with one another. The simplest fix that I could come up with is swapping the texture coordinates in the vertex shader. Here is the corrected 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);
}


我在问题中提到的github中有代码,以反映这些更改并改进用于手动环顾四周的相机.


I have the code in the github mentioned in the question to reflect these changes as well as improve the camera for manually looking around.

对于任何有兴趣的人,这里都是最终结果的动画gif!

Here is an animated gif of the final result for anyone who is interested!

这篇关于在Pygame + PyOpenGL中使用不同纹理的天空盒渲染不一致的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆