小精灵如何动态更改顶点的图片(动画)。 OpenGL

时间:2018-10-16 20:12:51

标签: python python-3.x opengl 3d pyglet

环境:

Python:3.6.6
pyglet版本:1.3.2

代码库:

abstract_model.py

import pyglet


def get_texture_group(file, order_group_index):
    texture = pyglet.image.load(file).texture
    order_group = pyglet.graphics.OrderedGroup(order_group_index)
    return pyglet.graphics.TextureGroup(texture, order_group)


class AbstractModel(object):

    def _create_as_vertex(self):
        v_x = self.cell_data.get("x") * 32
        v_y = self.cell_data.get("y") * -1 * 32

        texture_group = self.map_type_iamge.get(self.cell_data.get("t"))
        x_offset = self.x_offset * self.scale

        x, y, z = v_x + x_offset, v_y, self.z
        x_ = (texture_group.texture.width * self.scale + x_offset + v_x)
        y_ = (texture_group.texture.height * self.scale + v_y)

        tex_coords = ('t2f', (0, 0, 1, 0, 1, 1, 0, 1))

        self.vertices = self.batch.add(
            4, pyglet.gl.GL_QUADS,
            texture_group,
            ('v3f', (x, y, z,
                     x_, y, z,
                     x_, y_, z,
                     x, y_, z)),
            tex_coords)

    def _animate(self, dt):
        # lets assume that I have list of pyglet.graphics.TextureGroup
        # and they should somehow be drawn one after other
        print("I need change image. dt=", dt, self)
        pyglet.clock.schedule_once(self._animate, 1)

ground3d.py

import os
import pyglet

import settings
from models import abstract_model


GROUND_DIR = os.path.join(settings.STATIC_DIR, "ground")

order_group_index = 0

map_type_iamge = {
    1: abstract_model.get_texture_group(os.path.join(GROUND_DIR, "w1.png"), order_group_index),
    2: abstract_model.get_texture_group(os.path.join(GROUND_DIR, "t1.png"), order_group_index),
    1001: abstract_model.get_texture_group(os.path.join(GROUND_DIR, "t1_direction.png"), order_group_index),
}


class Ground3D(abstract_model.AbstractModel):

    def __init__(self, cell_data, batch):

        self.batch = batch
        self.cell_data = cell_data
        self.map_type_iamge = map_type_iamge
        self.scale = 1
        self.x_offset = 0
        self.z = 0
        self.entity = None

        self._create_as_vertex()
        pyglet.clock.schedule_once(self._animate, 1)

说明:

我有一些模型(例如,仅扁平矩形)应放置在3维上。这些模型应该是动画的,例如picture_1,第二个picture_2之后,等等。
从我的previous question中了解到,在3D批处理中使用pyglet.sprite.Sprite()并不是一个好主意。

问题:

如何在self.vertices上更改图片(使用TextureGroup或其他方法)?

或者我使用哪种arroach / classs来实现它。我找不到任何示例(就我的简单视觉而言),例如3维某些平面模型的动画。

有很多关于vertices旋转/移动/调整大小的示例,但是如何建立正确的问题(动画方面)以在Google中获得答案-我不知道。

PS :如果您阅读者在此主题上有任何有用的链接(对于pyglet或仅对于OpenGL),非常感谢您分享此链接)的评论。

1 个答案:

答案 0 :(得分:1)

纹理坐标。

您应该为曾经制作过动画的所有不同事物的所有帧创建一个单一的纹理图集。

最好,所有内容始终具有相同的动画速度和相同的帧数。

比方说,有两个精灵具有两个用于整个动画的帧,它们存储在64x64纹理图集中。 (编辑:对于64x64 PIXELS的含糊不清表示歉意,只是因为它可能暗示我们有64x64瓷砖图集,在我提到的其他地方都一样)

现在,您需要有一个具有全局值的全局计时器,该值指示当前的动画帧,而不是游戏帧。它应该独立于帧速率。

应该以您希望的速度不时更新一次更新值:

current_frame = (current_frame + 1) % animation_length

由于本示例中有2帧,因此结果如下:

# init
animation_length = 2
current_frame = 0

# updates:
current_frame = (0 + 1) % 2 # 1 % 2 -> 1
current_frame = (1 + 1) % 2 # 2 % 2 -> 0
...

现在,仅当帧发生变化时,才需要更新所有子画面的UV。

UV从左开始,从右开始,从0到1(据我所记得,就这个示例而言,是的,嘘)。

由于每个都有2帧,因此我们可以像这样在UV坐标中计算“平铺”:

tile_width = 1.0 / frames # 2 frames each, width will be 0.5
tile_height = 1.0 / sprites # 2 sprites each, height will be 0.5 too, perfect

现在,在第一帧上,您将像正常情况一样生成UV,只需要获取垂直ID或其他内容,然后使用tile_height * sprite_id获取当前的V坐标,就可以计算出Utile_width * current_frame

这假定您已经具有精灵批处理,因此您要做的是遍历更新时的每个精灵,并且基本上只是重新计算具有新框架的新UV,这意味着所有精灵将其框架更改为下一个框架,是的!

如果要使系统彼此独立,例如,某些动画的速度非常慢,而另一些动画的速度则更快,则需要不同的精灵批处理或适当的计算,以从何处更新顶点的UV缓冲区数组。其他所有内容都完全相同,除了现在的current_frame不再是全局的而是包含在内,更喜欢包含在某些列表中或管理动画计时器的单独对象中。

您无需更改着色器中的任何内容,只需为框架添加合适的UV,即可设置好。

顺便说一句,这是非常基础的,您可以自己应用一些逻辑,以便可以在纹理中使用32x32像素的16x16网格,每行精灵具有4种不同的动画,这些可能是精灵的状态(攻击,运行等),但是如何操作完全取决于您自己,最重要的是,让它正常工作。祝你好运。

但是,如果按照我说的那样做,那么状态将是另一个整数,而UV表示状态,假设所有状态都具有完全相同的宽度,则将是这样:

state_width = 1 / states
tile_width = 1 / (states * frames_per_state)
U = state_width * current_state + tile_width * current_frame

现在,出现一个问题,玩家可以在最后一个攻击帧开始动画。

正常情况下,具有动作的实体都应具有单独的计时器,正如我上面所描述的,它们是针对仅是背景的大量精灵,例如草。现在,当您将其分割时,您可以找到一种适当的方法,在分配新状态后将当前帧重置重置为0。

如果您的实体是对象,则可以编写适当的方法来在每次使用这些sprite重建sprite批处理时重新计算UV,然后定时器自身可以包含在对象中。

我们需要画点东西吗?检查动画状态,是否已更改,否?发送之前计算过的UV,否则,请稍等,我们需要重新计算,然后将其添加到VBO中,然后渲染您的东西,最后,它看起来就像是具有动画的,即使实际上是只是一个简单但出色的紫外线处理。

祝你好运。