Как изменить углы перспективы при суммировании спрайтов? - PullRequest
1 голос
/ 20 апреля 2020

Я создал программу предварительного просмотра стека спрайтов в Python, используя библиотеку Pygame. Смещение листа спрайта на -1 пиксель по оси Y создает перспективу 45 градусов по умолчанию, когда каждая ячейка сложена поверх предыдущей. Я также могу получить перспективу в 0 градусов, установив интервал между ячейками в 0.

Существует веб-программа Sprite Stack Studio, которая позволяет пользователю выбирать 0, 45, 60 и 90. градусные углы камеры. Я также видел картридж Пико-8, который позволяет получить полную перспективу в виде полусферы вокруг острова.

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

Мой лист спрайтов: enter image description here

Изображение: enter image description here

Пример студии стека спрайтов: https://spritestack.io/studio/?cloud_id=349&app=exporter

Остров PICO-8 Пример: https://www.lexaloffle.com/bbs/?pid=15116#p

1 Ответ

0 голосов
/ 21 апреля 2020

Правильно, поэтому у меня было быстрое воспроизведение на этом уроке по PyGame + OpenGL: https://stackabuse.com/advanced-opengl-in-python-with-pygame-and-pyopengl/

Этот код работает не очень хорошо, но он конвертирует спрайт-лист в воксели и выводит их в окно PyGame. Это действительно очень медленно 1009 *. По крайней мере для меня, я предполагаю, что это вообще не использует аппаратное 3D. Но она позволяет визуализировать модель под любым углом.

voxelised sprite sheet

Большая проблема в том, что она составляет 32 * 32 * 19 = 19456 вокселей (минус прозрачные). Оптимизация кода таким образом, чтобы соседние воксели одного и того же цвета были объединены в один прямоугольный angular воксель, очень помогли бы.

В любом случае, возможно, это подтолкнет вас к тому, чтобы ваше собственное решение заработало.

import pygame
import random
from pygame.locals import *

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

DISPLAY_WIDTH   = 800
DISPLAY_HEIGHT  = 800
DISPLAY_SURFACE = DOUBLEBUF|OPENGL

# A Unit Cube from (0,0,0) -> (1,1,1)
cube_vertices = ( (1,1,1),
                  (1,1,0),
                  (1,0,0),
                  (1,0,1),
                  (0,1,1),
                  (0,1,0),
                  (0,0,0),
                  (0,0,1)  )

# The Surfaces making up each face-square
cube_surfaces = ( (0,1,2,3),
                  (3,2,7,6),
                  (6,7,5,4),
                  (4,5,1,0),
                  (1,5,7,2),
                  (4,0,3,6)  )


### Class to hold a cube's worth of verticies
class Cube():
    def __init__( self, xmove, ymove, zmove, colour ):
        global cube_vertices
        self.colour   = colour
        self.vertices = []
        for vert in cube_vertices:
            new_x = vert[0] + xmove
            new_y = vert[1] + ymove
            new_z = vert[2] + zmove
            self.vertices.append( [new_x, new_y, new_z] )

    def drawCube( self ):
        """ Call the openGL function to render the cube """
        global cube_surfaces
        glBegin(GL_QUADS)
        for surface in cube_surfaces:
            glColor3fv( self.colour )
            for vertex in surface:
                glVertex3fv( self.vertices[ vertex ])
        glEnd()


### Class to convert a 2D sprite sheet into a (huge) set of 
### voxels-per-pixels
class SpriteSheet3D():
    def __init__( self, sheet_filename, sheet_count, sprite_width, sprite_height ):
        self.cube_list = []
        self.image     = pygame.image.load( sheet_filename ).convert_alpha()
        # TODO: sanity check sprite image matches supplied dimensions

        # Iterate through each sheet's pixels creating cubes/voxels
        # TODO: optimise voxels to combine pixels of the same colour into a single, bigger prism
        for z in range( 0, sheet_count ):
            for y in range( 0, sprite_height ):
                for x in range( 0, sprite_width ):
                    offset_x = x + ( z * sprite_width )
                    pixel_colour = self.image.get_at( ( offset_x, y ) )
                    # Only create opaque pixels (Pixels are RGBA)
                    if ( pixel_colour[3] > 128 ):                     # TODO: handle translucent pixels
                        self.createVoxel( x, y, z, pixel_colour )

    def createVoxel( self, x, y, z, pixel_colour ):
        """ Create a 3D Unit-Cube at (x,y,z) """
        # Pygame maps colours 0-255 for RGBA
        # OpenGL maps colour 0-1 for RGB
        gl_colour = ( pixel_colour[0]/255, pixel_colour[1]/255, pixel_colour[2]/255 )
        self.cube_list.append( Cube( x, y, z, gl_colour ) )

    def drawCubes( self ):
        """ Paint all the cubes """
        for cube in self.cube_list:
            cube.drawCube()


### Main
pygame.init()
pygame.display.set_mode( ( DISPLAY_WIDTH, DISPLAY_HEIGHT ), DISPLAY_SURFACE )
glEnable( GL_DEPTH_TEST ) 
gluPerspective( 45, DISPLAY_WIDTH/DISPLAY_HEIGHT, 0.1, 200.0 )
glTranslatef( -16, -16, -150 )  # move camera here

sprite_3d = SpriteSheet3D( 'sprite_stack.png', 19, 32, 32 ) 

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            quit()

    glRotatef( 3.6, 0,1,1 )  # rotate 3.6 degrees about Y and Z-axiz

    glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT )
    sprite_3d.drawCubes()
    pygame.display.flip()
    pygame.time.wait(1)


pygame.quit()
quit()
...