Python 如何加速此pyglet渲染?

Python 如何加速此pyglet渲染?,python,asynchronous,3d,rendering,pyglet,Python,Asynchronous,3d,Rendering,Pyglet,下面是渲染的代码。我只渲染了大约300个块,但速度非常慢 async def render(self): time_start = time.time() self.batch = pyglet.graphics.Batch() for block in self.blocks_to_be_rendered: self.draw_block(block) print("Time to render 1 chunk " + str

下面是渲染的代码。我只渲染了大约300个块,但速度非常慢

async def render(self):
    time_start = time.time()
    self.batch = pyglet.graphics.Batch()
    for block in self.blocks_to_be_rendered:
        self.draw_block(block)
    print("Time to render 1 chunk " + str(time_start-time.time()))


def Create_chunk(cx,cz):
    thr = threading.Thread(target=_Create_chunk,args=(cx,cz))
    thr.start()
    thr.join()
    asyncio.run(Chunks[cx][cz].render())
def draw_block(self, block):

    position = (block.x,block.y,block.z)
    if block._type == "grass":
        block = Grass
    else:
        print("HOLD ON A SEC")
        block = Grass
    vertices = get_vertices(*position)
    faces = ("left", "right", "top", "bottom", "front", "back")
    for vertex, face in zip(vertices, faces):
        self.batch.add(
            4,
            gl.GL_QUADS,
            block.textures[face],
            ("v3f/static", vertex),
            ("t2f/static", (0, 0, 1, 0, 1, 1, 0, 1)),
我尝试将“render”作为异步函数运行,因为渲染每个块需要0.3秒!!!!! 这意味着fps接近于0

任何关于优化/关键缺陷的帮助都将不胜感激

编辑:这段代码可能没有意义,因为我从主代码拼接了它,因为它会很长,但这里只是以防万一:

import pyglet
from pyglet.gl import *
from pyglet.window import key
from pyglet import gl, window
import math
import numpy as np
import random
import noise
import threading
import asyncio
from multiprocessing import Pool
import time
#AVOID FROM KEEPING GL FUNCTIONS IN CLASS BLOCK AND CLASS CHUNK BECAUSE THESE WILL BE EXECUTED FROM A DIFFERENT THREAD
def get_tex(file):
    tex = pyglet.image.load(file).get_texture()
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    return pyglet.graphics.TextureGroup(tex)


def make_textures(textures):
    result = {}
    base = "imgs/"
    faces = ("left", "right", "top", "bottom", "front", "back")
    for face in faces:
        file_name = textures.get(face, textures.get("side"))
        result[face] = get_tex(base + file_name)
    return result

def get_vertices(x, y, z):
    dx, dy, dz = x + 1, y + 1, z + 1
    return [
        (x, y, z, x, y, dz, x, dy, dz, x, dy, z),  # side
        (dx, y, dz, dx, y, z, dx, dy, z, dx, dy, dz),  # side
        (x, dy, dz, dx, dy, dz, dx, dy, z, x, dy, z),  # top
        (x, y, z, dx, y, z, dx, y, dz, x, y, dz),  # bottom
        (dx, y, z, x, y, z, x, dy, z, dx, dy, z),  # side
        (x, y, dz, dx, y, dz, dx, dy, dz, x, dy, dz),  # side
    ]
class Grass:
    textures = make_textures({"side": "grass_side.png", "top": "grass_top.png", "bottom": "dirt.png"})

class Block():
    def __init__(self,x,y,z):
        #for testing sake _type = dirt
        self._type = "grass"
        self.x = x
        self.y = y
        self.z = z
        self.width = 1
        self.height = 1
        self.length = 1


np.seed = random.randint(1,10000)
Chunks = {-1:{},0:{},1:{}}
class Chunk():
    def prepare_for_rendering(self):
        #Im too lazy to do actual faces so i will only render the nesecary blocks
        #MUST BE DONE IN A DIFFERENT THREAD OR FPS = -1
        time_start = time.time()
        self.blocks_to_be_rendered = []
        for x in range(0,16):
            for z in range(0,16):
                #Gives us an unspecified length dictionary
                for key in self.Chunk_blocks[x][z].keys():
                    #key is an integer
                    #block is Chunk_blocks[x][z][key]
                    try:
                        if self.Chunk_blocks[x-1][z][key] and self.Chunk_blocks[x+1][z][key] and self.Chunk_blocks[x][z+1][key] and self.Chunk_blocks[x][z-1][key] and self.Chunk_blocks[x][z][key+1] and self.Chunk_blocks[x][z][key-1]:
                            pass
                    except:
                        self.blocks_to_be_rendered.append(self.Chunk_blocks[x][z][key])
        print(time_start-time.time())
    def draw_block(self, block):

        position = (block.x,block.y,block.z)
        if block._type == "grass":
            block = Grass
        else:
            print("HOLD ON A SEC")
            block = Grass
        vertices = get_vertices(*position)
        faces = ("left", "right", "top", "bottom", "front", "back")
        for vertex, face in zip(vertices, faces):
            self.batch.add(
                4,
                gl.GL_QUADS,
                block.textures[face],
                ("v3f/static", vertex),
                ("t2f/static", (0, 0, 1, 0, 1, 1, 0, 1)),
            )
    def __init__(self,Cx,Cz):
    #C means chunk x and chunk y aka top left
        self.Chunk_blocks = {}
        self.batch = pyglet.graphics.Batch()
        self.blocks_to_be_rendered = []
        x =0
        z=0
        for x in range(0,16):
            Chunk_row ={}
            for z in range(0,16):
                top_y = round(noise.pnoise2((Cx*16+x)/16,(Cz*16+z)/16)*10)
                Chunk_collumn = {}
                Chunk_collumn[top_y] = Block(Cx*16+x,top_y,Cz*16+z)
                #print(top_y)
                for y in range(-top_y,30):
                    Chunk_collumn[top_y-y-1] = Block(Cx*16+x,top_y-y-1,Cz*16+z)
                Chunk_row[z]= Chunk_collumn
            self.Chunk_blocks[x] = Chunk_row
        thr = threading.Thread(target=self.prepare_for_rendering)
        thr.start()
    async def render(self):
        time_start = time.time()
        self.batch = pyglet.graphics.Batch()
        for block in self.blocks_to_be_rendered:
            self.draw_block(block)
        print("Time to render 1 chunk " + str(time_start-time.time()))


def Create_chunk(cx,cz):
    thr = threading.Thread(target=_Create_chunk,args=(cx,cz))
    thr.start()
    thr.join()
    asyncio.run(Chunks[cx][cz].render())
def _Create_chunk(cx,cz):
    time_start = time.time()
    if cx in Chunks.keys():
        Chunks[cx][cz] = Chunk(cx,cz)
    else:
        Chunks[cx] = {cz:Chunk(cx,cz)}
    print(time_start-time.time())


#THIS IS WHERE THE PYGLET/GL SEGMENT STARTS
#I AM TRYING TO SPERATE THIS SO WE CAN MUTLI TRHEAD
#WITHOUT ACCIDENTILY CALLING GL FUNCTIONS IN A SEPERATE THREAD OTHER THEN THE MAIN THREAD
#

Create_chunk(0,0)

class Player:
    def __init__(self, position=(8, 0, 8), rotation=(0, 0)):
        self.position = position
        self.rotation = rotation
        self.strafe = [0, 0, 0]  # forward, up, left

    def mouse_motion(self, dx, dy):
        x, y = self.rotation
        sensitivity = 0.15
        x += dx * sensitivity
        y += dy * sensitivity
        y = max(-90, min(90, y))
        self.rotation = x % 360, y

    def update(self, dt):
        motion_vector = self.get_motion_vector()
        speed = dt * 5
        self.position = [x + y * speed for x, y in zip(self.position, motion_vector)]

    def get_motion_vector(self):
        x, y, z = self.strafe
        if x or z:
            strafe = math.degrees(math.atan2(x, z))
            yaw = self.rotation[0]
            x_angle = math.radians(yaw + strafe)
            x = math.cos(x_angle)
            z = math.sin(x_angle)
        return x, y, z


def draw_camera(position, rotation):
    yaw, pitch = rotation
    gl.glRotatef(yaw, 0, 1, 0)
    gl.glRotatef(-pitch, math.cos(math.radians(yaw)), 0, math.sin(math.radians(yaw)))
    x, y, z = position
    gl.glTranslatef(-x, -y, -z)


def set_3d(width, height):
    gl.glEnable(gl.GL_DEPTH_TEST)
    gl.glMatrixMode(gl.GL_PROJECTION)
    gl.glLoadIdentity()
    gl.gluPerspective(65, width / height, 0.1, 60)
    gl.glMatrixMode(gl.GL_MODELVIEW)
    gl.glLoadIdentity()


class Window(pyglet.window.Window):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.player = Player()
        self.set_exclusive_mouse(True)
        pyglet.clock.schedule(self.update)

    def on_mouse_motion(self, x, y, dx, dy):
        self.player.mouse_motion(dx, dy)

    def on_key_press(self, symbol, modifiers):
        if symbol == window.key.ESCAPE:
            self.close()
        speed = 1
        if symbol == window.key.W:
            self.player.strafe[0] = -speed
        elif symbol == window.key.S:
            self.player.strafe[0] = speed
        elif symbol == window.key.A:
            self.player.strafe[2] = -speed
        elif symbol == window.key.D:
            self.player.strafe[2] = speed
        elif symbol == window.key.SPACE:
            self.player.strafe[1] = speed
        elif symbol == window.key.LSHIFT:
            self.player.strafe[1] = -speed

    def on_key_release(self, symbol, modifiers):
        if symbol == window.key.W:
            self.player.strafe[0] = 0
        elif symbol == window.key.S:
            self.player.strafe[0] = 0
        elif symbol == window.key.A:
            self.player.strafe[2] = 0
        elif symbol == window.key.D:
            self.player.strafe[2] = 0
        elif symbol == window.key.SPACE:
            self.player.strafe[1] = 0
        elif symbol == window.key.LSHIFT:
            self.player.strafe[1] = 0
                
    def update(self, dt):
        self.player.update(dt)
        chunk_x_player = round(self.player.position[0]/16)
        chunk_z_player = round(self.player.position[2]/16)
        for x in range(-2,2):
            for z in range(-2,2):
                try:
                    if Chunks[chunk_x_player+x][chunk_z_player+z]:
                        pass
                except:
                    Create_chunk(chunk_x_player+x,chunk_z_player+z)

    def on_draw(self):
        self.clear()
        time_start = time.time()
        set_3d(*self.get_size())
        draw_camera(self.player.position, self.player.rotation)
        #for block in self.model.blocks_to_be_rendered:
        #    self.model.draw_block(block)
        chunk_x_player = round(self.player.position[0]/16)
        chunk_z_player = round(self.player.position[2]/16)
        for x in range(-2,2):
            for z in range(-2,2):
                try:
                    Chunks[chunk_x_player+x][chunk_z_player+z].batch.draw()
                except:
                    print("ERROR")
                    pass
        print(time_start-time.time())


if __name__ == "__main__":
    Window(width=800, height=480, resizable=True)
    gl.glClearColor(0.5, 0.7, 1, 1)  # sky color
    pyglet.app.run()

到目前为止,这还不是一个完整的答案,但它需要一个评论,可能包含一些有用的信息。我建议您尽量避免线程,并使用异步IO。这主要是因为它可能很难正确执行,而且
asyncio
pyglet
都有自己的主事件循环。您可以创建自己的事件循环,所以这不是不可能的,但对于这种情况,它可能试图解决一些可以用更少的代码解决的问题,而不是更多的问题

您还将(我认为)在每次
render
迭代中替换批处理,或者至少在每次
render
调用中替换批处理,我假设您尽可能频繁地进行这些调用

class Chunk():
定义初始化(self、Cx、Cz):
self.batch=pyglet.graphics.batch()
def渲染(自):
self.batch=pyglet.graphics.batch()
您还应该在
\uuuu init\uuuu
函数中创建要渲染的
self.blocks,并让它运行。除非您对创建材质/对象加载程序并创建加载屏幕感到满意,或者对其进行预处理并更快地加载-跳过它,只需将创建部分保留在
\uuu init\uu
部分。这样可以避免线程,并确保不必每次渲染都执行此操作:

def渲染(自):
对于要渲染的self.blocks\u中的块:
自绘制块(块)
也就是说:

def渲染(自):
#删除整个旧批
self.batch=pyglet.graphics.batch()
#如果有线程锁,请等待线程锁
#因为资源是在主线程之外创建的
对于要渲染的self.blocks\u中的块:
位置=(block.x,block.y,block.z)
如果块类型=“草”:
块=草
其他:
打印(“等待秒”)
块=草
x、 y,z=位置
dx,dy,dz=x+1,y+1,z+1
顶点=[
(x,y,z,x,y,dz,x,dy,dz,x,dy,z),#侧
(dx,y,dz,dx,y,z,dx,dy,z,dx,dy,dz),#侧
(x,dy,dz,dx,dy,dz,dx,dy,z,x,dy,z),#顶部
(x,y,z,dx,y,z,dx,y,dz,x,y,dz),#底部
(dx,y,z,x,y,z,x,dy,z,dx,dy,z),#边
(x,y,dz,dx,y,dz,dx,dy,dz,x,dy,dz),#侧
]
面=(“左”、“右”、“上”、“下”、“前”、“后”)
对于压缩中的顶点、面(顶点、面):
#逐个重新创建批处理对象
self.batch.add(
4.
gl.gl_QUADS,
块.纹理[面],
(“v3f/静态”,顶点),
(“t2f/静态”,(0,0,1,0,1,1,0,1)),
)
打印(“渲染1块的时间”+str(Time\u start-Time.Time()))
(我扩展了您的代码,以便您了解渲染逻辑中的大量调用、循环和逻辑。在图形渲染中,每个时钟周期都很重要)

相反,您的整个渲染逻辑可以归结为:

def渲染(自):
self.batch.draw()

假设您在
\uuuu init\uuuuuu
函数中创建批处理。

最后,我还想说,通过
时钟检查调度字符更新或类似的事情。调度间隔(更新函数.5)
(本例每秒调用更新函数两次,这可能太少了).我想你弄错了密码。渲染函数只调用一次。在init函数中调用。即使只调用一次,执行该功能也需要0.3秒,这意味着如果您每帧创建一个新块,您的fps将为3!!!!!!!!!!但总的来说,你的信息很有帮助