2

我正在编写一个简单的应用程序,它加载图像并将其显示在屏幕上,以便分别呈现左半部分和右半部分。

import glm
import moderngl_window
import numpy as np
from PIL import Image


class BugExample(moderngl_window.WindowConfig):
    LEFT_TEXTURE_IDX = 0 
    RIGHT_TEXTURE_IDX = 1 

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        image = Image.open("test.jpg").transpose(Image.FLIP_TOP_BOTTOM)
        w, h = image.size
        w_even = 2 * (w // 2)
        left = image.crop((0, 0, w_even // 2, h))
        right = image.crop((w_even // 2, 0, w_even, h))
        self.texture_left = self.ctx.texture(left.size, 3, left.tobytes())
        self.texture_left.use(self.LEFT_TEXTURE_IDX)
        self.texture_right = self.ctx.texture(right.size, 3, right.tobytes())
        self.texture_right.use(self.RIGHT_TEXTURE_IDX)

        self.program = self.ctx.program(
            vertex_shader="""
                    #version 330
                    in vec2 in_position;
                    uniform mat4 model;
                    out vec2 uv0;
                    void main() {
                        gl_Position = model * vec4(in_position, 0.0, 1.0);
                        uv0 = (0.5 * in_position) + vec2(0.5);
                    }
                    """,
            fragment_shader="""
                    #version 330
                    out vec4 fragColor;
                    uniform sampler2D texture_idx;
                    in vec2 uv0;
                    void main() {
                        fragColor = texture(texture_idx, uv0);
                    }
                    """)
        self.left_scale_mat = glm.scale(glm.mat4(), glm.vec3(0.5, 1.0, 1.0))
        self.left_translate_mat = glm.translate(glm.mat4(), glm.vec3(-0.5, 0.0, 0.0))
        self.left_model_mat = self.left_translate_mat * self.left_scale_mat

        self.right_scale_mat = glm.scale(glm.mat4(), glm.vec3(0.5, 1.0, 1.0))
        self.right_translate_mat = glm.translate(glm.mat4(), glm.vec3(0.5, 0.0, 0.0))
        self.right_model_mat = self.right_translate_mat * self.right_scale_mat

        vertices = np.array([-1.0, 1.0, -1.0, -1.0, 1.0, -1.0,
                             -1.0, 1.0, 1.0, -1.0, 1.0, 1.0], dtype='f4')
        self.vbo = self.ctx.buffer(vertices)
        self.vao = self.ctx.simple_vertex_array(self.program, self.vbo, 'in_position')

    def render(self, time, frame_time):
        self.ctx.clear(1.0, 1.0, 1.0)

        self.program["model"].write(bytes(self.left_model_mat))
        self.program["texture_idx"].value = self.LEFT_TEXTURE_IDX
        self.vao.render()

        self.program["model"].write(bytes(self.right_model_mat))
        self.program["texture_idx"].value = self.RIGHT_TEXTURE_IDX
        self.vao.render()


if __name__ == '__main__':
    moderngl_window.run_window_config(BugExample, args=('--window', 'glfw'))

运行此程序将打开一个带有您的图像的窗口test.jpg

但是在纹理索引 31 处发生了一些奇怪的事情:

如果您更改索引以使首先加载的纹理(在我们的例子中是左侧纹理,如render方法中所述)具有索引 31,它将被另一个纹理覆盖,并且您将看到右半部分重复两次。

示例错误

我应该指出,如果我只有一个纹理,而不是两个,并且该纹理的索引为31,则不会有问题。仅当一个纹理具有索引且另一个纹理在纹理之后31加载时,才会出现此问题。31

[编辑:我还应该指出,我加载超过32 个纹理没有问题。如果我要将图像分成 32 个或更多块(而不是上面示例中的 2 个块),甚至 64 个块或更多块,唯一的问题将是纹理索引31将被最后加载的纹理覆盖。]

我有一个模糊的猜测,这与将数字31作为 int 操作的方式有关吗?(像这里

所以,最后,我的问题是- 这里发生了什么?我是否错过了正在发生的更大的事情,或者它只是生活中的一个事实,我应该避免纹理索引31并忘记它?

4

1 回答 1

2

该问题似乎与Context.texture.

当纹理加载到 GPU 时,会生成一个纹理对象并将其绑定到目标和纹理单元(DSA 除外)。ModernGL 似乎在内部使用纹理单元 31 来完成这项任务。

我对此非常确定,因为当我在之后打印活动纹理单元时self.ctx.texture(left.size, 3, left.tobytes())

from OpenGL.GL import *
print(glGetInteger(GL_ACTIVE_TEXTURE) - GL_TEXTURE0)

LEFT_TEXTURE_IDX输出为 31,与和的值无关RIGHT_TEXTURE_IDX


解释:

当您使用纹理单元 31 和 32 时:

LEFT_TEXTURE_IDX = 0 
RIGHT_TEXTURE_IDX = 1 

textur_left使用纹理单元 31 在内部加载:

self.texture_left = self.ctx.texture(left.size, 3, left.tobytes())

textue_left显式绑定到纹理单元 31:

self.texture_left.use(self.LEFT_TEXTURE_IDX)

texture_right使用纹理单元 31 在内部加载。这打破了textue_left与纹理单元 31 的绑定:

self.texture_right = self.ctx.texture(right.size, 3, right.tobytes())

texture_right显式绑定到纹理单元 32:

self.texture_right.use(self.RIGHT_TEXTURE_IDX)

最后texture_right绑定到纹理单元 31 和 32。

更改说明的顺序以解决问题。首先加载两个纹理,然后将它们分配给纹理单元:

self.texture_left = self.ctx.texture(left.size, 3, left.tobytes())
self.texture_right = self.ctx.texture(right.size, 3, right.tobytes())
self.texture_left.use(self.LEFT_TEXTURE_IDX)
self.texture_right.use(self.RIGHT_TEXTURE_IDX)
于 2020-10-12T12:31:37.693 回答