2

I am trying to create a headless renderer which takes 2 models with different textures and then renders them both into a single image

    texture = ctx.texture(texture_image.size, 3, texture_image.tobytes())
    texture.build_mipmaps()

    texture1 = ctx.texture(texture_image1.size, 3, texture_image1.tobytes())
    texture1.build_mipmaps()
    # Vertex Buffer and Vertex Array

    vbo = ctx.buffer(vertex_data)
    vao = ctx.simple_vertex_array(
        prog, vbo, *['in_vert', 'in_text', 'in_norm'])
    for i in range(2):


        # Matrices and Uniforms

        # Framebuffers

        fbo = ctx.framebuffer(
            ctx.renderbuffer((512, 512)),
            ctx.depth_renderbuffer((512, 512)),
        )

        # Rendering

        fbo.use()
        ctx.enable(ModernGL.DEPTH_TEST)
        ctx.clear(0.9, 0.9, 0.9)
        texture.use()

        vao.render()

        vbo = ctx.buffer(cylinder)
        vao = ctx.simple_vertex_array(
            prog, vbo, *['in_vert', 'in_text', 'in_norm'])

        fbo.use()
        ctx.enable(ModernGL.DEPTH_TEST)
        # ctx.clear(0.9, 0.9, 0.9)
        texture1.use()

        vao.render()

        # Loading the image using Pillow

        data = fbo.read(components=3, alignment=1)
        img = Image.frombytes('RGB', fbo.size, data, 'raw', 'RGB', 0, -1)
#         del data
#         del img

    img.save(f'output{i}.png')


if __name__ == '__main__':
    import time

    start = time.time()
    main()
    end = time.time()
    print(end-start)

In this only the second model is finally exported and the initial model is overwritten. I tried a lot of resources to find an answer to this but couldn't find any.

Edit- Added an image of what I am trying to create. The first model i.e. the cylinder and then the watch over it with a different texture.

The watch and the cylinder behind it are the two models.

2 Answers2

1

Here is a standalone example you can run. It renders two textured quads to a framebuffer and saves the framebuffer contents to a png file.

If you are making a draw loop of some kind it's imporant that you don't create resources inside that loop unless you know what that implies.

If this doesn't work for your models the problem is probably somewhere in the code you did not share like in the shader or the model itself (possibly placement or projection)

When working with OpenGL it's almost always a good idea to create a minimal version first and gradually add the content. This makes it easier to identify where the problem is located.

Output

enter image description here

import moderngl
from array import array
from PIL import Image

ctx = moderngl.create_context(standalone=True)
framebuffer_size = (512, 512)

texture1 = ctx.texture((2, 2), 3, array('B', [200, 0, 0] * 4))
texture2 = ctx.texture((2, 2), 3, array('B', [0, 200, 0] * 4))

fbo = ctx.framebuffer(
    ctx.renderbuffer(framebuffer_size),
    ctx.depth_renderbuffer(framebuffer_size),
)

program = ctx.program(
    vertex_shader="""
    #version 330

    in vec2 in_pos;
    in vec2 in_uv;
    out vec2 uv;

    void main() {
        gl_Position = vec4(in_pos, 0.0, 1.0);
        uv = in_uv;
    }
    """,
    fragment_shader="""
    #version 330

    uniform sampler2D texture0;
    out vec4 fragColor;
    in vec2 uv;

    void main() {
        fragColor = texture(texture0, uv);
    }
    """,
)

buffer1 = ctx.buffer(array('f',
    [
        # pos xy    uv
        -0.75,  0.75, 1.0, 0.0,
        -0.75, -0.75, 0.0, 0.0,
         0.75,  0.75, 1.0, 1.0,
         0.75, -0.75, 1.0, 0.0,
    ]
))

buffer2 = ctx.buffer(array('f',
    [
        # pos xy    uv
        -0.5,  0.5, 1.0, 0.0,
        -0.5, -0.5, 0.0, 0.0,
         0.5,  0.5, 1.0, 1.0,
         0.5, -0.5, 1.0, 0.0,
    ]
))


vao1 = ctx.vertex_array(program, [(buffer1, '2f 2f', 'in_pos', 'in_uv')])
vao2 = ctx.vertex_array(program, [(buffer2, '2f 2f', 'in_pos', 'in_uv')])

# --- Render ---
# Make a loop here if you need to render multiple passes

fbo.use()
fbo.clear()

# draw quad with red texture
texture1.use()
vao1.render(mode=moderngl.TRIANGLE_STRIP)

# draw quad with green texture
texture2.use()
vao2.render(mode=moderngl.TRIANGLE_STRIP)

ctx.finish()

img = Image.frombytes('RGB', fbo.size, fbo.read())
img.save('output.png')
Grimmy
  • 3,992
  • 22
  • 25
0

In the code you create two framebuffers. You mentioned you want a single image, and I assume you may want the models to be shown side by side.

Here is a correction to your code:

Full code:

texture = ctx.texture(texture_image.size, 3, texture_image.tobytes())
texture.build_mipmaps()

texture1 = ctx.texture(texture_image1.size, 3, texture_image1.tobytes())
texture1.build_mipmaps()
# Vertex Buffer and Vertex Array

vbo = ctx.buffer(vertex_data)
vao = ctx.simple_vertex_array(
    prog, vbo, *['in_vert', 'in_text', 'in_norm'])

# Matrices and Uniforms
# Framebuffers

fbo = ctx.framebuffer(
    ctx.renderbuffer((1024, 512)), # doubled width to get
    ctx.depth_renderbuffer((1024, 512)), # doubled width to get
)

# Rendering

fbo.use()
ctx.enable(ModernGL.DEPTH_TEST)
ctx.viewport = (0, 0, 1024, 512)  # x, y, width, height
ctx.clear(0.9, 0.9, 0.9)

ctx.viewport = (0, 0, 512, 512)  # x, y, width, height
texture.use()  # first texture
vao.render()

ctx.viewport = (512, 0, 512, 512)  # x, y, width, height
texture1.use()  # second texture
vao.render()

data = fbo.read(components=3, alignment=1)
img = Image.frombytes('RGB', fbo.size, data, 'raw', 'RGB', 0, -1)
img.save('output.png')


if __name__ == '__main__':
    import time

    start = time.time()
    main()
    end = time.time()
    print(end-start)
Szabolcs Dombi
  • 5,493
  • 3
  • 39
  • 71
  • Hi. In this code am i supposed to use a model which already both models in it or something else? Also i tried running this code but it didn't give out any png file. It just ran and gave no output. Neither to terminal nor a png ouput. – Arun Padmanabhan Jul 28 '20 at 21:18
  • I've added an image of the model that i finally want. If you know of a way where i can just take a model of the watch over the cylinder and then just apply different textures to both those parts of the models that will be a HUGE help as well. Thanks for your help btw. – Arun Padmanabhan Jul 28 '20 at 21:25