The latest code below is a ported python program of the tunnel.cpp program from Superbible OpenGL 7th edition.
The cube appears, however the textures do not. There is also supposed to be slight movement toward the tunnel, and that's not happening either. Any ideas what could be causing this?
Update: Thanks to Rabbid76 the textures now appear and they are rendering correctly.
#!/usr/bin/python3
import sys
import time
sys.path.append("./shared")
#from sbmloader import SBMObject # location of sbm file format loader
from ktxloader import KTXObject # location of ktx file format loader
from sbmath import m3dDegToRad, m3dRadToDeg, m3dTranslateMatrix44, m3dRotationMatrix44, m3dMultiply, m3dOrtho, m3dPerspective, rotation_matrix, translate, m3dScaleMatrix44
fullscreen = True
#import numpy.matlib
#import numpy as np
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
print ('''
ERROR: PyOpenGL not installed properly.
''')
sys.exit()
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
render_prog = GLuint(0)
render_vao = GLuint(0)
class uniforms:
mvp = GLint
offset = GLint
tex_wall = GLuint(0)
tex_ceiling = GLuint(0)
tex_floor = GLuint(0)
uniform = uniforms()
class Scene:
def __init__(self, width, height):
global render_prog
global render_vao
global uniform
global tex_wall, tex_ceiling, tex_floor
self.width = width
self.height = height
vs = GLuint(0)
fs = GLuint(0)
vs_source = '''
#version 420 core
out VS_OUT
{
vec2 tc;
} vs_out;
uniform mat4 mvp;
uniform float offset;
void main(void)
{
const vec2[4] position = vec2[4](vec2(-0.5, -0.5),
vec2( 0.5, -0.5),
vec2(-0.5, 0.5),
vec2( 0.5, 0.5));
vs_out.tc = (position[gl_VertexID].xy + vec2(offset, 0.5)) * vec2(30.0, 1.0);
gl_Position = mvp * vec4(position[gl_VertexID], 0.0, 1.0);
}
'''
fs_source = '''
#version 420 core
layout (location = 0) out vec4 color;
in VS_OUT
{
vec2 tc;
} fs_in;
layout (binding = 0) uniform sampler2D tex;
void main(void)
{
color = texture(tex, fs_in.tc);
}
'''
vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vs, vs_source)
glCompileShader(vs)
glGetShaderInfoLog(vs)
fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fs, fs_source)
glCompileShader(fs)
glGetShaderInfoLog(vs)
render_prog = glCreateProgram()
glAttachShader(render_prog, vs)
glAttachShader(render_prog, fs)
glLinkProgram(render_prog)
glDeleteShader(vs)
glDeleteShader(fs)
glGetProgramInfoLog(render_prog)
uniform.mvp = glGetUniformLocation(render_prog, "mvp")
uniform.offset = glGetUniformLocation(render_prog, "offset")
glGenVertexArrays(1, render_vao)
glBindVertexArray(render_vao)
ktxobj = KTXObject()
tex_wall = ktxobj.ktx_load("brick.ktx")
tex_ceiling = ktxobj.ktx_load("ceiling.ktx")
tex_floor = ktxobj.ktx_load("floor.ktx")
textures = [ tex_floor, tex_wall, tex_ceiling ]
for i in range (0, 3):
glBindTexture(GL_TEXTURE_2D, textures[i])
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glBindVertexArray(render_vao)
def display(self):
green = [ 0.0, 0.1, 0.0, 0.0 ]
currentTime = time.time()
glViewport(0, 0, self.width, self.height)
glClearBufferfv(GL_COLOR, 0, green)
glUseProgram(render_prog)
proj_matrix = (GLfloat * 16)(*identityMatrix)
proj_matrix = m3dPerspective(m3dDegToRad(60.0), float(self.width) / float(self.height), 0.1, 100.0)
glUniform1f(uniform.offset, -(currentTime * 0.03) % 1) # negative sign to postive changes direction
textures = [ tex_wall, tex_ceiling, tex_wall, tex_floor ]
for i in range(0, 4):
RZ = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RZ, i * m3dDegToRad(90.0), 0.0, 0.0, 1.0)
T = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(T, -5, 0, -10)
RY = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RY, m3dDegToRad(90.0), 0.0, 1.0, 0.0)
S = (GLfloat * 16)(*identityMatrix)
m3dScaleMatrix44(S, 300.0, 10.0, 1.0)
mv_matrix = (GLfloat * 16)(*identityMatrix)
mv_matrix = m3dMultiply(RZ, m3dMultiply(T, m3dMultiply(RY, S)))
mvp = (GLfloat * 16)(*identityMatrix)
mvp = m3dMultiply(proj_matrix , mv_matrix )
glUniformMatrix4fv(uniform.mvp, 1, GL_FALSE, mvp)
glBindTexture(GL_TEXTURE_2D, textures[i]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glutSwapBuffers()
def reshape(self, width, height):
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
print ('key:' , key)
if key == b'\x1b': # ESC
sys.exit()
elif key == b'f' or key == b'F': #fullscreen toggle
if (fullscreen == True):
glutReshapeWindow(512, 512)
glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
else:
glutFullScreen()
fullscreen = True
print('done')
def init(self):
pass
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( int(1/60), self.timer, 0)
time.sleep(1/60.0)
if __name__ == '__main__':
start = time.time()
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
w1 = glutCreateWindow('OpenGL SuperBible - Tunnel')
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
many_cubes = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
The program is supposed to appear like:
Update: With the amazing code and insights by Rabbid76 the output is now rendering correctly. An animated gif of the output is below.
Dependency files: brick.ktx , ceiling.ktx , floor.ktx ,
and in 'shared' folder ktxloader.py , sbmath.py
You've to use the global statement to set the variables tex_wall, tex_ceiling, tex_floor in global namespace, in the constructor of Scene:
class Scene:
def __init__(self, width, height):
global tex_wall, tex_ceiling, tex_floor
# [...]
tex_wall = ktxobj.ktx_load("brick.ktx")
tex_ceiling = ktxobj.ktx_load("ceiling.ktx")
tex_floor = ktxobj.ktx_load("floor.ktx")
# [...]
Further there are some issues when you set the model matrices. The y-scale has to be 10.0, to scale the walls to proper height and width:
m3dScaleMatrix44(S, 30.0, 1.0, 1.0)
m3dScaleMatrix44(S, 300.0, 10.0, 1.0)
The translation has to be done before the rotation around the z-axis, because walls, floor and ceiling should be rotated displaced. Scaling has to be done first:
mv_matrix = m3dMultiply(T, m3dMultiply(RZ, m3dMultiply(S, RY)))
mv_matrix = m3dMultiply(RZ, m3dMultiply(T, m3dMultiply(RY, S)))
In "ktxloader" module ptr is used, when the data bytes are read from the bitmap:
glTexSubImage2D(GL_TEXTURE_2D, i, 0, 0, width, height, h.glformat, h.gltype, data[ptr:])
So ptr has to be incremented by h.keypairbyte:
data_start = ptr + h.keypairbytes
dt = data[data_start:]
ptr += h.keypairbytes
The floor and the ceiling are swapped:
textures = [ tex_wall, tex_floor, tex_wall, tex_ceiling ]
textures = [ tex_wall, tex_ceiling, tex_wall, tex_floor ]
Before the texture offset uniform (uniform float offset) is set you've to use % (modulo) operator, because the value in currentTime is top large and doesn't fit in single precision (32 bit) floating point value. Since the texture coordinates are in range [0.0, 1.0], the fraction part of the offset can be calculated by % 1. e.g.:
glUniform1f(uniform.offset, (currentTime * -0.03) % 1)
Related
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;
How to resolve an error that I'm getting trying to assign values to memory allocated by glMapBufferRange This is a problematic function, due to not having casts in python. I am trying to assign values to this memory, though am experiencing the error stated in the title. I have also tried to create a np.array with a dimension of the size material but to no avail. Perhaps just not doing it correctly.
Update and SUCCESS! With the amazing help of Rabbid76 and the fixes first to allocate and be able to assign to glMapBufferRange memory, and then to the sbmloader.py the program renders successfully. Thank you.
Final results also found on my github PythonOpenGLSuperBible7Glut
support files: hdrbloom_support.zip
expected output rendering is what the actual results are rendering:
source code:
#!/usr/bin/python3
import sys
import time
import ctypes
fullscreen = True
sys.path.append("./shared")
from sbmloader import SBMObject # location of sbm file format loader
from ktxloader import KTXObject # location of ktx file format loader
from textoverlay import OVERLAY_
from shader import shader_load, link_from_shaders
from sbmath import m3dDegToRad, m3dRadToDeg, m3dTranslateMatrix44, m3dRotationMatrix44, \
m3dMultiply, m3dOrtho, m3dPerspective, rotation_matrix, translate, m3dScaleMatrix44, \
scale, m3dLookAt, normalize
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
print ('''
ERROR: PyOpenGL not installed properly.
''')
sys.exit()
import numpy as np
from math import cos, sin
import glm
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
myobject = SBMObject()
ktxobject = KTXObject()
overlay = OVERLAY_()
MAX_SCENE_WIDTH = 2048
MAX_SCENE_HEIGHT = 2048
SPHERE_COUNT = 32
tex_src = GLuint(0)
tex_lut = GLuint(0)
render_fbo = GLuint(0)
filter_fbo = [ GLuint(0) for _ in range(2) ]
tex_scene = GLuint(0)
tex_brightpass = GLuint(0)
tex_depth = GLuint(0)
tex_filter = [ GLuint(0) for _ in range(2) ]
program_render = GLuint(0)
program_filter = GLuint(0)
program_resolve = GLuint(0)
vao = GLuint(0)
exposure = 1.0
mode = 0
paused = False
bloom_factor = 1.0
show_bloom = True
show_scene = True
show_prefilter = False
bloom_thresh_min = 0.8
bloom_thresh_max = 1.2
class UNIFORMS_:
class scene:
bloom_thresh_min = 0.8
bloom_thresh_max = 1.2
class resolve:
exposure = 1.0
bloom_factor = 1.0
scene_factor = 0
uniforms = UNIFORMS_()
ubo_transform = GLuint(0)
ubo_material = GLuint(0)
def load_shaders():
global program_render
global program_filter
global program_resolve
global uniforms
shaders = [GLuint(0), GLuint(0)]
if (program_render):
glDeleteProgram(program_render)
shaders[0] = shader_load("hdrbloom-scene.vs.glsl", GL_VERTEX_SHADER)
shaders[1] = shader_load("hdrbloom-scene.fs.glsl", GL_FRAGMENT_SHADER)
program_render = link_from_shaders(shaders, 2, True)
uniforms.scene.bloom_thresh_min = glGetUniformLocation(program_render, "bloom_thresh_min")
uniforms.scene.bloom_thresh_max = glGetUniformLocation(program_render, "bloom_thresh_max")
if (program_filter):
glDeleteProgram(program_filter)
shaders[0] = shader_load("hdrbloom-filter.vs.glsl", GL_VERTEX_SHADER)
shaders[1] = shader_load("hdrbloom-filter.fs.glsl", GL_FRAGMENT_SHADER)
program_filter = link_from_shaders(shaders, 2, True)
if (program_resolve):
glDeleteProgram(program_resolve)
shaders[0] = shader_load("hdrbloom-resolve.vs.glsl", GL_VERTEX_SHADER)
shaders[1] = shader_load("hdrbloom-resolve.fs.glsl", GL_FRAGMENT_SHADER)
program_resolve = link_from_shaders(shaders, 2, True)
uniforms.resolve.exposure = glGetUniformLocation(program_resolve, "exposure")
uniforms.resolve.bloom_factor = glGetUniformLocation(program_resolve, "bloom_factor")
uniforms.resolve.scene_factor = glGetUniformLocation(program_resolve, "scene_factor")
class Scene:
def __init__(self, width, height):
global myobject
global vao
global render_fbo
global tex_scene
global tex_brightpass
global tex_depth
global filter_fbo
global tex_filter
global tex_lut
global ubo_transform
global ubo_material
self.width = width
self.height = height
buffers = [ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 ]
glGenVertexArrays(1, vao)
glBindVertexArray(vao)
load_shaders()
exposureLUT = [ 11.0, 6.0, 3.2, 2.8, 2.2, 1.90, 1.80, 1.80, 1.70, 1.70, 1.60, 1.60, 1.50, 1.50, 1.40, 1.40, 1.30, 1.20, 1.10, 1.00 ]
glGenFramebuffers(1, render_fbo)
glBindFramebuffer(GL_FRAMEBUFFER, render_fbo)
tex_scene = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, tex_scene)
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA16F, MAX_SCENE_WIDTH, MAX_SCENE_HEIGHT)
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_scene, 0)
tex_brightpass = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, tex_brightpass)
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA16F, MAX_SCENE_WIDTH, MAX_SCENE_HEIGHT)
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, tex_brightpass, 0)
tex_depth = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, tex_depth)
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT32F, MAX_SCENE_WIDTH, MAX_SCENE_HEIGHT)
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, tex_depth, 0)
glDrawBuffers(2, buffers)
#glGenFramebuffers(2, filter_fbo[0])
filter_fbo = [ glGenFramebuffers(1) for _ in range(2)]
#glGenTextures(2, tex_filter[0])
tex_filter = [glGenTextures(1) for _ in range(2)]
for i in range(0,2):
glBindFramebuffer(GL_FRAMEBUFFER, filter_fbo[i])
glBindTexture(GL_TEXTURE_2D, tex_filter[i])
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA16F, MAX_SCENE_WIDTH if i==0 else MAX_SCENE_HEIGHT, MAX_SCENE_HEIGHT if i==0 else MAX_SCENE_WIDTH)
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_filter[i], 0)
glDrawBuffers(1, buffers)
glBindFramebuffer(GL_FRAMEBUFFER, 0)
tex_lut = glGenTextures(1)
glBindTexture(GL_TEXTURE_1D, tex_lut)
glTexStorage1D(GL_TEXTURE_1D, 1, GL_R32F, 20)
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 20, GL_RED, GL_FLOAT, exposureLUT)
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
myobject.load("torus.sbm")
glGenBuffers(1, ubo_transform)
glBindBuffer(GL_UNIFORM_BUFFER, ubo_transform)
glBufferData(GL_UNIFORM_BUFFER, (2 + SPHERE_COUNT) * glm.sizeof(glm.mat4), None, GL_DYNAMIC_DRAW)
class material:
diffuse_color = glm.vec3
specular_color = glm.vec3
specular_power = GLfloat(0)
ambient_color = glm.vec3
glGenBuffers(1, ubo_material)
glBindBuffer(GL_UNIFORM_BUFFER, ubo_material)
size_material = ctypes.sizeof(ctypes.c_float) * 12;
glBufferData(GL_UNIFORM_BUFFER, SPHERE_COUNT * size_material, None, GL_STATIC_DRAW)
mat = glMapBufferRange(GL_UNIFORM_BUFFER, 0, SPHERE_COUNT * size_material, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)
m = (GLfloat * 12 * SPHERE_COUNT).from_address(mat)
ambient = 0.002
for i in range(SPHERE_COUNT):
fi = 3.14159267 * i / 8.0
m[i][0:3] = (ctypes.c_float * 3)(sin(fi) * 0.5 + 0.5, sin(fi + 1.345) * 0.5 + 0.5, sin(fi + 2.567) * 0.5 + 0.5)
m[i][4:7] = (ctypes.c_float * 3)(2.8, 2.8, 2.9)
m[i][7] = 30
m[i][8:11] = (ctypes.c_float * 3)(ambient * 0.025, ambient * 0.025, ambient * 0.025)
ambient *= 1.5
glUnmapBuffer(GL_UNIFORM_BUFFER)
def display(self):
global program_filter
global program_resolve
global program_render
global tex_filter
global exposure
global vao
global filter_fbo
global ubo_transform
global ubo_material
global bloom_thresh_min
global bloom_thresh_max
global uniforms
global tex_brightpass
global myobject
global render_fbo
currentTime = time.time()
black = [ 0.0, 0.0, 0.0, 1.0 ]
one = 1.0
last_time = 0.0
total_time = 0.0
if (not paused):
total_time += (currentTime - last_time)
last_time = currentTime
t = total_time
glViewport(0, 0, self.width, self.height)
glBindFramebuffer(GL_FRAMEBUFFER, render_fbo)
glClearBufferfv(GL_COLOR, 0, black)
glClearBufferfv(GL_COLOR, 1, black)
glClearBufferfv(GL_DEPTH, 0, one)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LESS)
glUseProgram(program_render)
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo_transform)
class transforms_t:
mat_proj = glm.mat4
mat_view = glm.mat4
mat_model = [glm.mat4 for _ in range(SPHERE_COUNT)]
size_transforms_t = glm.sizeof(glm.mat4) * (SPHERE_COUNT+2)
mbuffer = glMapBufferRange(GL_UNIFORM_BUFFER, 0, size_transforms_t, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)
bufferp = (GLfloat * 16 * (SPHERE_COUNT+2)).from_address(mbuffer)
mat_proj = (GLfloat * 16)(*identityMatrix)
mat_proj = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 1.0, 1000.0)
T = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(T, 0.0, 0.0, -20.0)
bufferp[0] = mat_proj
bufferp[1] = T
for i in range(2, SPHERE_COUNT+2):
fi = 3.141592 * i / 16.0
# // float r = cosf(fi * 0.25f) * 0.4f + 1.0f
r = 0.6 if (i & 2) else 1.5
T1 = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(T1, cos(t + fi) * 5.0 * r, sin(t + fi * 4.0) * 4.0, sin(t + fi) * 5.0 * r)
RY = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RY, currentTime * m3dDegToRad(30.0) * fi, sin(t + fi * 2.13) * 75.0, cos(t + fi * 1.37) * 92.0, 0.0)
m_model = (GLfloat * 16)(*identityMatrix)
m_model = m3dMultiply(T1, RY)
bufferp[i] = m_model
glUnmapBuffer(GL_UNIFORM_BUFFER)
glBindBufferBase(GL_UNIFORM_BUFFER, 1, ubo_material)
glUniform1f(uniforms.scene.bloom_thresh_min, bloom_thresh_min)
glUniform1f(uniforms.scene.bloom_thresh_max, bloom_thresh_max)
myobject.render(SPHERE_COUNT)
glDisable(GL_DEPTH_TEST)
glUseProgram(program_filter)
glBindVertexArray(vao)
glBindFramebuffer(GL_FRAMEBUFFER, filter_fbo[0])
glBindTexture(GL_TEXTURE_2D, tex_brightpass)
glViewport(0, 0, self.height, self.width)
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
glBindFramebuffer(GL_FRAMEBUFFER, filter_fbo[1])
glBindTexture(GL_TEXTURE_2D, tex_filter[0])
glViewport(0, 0, self.width, self.height)
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
glUseProgram(program_resolve)
glUniform1f(uniforms.resolve.exposure, exposure)
if (show_prefilter):
glUniform1f(uniforms.resolve.bloom_factor, 0.0)
glUniform1f(uniforms.resolve.scene_factor, 1.0)
else:
glUniform1f(uniforms.resolve.bloom_factor, bloom_factor if show_bloom else 0.0)
glUniform1f(uniforms.resolve.scene_factor, 1.0 if show_scene else 0.0 )
glBindFramebuffer(GL_FRAMEBUFFER, 0)
glActiveTexture(GL_TEXTURE1)
glBindTexture(GL_TEXTURE_2D, tex_filter[1])
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, tex_brightpass if show_prefilter else tex_scene)
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
glutSwapBuffers()
def reshape(self, width, height):
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
print ('key:' , key)
if key == b'\x1b': # ESC
sys.exit()
elif key == b'f' or key == b'F': #fullscreen toggle
if (fullscreen == True):
glutReshapeWindow(512, 512)
glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
else:
glutFullScreen()
fullscreen = True
def init(self):
pass
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( int(1/60), self.timer, 0)
time.sleep(1/60.0)
if __name__ == '__main__':
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
w1 = glutCreateWindow('OpenGL SuperBible - HDR Bloom')
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
ported from: hdrbloom.cpp a Superbible Opengl example 7th ed. p.490
First of all note, that the memory layout of the (std140) structure in the uniform block
struct material_t
{
vec3 diffuse_color;
vec3 specular_color;
float specular_power;
vec3 ambient_color;
};
is
diffuse_color : 3 floats (x, y, z), 1 float alignment
specular_color : 3 floats (x, y, z),
specular_power : 1 float,
ambient_color : 3 floats (x, y, z), 1 float alignment
Please read Should I ever use a vec3 inside of a uniform buffer or shader storage buffer object?
and see OpenGL 4.6 API Core Profile Specification; 7.6.2.2 Standard Uniform Block Layout, page 144.
Thus the size of the buffer is 12 * ctypes.sizeof(ctypes.c_float)
size_material = ctypes.sizeof(ctypes.c_float) * 12;
Create the buffer for the uniform block array:
layout (binding = 1, std140) uniform MATERIAL_BLOCK
{
material_t material[32];
} materials;
glBufferData(GL_UNIFORM_BUFFER, SPHERE_COUNT * size_material, None, GL_STATIC_DRAW)
Map a 2 dimensional array, with shape (SPHERE_COUNT, 12) to the buffer memory:
mat = glMapBufferRange(GL_UNIFORM_BUFFER, 0, SPHERE_COUNT * size_material, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)
m = (GLfloat * 12 * SPHERE_COUNT).from_address(mat)
Assign the values in the loop by array assignment:
ambient = 0.002
for i in range(SPHERE_COUNT):
fi = 3.14159267 * i / 8.0
m[i][0:3] = (ctypes.c_float * 3)(sin(fi) * 0.5 + 0.5, sin(fi + 1.345) * 0.5 + 0.5, sin(fi + 2.567) * 0.5 + 0.5)
m[i][4:7] = (ctypes.c_float * 3)(2.8, 2.8, 2.9)
m[i][7] = 30
m[i][8:11] = (ctypes.c_float * 3)(ambient * 0.025, ambient * 0.025, ambient * 0.025)
ambient *= 1.5
The size of the 2nd uniform block
layout (binding = 0, std140) uniform TRANSFORM_BLOCK
{
mat4 mat_proj;
mat4 mat_view;
mat4 mat_model[32];
} transforms;
is
size_transforms_t = glm.sizeof(glm.mat4) * (SPHERE_COUNT+2)
Map a 2 dimensional array, with shape ((SPHERE_COUNT+2), 16) to the buffer memory:
mbuffer = glMapBufferRange(GL_UNIFORM_BUFFER, 0, size_transforms_t, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)
bufferp = (GLfloat * 16 * (SPHERE_COUNT+2)).from_address(mbuffer)
SBMObject.render_sub_object doesn't render the instances. It has to be somthing like:
def render_sub_object(self, object_index, instance_count, base_instance):
global index_type
glBindVertexArray(self.vao)
if instance_count == 0:
glDrawArrays(GL_TRIANGLES, 0, self.vertexcount)
else:
glDrawArraysInstancedBaseInstance(GL_TRIANGLES,
0,
self.vertexcount,
instance_count,
base_instance)
# [...]
show_prefilter, show_bloom and show_scene are boolean values, they'll never be equal 0.0. Either the uniform float bloom_factor or float scene_factor has to be grater then 0.0, else the fragment color is black.
glUniform1f(uniforms.resolve.bloom_factor, bloom_factor if show_bloom==0 else 0.0)
glUniform1f(uniforms.resolve.scene_factor, 1.0 if show_scene==0 else 0.0)
glUniform1f(uniforms.resolve.bloom_factor, bloom_factor if show_bloom else 0.0)
glUniform1f(uniforms.resolve.scene_factor, 1.0 if show_scene else 0.0 )
glBindTexture(GL_TEXTURE_2D, tex_brightpass if show_prefilter==0 else tex_scene)
glBindTexture(GL_TEXTURE_2D, tex_brightpass if show_prefilter else tex_scene)
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
How to python debug opengl starfield simulation that drawing a black screen? The code seems ok, but obviously something is off. Are there any additional functions such as glGetError that can verify variables to assist in debugging? I believe I have hurdled, coded correctly a glMapBufferRange function call.
UPDATE: With excellent support by Rabbid76 the program is now rendering the same as expected results. Thank you very much!
Expected output:
Support files: starfield_support.zip
Ported from: starfield.cpp
source code:
#!/usr/bin/python3
import sys
import time
import ctypes
fullscreen = True
sys.path.append("./shared")
from sbmloader import SBMObject # location of sbm file format loader
from ktxloader import KTXObject # location of ktx file format loader
from textoverlay import OVERLAY_
from shader import shader_load, link_from_shaders
from sbmath import m3dDegToRad, m3dRadToDeg, m3dTranslateMatrix44, m3dRotationMatrix44, \
m3dMultiply, m3dOrtho, m3dPerspective, rotation_matrix, translate, m3dScaleMatrix44, \
scale, m3dLookAt, normalize
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
print ('''
ERROR: PyOpenGL not installed properly.
''')
sys.exit()
import numpy as np
from math import cos, sin, floor
import glm
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
myobject = SBMObject()
ktxobject = KTXObject()
overlay = OVERLAY_()
render_prog = GLuint(0)
star_texture = GLuint(0)
star_vao = GLuint(0)
star_buffer = GLuint(0)
debugcontext=True
errors_only=True
class UNIFORMS_:
time=0
proj_matrix=0
uniforms = UNIFORMS_()
NUM_STARS = 2000
import random
random.seed (0x13371337)
def random_float():
return random.random()
def checkGLError():
status = glGetError()
if status != GL_NO_ERROR:
raise RuntimeError('gl error %s' % (status,))
#GLDEBUGPROC
def CB_OpenGL_DebugMessage(source, type, id, severity, length, message, userParam):
msg = message[0:length]
print('debug:', msg)
class Scene:
def __init__(self, width, height):
global render_prog
global star_vao
global star_buffer
global uniforms
global star_texture
self.width = width
self.height = height
vs = GLuint(0)
fs = GLuint(0)
fs_source = '''
#version 410 core
layout (location = 0) out vec4 color;
uniform sampler2D tex_star;
flat in vec4 starColor;
void main(void)
{
color = starColor * texture(tex_star, gl_PointCoord);
//color.r = 1.0;
}
'''
vs_source = '''
#version 410 core
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 color;
uniform float time;
uniform mat4 proj_matrix;
flat out vec4 starColor;
void main(void)
{
vec4 newVertex = position;
newVertex.z += time;
newVertex.z = fract(newVertex.z);
float size = (20.0 * newVertex.z * newVertex.z);
starColor = smoothstep(1.0, 7.0, size) * color;
newVertex.z = (999.9 * newVertex.z) - 1000.0;
gl_Position = proj_matrix * newVertex;
gl_PointSize = size;
}
'''
vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vs, vs_source)
glCompileShader(vs)
if not glGetShaderiv(vs, GL_COMPILE_STATUS):
print( 'compile error:' )
print( glGetShaderInfoLog(vs) )
fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fs, fs_source)
glCompileShader(fs)
if not glGetShaderiv(fs, GL_COMPILE_STATUS):
print( 'compile error:' )
print( glGetShaderInfoLog(fs) )
render_prog = glCreateProgram()
glAttachShader(render_prog, vs)
glAttachShader(render_prog, fs)
glLinkProgram(render_prog)
if not glGetProgramiv(render_prog, GL_LINK_STATUS):
print( 'link error:' )
print( glGetProgramInfoLog(render_prog) )
glDeleteShader(vs)
glDeleteShader(fs)
uniforms.time = glGetUniformLocation(render_prog, "time")
uniforms.proj_matrix = glGetUniformLocation(render_prog, "proj_matrix")
star_texture = ktxobject.ktx_load("star.ktx")
glGenVertexArrays(1, star_vao)
glBindVertexArray(star_vao)
class star_t:
position = glm.vec3
color = glm.vec3
size_star_t = ctypes.sizeof(ctypes.c_float) * 6; # same as glm.sizeof(glm.vec3) * 2
glGenBuffers(1, star_buffer)
glBindBuffer(GL_ARRAY_BUFFER, star_buffer)
glBufferData(GL_ARRAY_BUFFER, NUM_STARS * size_star_t, None, GL_STATIC_DRAW)
star = glMapBufferRange(GL_ARRAY_BUFFER, 0, NUM_STARS * size_star_t, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)
m = (GLfloat * 6 * NUM_STARS).from_address(star)
for i in range(0, 1000):
m[i][0] = (random_float() * 2.0 - 1.0) * 100.0
m[i][1] = (random_float() * 2.0 - 1.0) * 100.0
m[i][2] = random_float()
m[i][3] = 0.8 + random_float() * 0.2
m[i][4] = 0.8 + random_float() * 0.2
m[i][5] = 0.8 + random_float() * 0.2
glUnmapBuffer(GL_ARRAY_BUFFER)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, size_star_t, None)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, size_star_t, ctypes.c_void_p(glm.sizeof(glm.vec3) ) )
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
def display(self):
global render_prog
global star_vao
global uniforms
currentTime = time.time()
black = [ 0.0, 0.0, 0.0, 0.0 ]
one = [ 1.0 ]
t = currentTime
proj_matrix = (GLfloat * 16)(*identityMatrix)
proj_matrix = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 0.1, 1000.0)
t *= 0.1
t -= floor(t)
glViewport(0, 0, self.width, self.height)
glClearBufferfv(GL_COLOR, 0, black)
glClearBufferfv(GL_DEPTH, 0, one)
glUseProgram(render_prog)
glUniform1f(uniforms.time, t)
glUniformMatrix4fv(uniforms.proj_matrix, 1, GL_FALSE, proj_matrix)
glEnable(GL_BLEND)
glBlendFunc(GL_ONE, GL_ONE)
glBindVertexArray(star_vao)
glEnable(GL_PROGRAM_POINT_SIZE)
glDrawArrays(GL_POINTS, 0, NUM_STARS)
glutSwapBuffers()
def reshape(self, width, height):
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
print ('key:' , key)
if key == b'\x1b': # ESC
sys.exit()
elif key == b'f' or key == b'F': #fullscreen toggle
if (fullscreen == True):
glutReshapeWindow(512, 512)
glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
else:
glutFullScreen()
fullscreen = True
def init(self):
pass
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( int(1/60), self.timer, 0)
time.sleep(1/60.0)
if __name__ == '__main__':
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
glutInitContextVersion(4,1)
glutInitContextProfile(GLUT_CORE_PROFILE)
w1 = glutCreateWindow('OpenGL SuperBible - Starfield')
if debugcontext:
glDebugMessageCallback(CB_OpenGL_DebugMessage, None)
if errors_only:
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, None, GL_FALSE)
glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DONT_CARE, 0, None, GL_TRUE)
else:
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, None, GL_TRUE)
glEnable(GL_DEBUG_OUTPUT)
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS)
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Starting debug messaging service")
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
How to python debug opengl [...]
Those days it is common to generate debug output when debugging OpenGL.
The Kronos wiki page Debug Output tells everything about it what you've to know.
A debug message callback is specified by glDebugMessageCallback. The debug output has to be enabled by glEnable(GL_DEBUG_OUTPUT). Synchronous outputs can are generated when glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS) is enabled.
Which outputs are generated can be set by glDebugMessageControl in detail.
To activate the debug output you've to generate a debug call back function with the decorator #GLDEBUGPROC:
#GLDEBUGPROC
def CB_OpenGL_DebugMessage(source, type, id, severity, length, message, userParam):
msg = message[0:length]
print('debug:', msg)
Activate the debug callbacks after creating the window:
if __name__ == '__main__':
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
w1 = glutCreateWindow('OpenGL SuperBible - Starfield')
if debugcontext:
glDebugMessageCallback(CB_OpenGL_DebugMessage, None)
if errors_only:
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, None, GL_FALSE)
glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DONT_CARE, 0, None, GL_TRUE)
else:
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, None, GL_TRUE)
glEnable(GL_DEBUG_OUTPUT)
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS)
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Starting debug messaging service")
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
Porting over a chapter 7 example of instanced rendering from Superbible OpenGL 7th ed. and run into a problem with the function glBufferSubData
Whatever I do to it, it won't accept the data. Make it into a pointer, a byte string, list itself. Please help any assistance would be very much appreciated. Thank You.
Update: Using the excellent answer from Rabbid76 the function glBufferSubData is now accepting the data and the numpy version is very nice, the ctypes version is an insightful answer and very good to know. Also, about the 2nd parameter, it does indeed need to be a int or long, not a GLuint(0) in python.
Update and Success: Another very fine answer by Rabbid76 to get the rendering working.
The function glVertexAttribPointer needs a pointer to the lengths of the initial lists of data so it can offset. Thank You very much.
source code to: instancedattribs.py
#!/usr/bin/python3
import sys
import time
import ctypes
fullscreen = True
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
print ('''
ERROR: PyOpenGL not installed properly.
''')
sys.exit()
import numpy as np
square_buffer = GLuint(0)
square_vao = GLuint(0)
square_program = GLuint(0)
square_vs_source = '''
#version 410 core
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 instance_color;
layout (location = 2) in vec4 instance_position;
out Fragment
{
vec4 color;
} fragment;
void main(void)
{
gl_Position = (position + instance_position) * vec4(0.25, 0.25, 1.0, 1.0);
fragment.color = instance_color;
}
'''
square_fs_source = '''
#version 410 core
precision highp float;
in Fragment
{
vec4 color;
} fragment;
out vec4 color;
void main(void)
{
color = fragment.color;
}
'''
class Scene:
def __init__(self, width, height):
global square_buffer
global square_vao
global square_program
self.width = width
self.height = height
square_vertices = np.array([
-1.0, -1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0,
-1.0, 1.0, 0.0, 1.0], dtype='float32')
instance_colors = np.array([
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
1.0, 1.0, 0.0, 1.0], dtype='float32')
instance_positions = np.array([
-2.0, -2.0, 0.0, 0.0,
2.0, -2.0, 0.0, 0.0,
2.0, 2.0, 0.0, 0.0,
-2.0, 2.0, 0.0, 0.0], dtype='float32')
glGenVertexArrays(1, square_vao)
glGenBuffers(1, square_buffer)
glBindVertexArray(square_vao)
glBindBuffer(GL_ARRAY_BUFFER, square_buffer)
offset = 0 # not GLuint(0)
bufferSize = (len(square_vertices) + len(instance_colors) + len(instance_positions))*4
glBufferData(GL_ARRAY_BUFFER, bufferSize, None, GL_STATIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(square_vertices)*4, square_vertices)
offset += len(square_vertices)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_colors)*4, instance_colors)
offset += len(instance_colors)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_positions)*4, instance_positions)
offset += len(instance_positions)*4
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
offsetInstanceColor = len(square_vertices)*4
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstanceColor))
offsetInstancPosition = (len(instance_colors) + len(instance_positions))*4
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstancPosition))
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glEnableVertexAttribArray(2)
glVertexAttribDivisor(1, 1)
glVertexAttribDivisor(2, 1)
square_program = glCreateProgram()
square_vs = GLuint(0)
square_vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(square_vs, square_vs_source)
glCompileShader(square_vs)
glAttachShader(square_program, square_vs)
square_fs = GLuint(0)
square_fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(square_fs, square_fs_source)
glCompileShader(square_fs)
glAttachShader(square_program, square_fs)
glLinkProgram(square_program)
glDeleteShader(square_vs)
glDeleteShader(square_fs)
def display(self):
black = [ 0.0, 0.0, 0.0, 0.0 ]
glClearBufferfv(GL_COLOR, 0, black)
glUseProgram(square_program)
glBindVertexArray(square_vao)
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, 4)
glutSwapBuffers()
def reshape(self, width, height):
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
print ('key:' , key)
if key == b'\x1b': # ESC
sys.exit()
elif key == b'f' or key == b'F': #fullscreen toggle
if (fullscreen == True):
glutReshapeWindow(512, 512)
glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
else:
glutFullScreen()
fullscreen = True
print('done')
def init(self):
pass
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( int(1/60), self.timer, 0)
time.sleep(1/60.0)
if __name__ == '__main__':
start = time.time()
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
w1 = glutCreateWindow('OpenGL SuperBible - Instanced Attributes')
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
many_cubes = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
expected rendering output:
Ported from: instancedattribs.cpp
In compare to glBufferData, PyOpenGl's glBufferSubData the size parameter can't be omitted.
You've to pass the size of the buffer (in bytes) and a pointer to the buffer. But note, the 2nd and the 3rd parameter have to be a python int or long, even PyOpneGL's GLuint will cause an error.
You've some possibilities, either create an array PyOpenGL's GLfloat
offset = 0
dataArray = (GLfloat*len(square_vertices))(*square_vertices)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
or use pythons built-in ctypes library:
import ctypes
offset = 0
dataArray = (ctypes.c_float*len(square_vertices))(*square_vertices)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
or create a NumPy array:
import numpy as np
offset = 0
dataArray = np.array(square_vertices, dtype='float32')
glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
Note, for the 2nd and 3d parameter you can use a cast to either int (e.g int(offset)) or long (e.g. long(offset)).
Further note, the offset and size parameters to glBufferData, glBufferSubData and glVertexAttribPointer are values in size of bytes rather than the number of elements of the arrays.
The size in bytes is calculated by the number of elements multiplied by the size of 1 element.
The size of 1 element is 4, since the size of float (GLfloat, ctypes.c_float, 'float32') in bytes is 4.
If a named buffer object is bound, then the last parameter of glVertexAttribPointer is treated as a byte offset into the buffer object's data store, but the type of the parameter is still a pointer.
So you've to use ctypes.c_void_p(offset). If offset is 0 it is possible to pass None.
bufferSize = (len(square_vertices) + len(instance_colors) + len(instance_positions))*4
glBufferData(GL_ARRAY_BUFFER, bufferSize, None, GL_STATIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(square_vertices)*4, square_vertices)
offset += len(square_vertices)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_colors)*4, instance_colors)
offset += len(instance_colors)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_positions)*4, instance_positions)
offset += len(instance_positions)*4
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
offsetInstanceColor = len(square_vertices)*4
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstanceColor))
offsetInstancPosition = (len(instance_colors) + len(instance_positions))*4
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstancPosition))
import textwrap
import numpy as np
from ctypes import *
from OpenGL.GL import *
from OpenGL.GL.ARB.multitexture import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
class TestOpenglManager():
# -------- Magic functions --------
def __init__(self):
self.window_width = 800
self.window_height = 800
# -------- Glut stuff --------
def reshape(self, w, h):
self.window_width = w
self.window_height = h
def animate(self):
glutPostRedisplay()
def visible(self, vis):
if (vis == GLUT_VISIBLE):
glutIdleFunc(self.animate)
else:
glutIdleFunc(0)
def key_pressed(self, *args):
if args[0] == b"\x1b":
sys.exit()
def run(self):
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
glutInitWindowSize(self.window_width, self.window_height)
glutInitWindowPosition(800, 100)
glutCreateWindow(b'Test')
glutDisplayFunc(self.display)
glutReshapeFunc(self.reshape)
glutIdleFunc(self.animate)
glutVisibilityFunc(self.visible)
glutKeyboardFunc(self.key_pressed)
self.init()
glutMainLoop()
# -------- Resource allocation --------
def init_shaders(self):
def make_vs(source):
vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vs, source)
glCompileShader(vs)
result = glGetShaderiv(vs, GL_COMPILE_STATUS)
if not(result):
raise Exception("Error: {0}".format(
glGetShaderInfoLog(vs)
))
return vs
def make_fs(source):
fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fs, source)
glCompileShader(fs)
result = glGetShaderiv(fs, GL_COMPILE_STATUS)
if not(result):
raise Exception("Error: {0}".format(
glGetShaderInfoLog(fs)
))
return fs
def make_program(vs, fs):
program = glCreateProgram()
glAttachShader(program, vs)
glAttachShader(program, fs)
glLinkProgram(program)
return program
vs = textwrap.dedent("""
#version 130
in vec3 position;
void main()
{
gl_Position = vec4(position, 1.0f);
}
""")
fs = textwrap.dedent("""
#version 130
out vec4 frag_color;
uniform vec3 color;
void main() {
frag_color = vec4(color,1.0);
}
""")
self.prog = make_program(make_vs(vs), make_fs(fs))
def init_vbos(self):
vertices = np.array([
# Positions
0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0,
-0.5, 0.5, 0.0,
], dtype=np.float32)
indices = np.array([
0, 1, 3,
1, 2, 3
], dtype=np.int32)
vao_id = glGenVertexArrays(1)
vbo_id = glGenBuffers(1)
ebo_id = glGenBuffers(1)
glBindVertexArray(vao_id)
print("Vertices: Uploading {0} bytes".format(
ArrayDatatype.arrayByteCount(vertices)))
glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
glBufferData(
GL_ARRAY_BUFFER,
ArrayDatatype.arrayByteCount(vertices),
vertices, GL_STATIC_DRAW
)
print("Indices: Uploading {0} bytes".format(
ArrayDatatype.arrayByteCount(indices)))
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_id)
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
ArrayDatatype.arrayByteCount(indices),
indices, GL_STATIC_DRAW
)
print("Position: Location {0}".format(
glGetAttribLocation(self.prog, "position")))
vertex_stride = 3
glEnableVertexAttribArray(glGetAttribLocation(self.prog, "position"))
glVertexAttribPointer(glGetAttribLocation(self.prog, "position"),
3, GL_FLOAT, GL_FALSE, vertex_stride, c_void_p(0)
)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
self.obj_metadata = {
"vao_id": vao_id,
"vbo_id": vbo_id,
"ebo_id": ebo_id,
"type": "3v3c3t",
"stride": vertex_stride,
"indices": len(indices),
"vertices": vertices,
"num_triangles": int(len(vertices) / vertex_stride),
"num_vertices": len(vertices)
}
def init(self):
glClearColor(0.0, 0.0, 0.0, 0.0)
# glEnable(GL_MULTISAMPLE)
# glEnable(GL_DEPTH_TEST)
# glEnable(GL_TEXTURE_2D)
self.init_shaders()
self.init_vbos()
def display(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glBindVertexArray(self.obj_metadata["vao_id"])
glUseProgram(self.prog)
glUniform3f(glGetUniformLocation(self.prog, "color"), 0.0, 1.0, 0.0)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
glBindVertexArray(0)
glUseProgram(0)
glBindVertexArray(0)
glutSwapBuffers()
if __name__ == "__main__":
TestOpenglManager().run()
I've been trying to figure out for a while what could be wrong with the above snippet and I don't understand what's wrong, it should drawing a green triangle but instead I'll get a black screen
What's going on?
First of all your vertex_stride is incorrect. It should be 0 not 3. Lastly with PyOpenGL when calling glDrawElements you need to pass None or ctypes.c_void_p(0) and not 0.
The thing is that the stride is a byte offset between each vertex attribute. So as your vertex is laid out as:
X Y Z
X Y Z
X Y Z
Then there is an offset of 0 bytes between the next pair of X, Y, Z. However if your vertex was laid out in this manner:
X Y Z R G B
X Y Z R G B
X Y Z R G B
Then having the stride as 3 (or more correctly 3 * sizeof(float)) would be correct.