Reputation: 45
So I'm trying to render 2D (and eventually 3D) models using OpenGL in Python. I've done this before in Java (long time ago) and somewhat started off by copying the program.
Everything looks perfect, but whenever I run the program I get a blank screen.
With an earlier version today (before I did some cleaning up) I would only sometimes get a blank screen (about 75% of the time) by simply restarting the kernel and not changing anything. I ran the program on three different computers with the same results. (The 75% blank was on two Ubuntu 20.04 systems, I did not try this part on Windows.)
My guess is that something is going wrong when loading the vertices used to render a 2D texture (in the on_enable(..)
of JGE2D.Renderer
), though I'm not sure of course.
I'm kind of out of ideas now and am hoping someone more experienced might know what's wrong, or perhaps have a suggestion as to where to look. The program basically takes a main engine to which the renderer is inserted as a separate module.
Currently textures are not rendered by the shader program.
I uploaded the full program to github (https://github.com/jeussa/JGE). Though, everything relevant should be down here.
The main program (minus a few utility classes):
import numpy as np
import os
import platform
import sys
import time
from PIL import Image
import traceback
import glfw as GLFW
from OpenGL import GL
# ==========
# = Engine =
# ==========
class Engine:
def __init__(self, fps = 60):
self.modules = []
self.sync = Sync(fps)
self.ups = UPS()
self.scheduler = Scheduler()
# = Start =
def start(self):
self.startat = time.time()
Console.info("==========================================================================")
Console.info("os.name == \"{a}\"".format(a=os.name))
Console.info("sys.platform == \"{a}\"".format(a=sys.platform))
Console.info("platform.system() == \"{a}\"".format(a=platform.system()))
Console.info("platform.machine() == \"{a}\"".format(a=platform.machine()))
Console.info("platform.architecture() == \"{a}\"".format(a=platform.architecture()))
Console.info("sys.version == \"{a}\"".format(a=sys.version))
Console.info("==========================================================================")
# Initialize
if not GLFW.init():
raise ValueError("Failed to initialize GLFW !")
GLFW.set_error_callback(Console.gl_error)
# Load modules
Console.info("Loading modules ...")
self.__call_modules__("on_load")
# Configure GLFW
GLFW.default_window_hints()
# Create window
self.window = GLFW.create_window(800, 600, "jeussa Graphics Engine", None, None)
if not self.window:
raise ValueError("Failed to create GLFW window !")
GLFW.make_context_current(self.window)
# Clear window
GL.glClearColor(0, .8, 0, 0)
# Enable modules
Console.info("Enabling modules ...")
self.__call_modules__("on_enable")
# Finish
Console.info("Startup done! [{a} ms]".format(a=round((time.time()-self.startat)*1000)))
# = Loop =
def loop(self):
Console.info("Entering loop ...")
while not GLFW.window_should_close(self.window):
self.sync.start()
# Events
GLFW.poll_events()
# Scheduler
self.scheduler.poll()
# Clear buffers
GL.glViewport(0, 0, *GLFW.get_window_size(self.window))
GL.glClear(GL.GL_COLOR_BUFFER_BIT|GL.GL_DEPTH_BUFFER_BIT)
# Loop modules
self.__call_modules__("on_loop")
# Update window
GLFW.swap_buffers(self.window)
self.ups.tick()
self.sync.end()
Console.info("Exiting loop ...")
# = End =
def end(self):
Console.info("Disabling modules ...")
self.__call_modules__("on_disable")
Console.info("Stopping JGEngine ...")
GLFW.terminate()
Console.info("JGEngine stopped")
# = Call Modules =
def __call_modules__(self, func):
for module in self.modules:
if hasattr(module.__class__, func):
attr = getattr(module.__class__, func)
if callable(attr):
try:
attr(module)
except Exception:
traceback.print_exc()
self.modules.remove(module)
The 2D renderer:
import numpy as np
from OpenGL import GL
from JGE import Console
from JGEMath import Matrix4
# ============
# = Renderer =
# ============
class Renderer:
def __init__(self, engine):
self.engine = engine
self.models = []
self.vao = None
self.vbo = None
self.shader = None
# = On Enable =
def on_enable(self):
# Create VAO
self.vao = GL.glGenVertexArrays(1)
GL.glBindVertexArray(self.vao)
self.vbo = GL.glGenBuffers(1)
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vbo)
GL.glBufferData(GL.GL_ARRAY_BUFFER, np.array([
-.5, .5, -.5, -.5, .5, .5, .5, -.5
], np.float32), GL.GL_STATIC_DRAW)
GL.glVertexAttribPointer(0, 2, GL.GL_FLOAT, False, 0, 0)
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
# Load Shader
self.shader = [GL.glCreateProgram()]
for program in [["lib/JGE2D.vs.glsl", GL.GL_VERTEX_SHADER], ["lib/JGE2D.fs.glsl", GL.GL_FRAGMENT_SHADER]]:
input = open(program[0], 'r')
id = GL.glCreateShader(program[1])
GL.glShaderSource(id, input.read())
GL.glCompileShader(id)
input.close()
if GL.glGetShaderiv(id, GL.GL_COMPILE_STATUS) == GL.GL_FALSE:
Console.error("Failed to compile shader !")
Console.error(GL.glGetProgramInfoLog(id))
raise ValueError("Failed to compile shader !")
self.shader.append(id)
GL.glAttachShader(self.shader[0], self.shader[1])
GL.glAttachShader(self.shader[0], self.shader[2])
GL.glLinkProgram(self.shader[0])
if GL.glGetProgramiv(self.shader[0], GL.GL_LINK_STATUS) == GL.GL_FALSE:
raise ValueError("Failed to link shader programs !")
self.uv_InvertY = GL.glGetUniformLocation(self.shader[0], "in_InvertY")
self.uv_ObjPos = GL.glGetUniformLocation(self.shader[0], "in_ObjPos")
# = On Loop =
def on_loop(self):
if not self.models:
return
# Prepare
GL.glUseProgram(self.shader[0])
GL.glBindVertexArray(self.vao)
GL.glDisable(GL.GL_DEPTH_TEST)
GL.glEnableVertexAttribArray(0)
GL.glBindAttribLocation(self.shader[0], 0, "in_Vector")
# Render
for model in self.models:
GL.glActiveTexture(GL.GL_TEXTURE0)
GL.glBindTexture(GL.GL_TEXTURE_2D, model.texture.handle)
GL.glUniformMatrix4fv(self.uv_ObjPos, 1, False, np.matrix.flatten(model.generate_translation_matrix()))
GL.glUniform1f(self.uv_InvertY, 1 if model.invert_y else 0)
GL.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4)
# Cleanup
GL.glUseProgram(0)
GL.glDisableVertexAttribArray(0)
GL.glBindVertexArray(0)
And the shader programs (vertex shader and fragment shader respectively):
#version 400 core
in vec2 in_Vector;
uniform float in_InvertY;
uniform mat4 in_ObjPos;
out vec2 pass_TexVec;
void main(void){
// Position
//gl_Position = in_ObjPos * vec4(in_Vector, 0.0, 1.0); // The position of the current vertex on the screen
gl_Position = vec4(in_Vector, 0.0, 1.0);
// Texture
if(in_InvertY > 0.5)pass_TexVec = vec2(in_Vector.x + 0.5, 0.5 - in_Vector.y);
else pass_TexVec = vec2(in_Vector.x + 0.5, in_Vector.y + 0.5);
}
#version 400 core
in vec2 pass_TexVec;
uniform sampler2D in_Texture;
out vec4 gl_FragColor;
void main(void){
// Texture
//out_Color = texture2D(in_Texture, pass_TexVec);
//if(out_Color.a < 0.5)discard;
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
Anyways, thanks for stopping by my post!!! :)
Upvotes: 1
Views: 224
Reputation: 210909
If a named buffer object is bound, then the 6th parameter of glVertexAttribPointer
is treated as a byte offset into the buffer object's data store. But the type of the parameter is a pointer anyway (c_void_p
).
So if the offset is 0, then the 6th parameter can either be None
or c_void_p(0)
:
GL.glVertexAttribPointer(0, 2, GL.GL_FLOAT, False, 0, 0)
GL.glVertexAttribPointer(0, 2, GL.GL_FLOAT, False, 0, None)
Upvotes: 1