I am trying to make a minimal Moderngl example of about <150 lines so I can better understand how it works. The issue here is that when I try to render this texture (it is an image of a mushroom), it instead draws a fully white screen. What am I doing wrong?
Below is all of the code for the project (<200 lines). You can easily copy it and run it, however you do need an image to load into the texture
main.py
import moderngl as mg
import pygame
import sys
import texture
class Context:
def __init__(self, size):
self.window_size = size
pygame.display.set_mode(size, pygame.OPENGL | pygame.DOUBLEBUF)
self.ctx = mg.create_context()
self.clock = pygame.time.Clock()
m = pygame.image.load("images/Mushroom1.png").convert()
self.tex = texture.Texture(self.ctx, m)
def get_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.tex.destroy()
pygame.quit()
sys.exit()
def render(self):
self.ctx.clear(color=(1.0,0.0,0.0))
self.tex.render()
pygame.display.flip()
def run(self):
while True:
self.get_events()
self.render()
self.clock.tick(60)
c = Context((800,600))
c.run()
texture.py
import moderngl as mg
import numpy as np
import pygame
class Texture:
def __init__(self, ctx, img):
self.ctx = ctx
self.img = img
self.vbo = self.create_vbo()
self.texture = self.get_texture()
self.shader = self.get_shader_program("default")
self.vao = self.get_vao()
def render(self):
self.shader["Texture_0"] = 0
self.texture.use(location=0)
self.vao.render()
def destroy(self):
self.vbo.release()
self.texture.release()
self.shader.release()
self.vao.release()
def get_texture(self):
texture = self.ctx.texture((self.img.get_width(), self.img.get_height()), 4, pygame.image.tostring(self.img, "RGBA"))
return texture
def get_vao(self):
vao = self.ctx.vertex_array(self.shader, [(self.vbo, "2f 3f", "in_coords", "in_position")])
return vao
def create_vbo(self):
vertices = [(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)]
tex_coords = [(0.2, 0.2), (0.8, 0.2), (0.8, 0.8), (0.2, 0.8)]
indices = [(0, 1, 2), (0, 2, 3)]
vertices = self.get_data(vertices, indices)
tex_coords = self.get_data(tex_coords, indices)
vertices = np.hstack([tex_coords, vertices])
vbo = self.ctx.buffer(vertices)
return vbo
@staticmethod
def get_data(vertices, indices):
data = [vertices[ind] for t in indices for ind in t]
return np.array(data, dtype="f4")
def get_shader_program(self, shader_name):
with open(f"shaders/{shader_name}.vert") as vert:
v_shader = vert.read()
with open(f"shaders/{shader_name}.frag") as frag:
f_shader = frag.read()
program = self.ctx.program(vertex_shader = v_shader, fragment_shader = f_shader)
return program
vertex shader source:
#version 330
layout (location = 0) in vec2 in_coords;
layout (location = 1) in vec3 in_position;
out vec2 uv_0;
void main() {
vec2 uv_0 = in_coords;
gl_Position = vec4(in_position, 1.0);
}
and the fragment shader source:
#version 330
uniform sampler2D Texture_0;
layout (location = 0) out vec4 fragColor;
in vec2 uv_0;
void main() {
vec3 color = vec3(texture(Texture_0, uv_0));
fragColor = vec4(color, 1.0);
}
In your vertex shader, you are redeclaring uv_0
vec2 uv_0 = in_coords;
instead of assigning the value to the output value defined above.
Change vec2 uv_0 = in_coords;
to uv_0 = in_coords;