在Pygame+PyOpenGL中使用不同纹理的skybox渲染不一致

2024-10-02 22:36:31 发布

您现在位置:Python中文网/ 问答频道 /正文

受我的incomplete answerto this问题的启发,我正在根据this教程在PyOpenGL中实现一个简单的skybox,根据需要对OpenGL 2.1/glsl120和python2.7-isms做一些小的调整。在大多数情况下,它是成功的,但是取决于我传递给我的立方体地图的六幅图像,这些图像要么在一对相反的脸之间交换,要么被随机旋转!下面是这个演示的主要类:

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

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

./shaders/skybox.vert

^{pr2}$

./shaders/skybox.frag

#version 120
varying vec3 tex_coords;
uniform samplerCube skybox;

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

我相信在玩了很多游戏之后,错误出在pygame的skybox图片的加载上。我测试了三组skybox图像。每一个都有一个不同的视觉错误,我在上面的代码中已经注意到了。下面是三个skybox的测试源代码(一定要重命名这些图像,以便它们在各自的文件名中包括rightlefttopbottomback、或{})。在

  • ./images1/here
  • ./images2/:here
  • ./images3/:here(使用此zip中的“光线”图像)

这三个skybox都使用不同的图像格式(分别是bmp、tga和png)。我怎样才能在不依赖于表面上随机旋转或图像交换的情况下稳定地处理所有这些和未来的图像情况?任何帮助或意见将不胜感激。在

更新: 我已经创建了一个github repository,您可以在其中测试代码而不必创建主.py和着色器,下载图像,重命名并自行组织内容。这将使代码更容易运行,以防您对测试感兴趣。在

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

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

如果你需要其他信息,请告诉我!在


Tags: imageimporturlmaptimetoppygameface
1条回答
网友
1楼 · 发布于 2024-10-02 22:36:31

所以,我在渲染skybox时遇到的所有问题都可以归结为两个原因,其中没有一个是由于pygame加载不同文件格式图像的方式不一致造成的!在

  1. 天空盒图像在立方体两个面之间的接缝是如何连接的方面彼此不一致的。这解释了为什么每个skybox图像测试结果都有不同的问题。按照this问题中描述的在立方体内部的约定,我翻转并重新保存了paint中的图像。在
  2. 然而,光靠这一点是不够的。结果发现,OpenGL在“cubemap land”中z轴的约定是翻转的。这导致正面和背面互相交换。我能想到的最简单的解决方案是交换顶点着色器中的纹理坐标。这是更正的顶点着色器。在

    #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

相关问题 更多 >