小狗。如何动态更改顶点的图片(动画)。 OpenGL
Pyglet. How to change picture(animate) for vertices dynamically. OpenGL
环境:
Python:3.6.6
小狗版本: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/classes 来实现它。我找不到任何示例(就我的简单愿景而言)通常情况下,例如 3 维平面模型的动画。
关于 vertices
的 rotating/moving/resizing 有很多例子,但是如何构建正确的问题(动画方面)以获得 google 中的答案 - 我不知道。
PS:如果你,reader,有任何有用的 link这个主题(针对 pyglet 或仅针对 OpenGL)非常感谢您在评论中分享此 link(s)。
纹理坐标。
你应该有一个单一的纹理图集,用于所有动画过的不同事物的所有帧。
最好所有的动画速度和帧数始终相同。
假设有两个精灵,整个动画有 2 帧,并且它们存储在 64x64 纹理图集中。 (编辑:抱歉含糊不清,64x64 PIXELS,只是因为它可能暗示我们有 64x64 tile atlas,在我提到这个的其他地方都一样)
现在,您需要一个具有全局值的全局计时器,它指示当前动画帧,而不是游戏帧。它应该独立于帧率。
所述值应以您希望的速度每隔一段时间更新一次,如下所示:
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 坐标中的 "tiles":
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
坐标,然后你的 U
计算方式如 tile_width * current_frame
.
这假设你已经有了精灵批处理,所以你所做的是在更新时遍历每个精灵,基本上只是用新的帧重新计算新的 UV,这意味着所有精灵都会将它们的帧更改为下一个,耶!
如果你想拥有彼此独立的系统,比如说,一些动画非常慢,而另一些则更快,你需要不同的精灵批次或适当的计算从哪里到哪里你需要更新 UV在顶点缓冲区数组中。其他一切都完全一样,除了现在 current_frame 不是全局的而是包含的,最好是在管理动画计时器的某个列表或单独的对象中。
您无需更改着色器中的任何内容,它们只需要为帧设置正确的 UV,您就可以设置了。
顺便说一句,这是非常基本的,您可以自己应用一些逻辑,这样您就可以在纹理中使用 32x32 像素的 16x16 网格,每行精灵都有 4 种不同的动画,这些可以是精灵的状态( attack,运行,等等),但如何操作完全取决于您自己,最重要的是,让它发挥作用。祝你好运。
但是如果按照我说的那样做,那么state就是另外一个整数,state就是UV,假设所有state的宽度完全一样,那就是这样的:
state_width = 1 / states
tile_width = 1 / (states * frames_per_state)
U = state_width * current_state + tile_width * current_frame
现在出现了一个问题,玩家可以在最后一个攻击帧开始他的动画。
这很正常,有动作的实体都应该有单独的计时器,我上面描述的是用于大量只是背景的精灵,比如草。现在当你把它分开时,你可以有一个正确的方法来在分配新状态时将当前帧重置为0。
如果您的实体是对象,您可以编写适当的方法,在每次使用这些精灵重建精灵批次时重新计算 UV,然后计时器本身可以包含在对象中。
我们需要画点什么?检查动画状态,它改变了吗?发送之前计算好的UV,否则,稍等一下,我们需要重新计算,然后才将它们添加到VBO,好吧,渲染你的东西,最后,它看起来就像你有动画一样,即使真的,它是只是一个简单但很棒的 UV 操作。
祝你好运。
环境:
Python:3.6.6
小狗版本: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/classes 来实现它。我找不到任何示例(就我的简单愿景而言)通常情况下,例如 3 维平面模型的动画。
关于 vertices
的 rotating/moving/resizing 有很多例子,但是如何构建正确的问题(动画方面)以获得 google 中的答案 - 我不知道。
PS:如果你,reader,有任何有用的 link这个主题(针对 pyglet 或仅针对 OpenGL)非常感谢您在评论中分享此 link(s)。
纹理坐标。
你应该有一个单一的纹理图集,用于所有动画过的不同事物的所有帧。
最好所有的动画速度和帧数始终相同。
假设有两个精灵,整个动画有 2 帧,并且它们存储在 64x64 纹理图集中。 (编辑:抱歉含糊不清,64x64 PIXELS,只是因为它可能暗示我们有 64x64 tile atlas,在我提到这个的其他地方都一样)
现在,您需要一个具有全局值的全局计时器,它指示当前动画帧,而不是游戏帧。它应该独立于帧率。
所述值应以您希望的速度每隔一段时间更新一次,如下所示:
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 坐标中的 "tiles":
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
坐标,然后你的 U
计算方式如 tile_width * current_frame
.
这假设你已经有了精灵批处理,所以你所做的是在更新时遍历每个精灵,基本上只是用新的帧重新计算新的 UV,这意味着所有精灵都会将它们的帧更改为下一个,耶!
如果你想拥有彼此独立的系统,比如说,一些动画非常慢,而另一些则更快,你需要不同的精灵批次或适当的计算从哪里到哪里你需要更新 UV在顶点缓冲区数组中。其他一切都完全一样,除了现在 current_frame 不是全局的而是包含的,最好是在管理动画计时器的某个列表或单独的对象中。
您无需更改着色器中的任何内容,它们只需要为帧设置正确的 UV,您就可以设置了。
顺便说一句,这是非常基本的,您可以自己应用一些逻辑,这样您就可以在纹理中使用 32x32 像素的 16x16 网格,每行精灵都有 4 种不同的动画,这些可以是精灵的状态( attack,运行,等等),但如何操作完全取决于您自己,最重要的是,让它发挥作用。祝你好运。
但是如果按照我说的那样做,那么state就是另外一个整数,state就是UV,假设所有state的宽度完全一样,那就是这样的:
state_width = 1 / states
tile_width = 1 / (states * frames_per_state)
U = state_width * current_state + tile_width * current_frame
现在出现了一个问题,玩家可以在最后一个攻击帧开始他的动画。
这很正常,有动作的实体都应该有单独的计时器,我上面描述的是用于大量只是背景的精灵,比如草。现在当你把它分开时,你可以有一个正确的方法来在分配新状态时将当前帧重置为0。
如果您的实体是对象,您可以编写适当的方法,在每次使用这些精灵重建精灵批次时重新计算 UV,然后计时器本身可以包含在对象中。
我们需要画点什么?检查动画状态,它改变了吗?发送之前计算好的UV,否则,稍等一下,我们需要重新计算,然后才将它们添加到VBO,好吧,渲染你的东西,最后,它看起来就像你有动画一样,即使真的,它是只是一个简单但很棒的 UV 操作。
祝你好运。