0

I am using ModernGL to render a 2D texture with pixel perfect precision. My image contains a series of boxes which are 1 px wide, but when I render them, some of the edges are wider than others. I am using orthographic projection which should give me 1:1 pixel accuracy. Here is a minimum working example:

import pygame
from pygame.locals import *
import moderngl
import numpy as np
from PIL import Image
import glm

pygame.init()
pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MAJOR_VERSION, 3)
pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MINOR_VERSION, 3)
pygame.display.gl_set_attribute(pygame.GL_CONTEXT_PROFILE_MASK, pygame.GL_CONTEXT_PROFILE_CORE)

SCREEN_WIDTH = 1100
SCREEN_HEIGHT = 900
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.DOUBLEBUF | pygame.OPENGL)
clock = pygame.time.Clock()
ctx = moderngl.create_context()
ctx.enable(moderngl.CULL_FACE)
ctx.cull_face = 'back'

vert = np.array([
    # x, y, z, tx, ty
    0.0, 1.0, 0.0, 0.0, 0.0,
    1.0, 0.0, 0.0, 1.0, 1.0,
    0.0, 0.0, 0.0, 0.0, 1.0,

    0.0, 1.0, 0.0, 0.0, 0.0,
    1.0, 1.0, 0.0, 1.0, 0.0,
    1.0, 0.0, 0.0, 1.0, 1.0,
], dtype='f4')

prog = ctx.program(
    vertex_shader =
    '''
    #version 330

    in vec3 in_vert;
    in vec3 in_color;
    in vec2 in_text;

    out vec3 v_color;
    out vec2 v_text;

    uniform mat4 projection;
    uniform mat4 view;
    uniform mat4 model;

    void main() {
        gl_Position = projection * view * model * vec4(in_vert, 1.0);
        v_color = in_color;
        v_text = in_text;
    }
    ''',
    fragment_shader =
    '''
    #version 330

    uniform sampler2D Texture;
    uniform vec3 Color;

    in vec3 v_color;
    in vec2 v_text;

    out vec4 f_color;

    void main() {
        f_color = texture(Texture, v_text);
    }
    ''',
)

vbo = ctx.buffer(vert)
vao = ctx.simple_vertex_array(prog, vbo, 'in_vert', 'in_text')

IMG_WIDTH = 858
IMG_HEIGHT = 647
img = Image.open("img.png")
texture = ctx.texture(img.size, 4, img.tobytes())
texture.use()

model = glm.mat4()
model = glm.translate(model, glm.vec3(0, 100, 0))

model = glm.scale(model, glm.vec3(IMG_WIDTH, IMG_HEIGHT, 0))
prog['model'].write(model)

view = glm.mat4()
prog['view'].write(view)

projection = glm.ortho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 100)
prog['projection'].write(projection)

vao.render()

while True:
    clock.tick(60)
    pygame.display.flip()

I have confirmed using file that my image is indeed 858x647:

img.png: PNG image data, 858 x 647, 8-bit/color RGBA, non-interlaced

EDIT: Added screenshots This is how the image is supposed to look: expected result

and this is how it appears in my opengl application: enter image description here

notice how some edges on the squares appear to be wider than others.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
00728M
  • 13
  • 4
  • @Rabbid76 Added some screenshots – 00728M Dec 27 '20 at 09:47
  • 1
    It looks like texture is with NEAREST filter. But modernGL page says that it is LINEAR by default. – Hihikomori Dec 27 '20 at 12:24
  • @Hihikomori I can confirm that it is using linear filtering. Manually setting the filter attribute `texture.filter = (moderngl.LINEAR, moderngl.LINEAR)` does not change its value. – 00728M Dec 28 '20 at 00:22
  • Pixel perfect rendering on OpenGL can be a challenge sometimes. If you get streching when using nearest filtering there are probable some inaccuracies in your scaling? – Grimmy Jan 08 '21 at 06:47

0 Answers0