Motivated by my incomplete answer to this question, I am implementing a simple skybox in PyOpenGL in accordance with this tutorial, making minor tweaks as needed for OpenGL 2.1/GLSL 120 and python2.7-isms. For the most part, it works successfully, but depending on what six images I pass to my cubemap, the images either end up swapped between a single pair of opposite faces or are randomly rotated! Below is the main class of this demo:
import pygame
import sys
import time
import glob
import numpy as np
from ctypes import *
from OpenGL.GL import *
from OpenGL.GL import shaders
from OpenGL.GLU import *
def load_shaders(vert_url, frag_url):
vert_str = "\n".join(open(vert_url).readlines())
frag_str = "\n".join(open(frag_url).readlines())
vert_shader = shaders.compileShader(vert_str, GL_VERTEX_SHADER)
frag_shader = shaders.compileShader(frag_str, GL_FRAGMENT_SHADER)
program = shaders.compileProgram(vert_shader, frag_shader)
return program
def load_cubemap(folder_url):
tex_id = glGenTextures(1)
face_order = ["right", "left", "top", "bottom", "back", "front"]
"""
#hack that fixes issues for ./images1/
face_order = ["right", "left", "top", "bottom", "front", "back"]
"""
face_urls = sorted(glob.glob(folder_url + "*"))
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_CUBE_MAP, tex_id)
for i, face in enumerate(face_order):
face_url = [face_url for face_url in face_urls if face in face_url.lower()][0]
face_image = pygame.image.load(face_url).convert()
"""
#hack that fixes issues for ./images2/
if face == "bottom":
face_image = pygame.transform.rotate(face_image, 270)
if face == "top":
face_image = pygame.transform.rotate(face_image, 90)
"""
"""
#hack that fixes issues for ./images3/
if face == "bottom" or face == "top":
face_image = pygame.transform.rotate(face_image, 180)
"""
face_surface = pygame.image.tostring(face_image, 'RGB')
face_width, face_height = face_image.get_size()
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, face_width, face_height, 0, GL_RGB, GL_UNSIGNED_BYTE, face_surface)
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)
glBindTexture(GL_TEXTURE_CUBE_MAP, 0)
return tex_id
def render():
global width, height, program
global rotation, cubemap
glEnable(GL_DEPTH_TEST)
glEnable(GL_TEXTURE_2D)
glEnable(GL_TEXTURE_CUBE_MAP)
skybox_right = [1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1]
skybox_left = [-1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1]
skybox_top = [-1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1]
skybox_bottom = [-1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1]
skybox_back = [-1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1]
skybox_front = [-1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1]
skybox_vertices = np.array([skybox_right, skybox_left, skybox_top, skybox_bottom, skybox_back, skybox_front], dtype=np.float32).flatten()
skybox_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, skybox_vbo)
glBufferData(GL_ARRAY_BUFFER, skybox_vertices.nbytes, skybox_vertices, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glClear(GL_COLOR_BUFFER_BIT)
glClear(GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(60, float(width)/height, 0.1, 1000)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
#glRotate(rotation, 0, 1, 0)#spin around y axis
#glRotate(rotation, 1, 0, 0)#spin around x axis
glRotate(rotation, 1, 1, 1)#rotate around x, y, and z axes
glUseProgram(program)
glDepthMask(GL_FALSE)
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap)
glEnableClientState(GL_VERTEX_ARRAY)
glBindBuffer(GL_ARRAY_BUFFER, skybox_vbo)
glVertexPointer(3, GL_FLOAT, 0, None)
glDrawArrays(GL_TRIANGLES, 0, 36)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glDisableClientState(GL_VERTEX_ARRAY)
glBindTexture(GL_TEXTURE_CUBE_MAP, 0)
glDepthMask(GL_TRUE)
glUseProgram(0)
pygame.display.flip()
if __name__ == "__main__":
title = "Skybox"
target_fps = 60
(width, height) = (800, 600)
flags = pygame.DOUBLEBUF|pygame.OPENGL
screen = pygame.display.set_mode((width, height), flags)
prev_time = time.time()
rotation = 0
cubemap = load_cubemap("./images1/")#front and back images appear swapped
#cubemap = load_cubemap("./images2/")#top and bottom images appear rotated by 90 and 270 degrees respectively
#cubemap = load_cubemap("./images3/")#top and bottom images appear rotated by 180 degrees
program = load_shaders("./shaders/skybox.vert", "./shaders/skybox.frag")
pause = False
while True:
#Handle the events
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
pause = not pause
#Do computations and render stuff on screen
if not pause:
rotation += 1
render()
#Handle timing code for desired FPS
curr_time = time.time()
diff = curr_time - prev_time
delay = max(1.0/target_fps - diff, 0)
time.sleep(delay)
fps = 1.0/(delay + diff)
prev_time = curr_time
pygame.display.set_caption("{0}: {1:.2f}".format(title, fps))
I use the following vertex and fragment shaders for displaying the cubemaps for the skybox:
./shaders/skybox.vert
#version 120
varying vec3 tex_coords;
void main()
{
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
tex_coords = vec3(gl_Vertex);
}
./shaders/skybox.frag
#version 120
varying vec3 tex_coords;
uniform samplerCube skybox;
void main()
{
gl_FragColor = textureCube(skybox, tex_coords);
}
I believe after much playing around that the error is in pygame's loading of the skybox images. I have tested three sets of skybox images. Each one has a different visual error and hack to fix them, which I have noted in the above code. Here are the sources for the three skyboxes for testing (be sure to rename the images so that they include right, left, top, bottom, back, or front in their respective file names).
./images1/: here
./images2/: here
./images3/: here (using the "rays" images in this zip)
All of these three skyboxes use different image formats (bmp, tga, and png respectively). How can I consistently handle all of these and future image cases robustly without relying on seemingly random rotations or image swaps? Any help or insight would be greatly appreciated.
Update:
I have created a github repository where you can test out the code without having to create a main.py and shaders, download the images, and rename and organize the contents yourself. This should make the code a lot easier to run in case you are interested in testing it out.
Here are the versions of everything that I am using:
python 2.7.12
pygame 1.9.2b1
pyopengl 3.1.0 (using opengl 2.1 and GLSL 120)
Let me know if you need any other information!
So, it turns out that all of the issues that I was having rendering the skybox could be boiled down to two causes, none of which were due to inconsistencies in how pygame loads images of various file formats!
The skybox images were inconsistent with one another in how the seams between two faces of the cubes were attached. This explained why each skybox image test result had different issues. Following the convention of being inside the cube describe in this question, I flipped and resaved the images in paint.
That alone was not enough, however. It turns out that the OpenGL convention for the z-axis in "cubemap-land" is flipped. This caused the front and back faces to be swapped with one another. The simplest fix that I could come up with is swapping the texture coordinates in the vertex shader. Here is the corrected vertex shader.
#version 120
varying vec3 tex_coords;
void main()
{
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
tex_coords = vec3(gl_Vertex) * vec3(1, 1, -1);
}
I have the code in the github mentioned in the question to reflect these changes as well as improve the camera for manually looking around.
Here is an animated gif of the final result for anyone who is interested!
Related
I'm programming using Python and Modern OpenGL, and I tried to implement the glMultiDrawArraysIndirect function in my code to draw a simple shape, I want to apply it later on to a more complex thing, but this is just a simple test that I don't know exactly where the error is.
import glfw, time, ctypes, math, pyrr
import numpy as np
from OpenGL.GL import *
from OpenGL.GL.shaders import *
glfw.init()
glfw.window_hint(glfw.SAMPLES, 4)
w = glfw.create_window(640, 480, "Galeria das Sombras", None, None)
glfw.make_context_current(w)
v = """
#version 430
in layout(location=0) vec3 posicao;
in layout(location=1) vec2 textura;
uniform mat4 view;
uniform vec3 def;
uniform vec3 pos;
uniform vec3 scale;
uniform float giro;
uniform float giro2;
out vec2 texcords;
void main(){
texcords = textura;
vec3 p = vec3(posicao.x*scale.x,posicao.y*scale.y,posicao.z*scale.z);
p = p+def;
p = vec3(-sin(giro)*p.z+cos(giro)*p.x,p.y,sin(giro)*p.x+cos(giro)*p.z);
p = vec3(p.x,-sin(giro2)*p.z+cos(giro2)*p.y,sin(giro2)*p.y+cos(giro2)*p.z);
p = p+pos+vec3(gl_InstanceID,0,0);
gl_Position = view*vec4(p,1);
}
"""
f = """
#version 430
in vec2 texcords;
uniform vec3 cor;
uniform sampler2D texinfo;
void main(){
gl_FragColor = vec4(cor,1)*texture(texinfo,texcords);
}
"""
shader = compileProgram(compileShader(v,GL_VERTEX_SHADER),compileShader(f,GL_FRAGMENT_SHADER))
tudo = [-1,-1,0,0,1,
1,-1,0,1,1,
1,1,0,1,0,
-1,1,0,0,0]
tudo = np.array(tudo, np.float32)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, len(tudo)*4, tudo, GL_STATIC_DRAW)
'''
tudo = [[1,1,0,3],[2,2,1,3]]
tudo = np.array(tudo, np.uint8)
VBI = glGenBuffers(1)
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, VBI)
glBufferData(GL_DRAW_INDIRECT_BUFFER, len(tudo)*4, tudo, GL_STATIC_DRAW)
'''
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 20, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 20, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glUseProgram(shader)
view = pyrr.matrix44.create_perspective_projection_matrix(60, 640/480, .1, 1000)
p = glGetUniformLocation(shader, "view")
glUniformMatrix4fv(p, 1, GL_FALSE, view)
glEnable(GL_DEPTH_TEST)
glEnable(GL_MULTISAMPLE)
glEnable(GL_TEXTURE_2D)
from PIL import Image
t = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, t)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
buffer = Image.open("p.jpg")
data = buffer.tobytes()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, buffer.size[0], buffer.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, data)
girar = 0
tempo = glfw.get_time()
tfps = glfw.get_time()
fps = 0
action = 0
while not glfw.window_should_close(w):
glfw.swap_buffers(w)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
view = pyrr.matrix44.create_perspective_projection_matrix(60, glfw.get_window_size(w)[0]/glfw.get_window_size(w)[1], .1, 1000)
p = glGetUniformLocation(shader, "view")
glUniformMatrix4fv(p, 1, GL_FALSE, view)
glViewport(0,0,glfw.get_window_size(w)[0],glfw.get_window_size(w)[1])
p = glGetUniformLocation(shader, "cor")
glUniform3f(p, 1, 0, 0)
p = glGetUniformLocation(shader, "def")
glUniform3f(p, 0, 0, 0)
p = glGetUniformLocation(shader, "scale")
glUniform3f(p, 1, 1, 1)
p = glGetUniformLocation(shader, "giro")
glUniform1f(p, girar*math.pi/180)
if glfw.get_time() - tempo > 1/60:
girar+=1
tempo = glfw.get_time()
if action > 0:
action-=.05
if action < 0:
action = 0
p = glGetUniformLocation(shader, "giro2")
if glfw.get_key(w, glfw.KEY_W) and action == 0:
action = 2
if action > 1:
glUniform1f(p, (1-(action-1))*-90*(math.pi/180))
else:
glUniform1f(p, action*-90*(math.pi/180))
p = glGetUniformLocation(shader, "pos")
glUniform3f(p, 0, 0, -10)
glMultiDrawArraysIndirect(GL_TRIANGLES, np.array([[0,3,1,0],[1,3,1,1]]), 2, 1)
fps+=1
if glfw.get_time() - tfps > 1:
print("FPS:",fps)
fps = 0
tfps = glfw.get_time()
glfw.poll_events()
if fps > 400:
time.sleep(.01)
glfw.destroy_window(w)
glfw.terminate()
In the VBO there is a square, but I was going to draw only 2 triangles using the first 3 points and then the last 3 next to each other, I didn't find many examples of this type of code on the internet, only glMultiDrawArraysIndirect documentation but I couldn't do it run in my code, at least not without giving a good lock, when I change the drawcount for 1 wheel but nothing appears on the screen.
There is and the drawcount I took from the site: http://docs.gl/gl4/glMultiDrawArraysIndirect
I tried to change the indirect value for different types of numpy arrays, with different uint dtypes but most of them either error or run without anything appearing on the screen does anyone know what's wrong?
When you specify the NumPy array you need to specify the type uint32. The last argument (stride) is specifies the distance in basic machine units between elements of the draw parameter array (16 bytes):
(see glMultiDrawArraysIndirect)
indirect = np.array([[3, 10, 0, 0], [3, 5, 1, 0]], dtype=np.uint32)
glMultiDrawArraysIndirect(GL_TRIANGLES, indirect, 2, 16)
or
glMultiDrawArraysIndirect(GL_TRIANGLES, indirect,
indirect.shape[0], indirect.dtype.itemsize * indirect.shape[1])
The above code does the same as:
(see glDrawArraysIndirect)
indirect1 = np.array([3, 10, 0, 0], dtype=np.uint32)
glDrawArraysIndirect(GL_TRIANGLES, indirect1)
indirect2 = np.array([3, 5, 1, 0], dtype=np.uint32)
glDrawArraysIndirect(GL_TRIANGLES, indirect2)
Respectively the same as:
(see glDrawArraysInstancedBaseInstance)
glDrawArraysInstancedBaseInstance(GL_TRIANGLES, 0, 3, 10, 0)
glDrawArraysInstancedBaseInstance(GL_TRIANGLES, 1, 3, 5, 0)
When you use glDrawArraysIndirect or glMultiDrawArraysIndirect, you need to create the following data structure:
(see GLAPI/glMultiDrawArraysIndirect)
typedef struct {
uint count;
uint instanceCount;
uint first;
uint baseInstance;
} DrawArraysIndirectCommand;
This can be achieved using a NumPy array with the data type uint32:
np.array([count, instanceCount, first, baseInstance], dtype=np.uint32)
I want to open two nrrd files and then overlap these two files. The first step in this step is to open the nrrd file using vtk's vktImageData or Mapper. I wrote the code to do this, but only black screen appears when I run it.
I've already done this by replacing the Image with a volume and printing the two files over and over. However, when I tried to do color mapping on this result, I noticed that the coordinates of the color mapping did not match the coordinates of the two files. At this time, I did not use the same mapper that rendered the object and the mapper of the color mapping, and I thought that the coordinates were different.
So I start from the beginning to use the same mapper that renders both files and color mapping.
import vtk
def main():
renWin = vtk.vtkRenderWindow()
renderer = vtk.vtkRenderer()
renWin.AddRenderer(renderer)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
reader = vtk.vtkNrrdReader()
reader.SetFileName('Segmentation-label_2.nrrd')
reader.Update()
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(reader.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
renderer.AddActor(actor)
iren.Initialize()
renWin.Render()
iren.Start()
if __name__ == "__main__":
main()
When I run this code, only black screen is output. I have searched a lot of data, but I do not know what the problem is.
I would appreciate your reply.
By adjusting the example given in https://vtk.org/Wiki/VTK/Examples/Python/DataManipulation/Cube.py this should load your 3D image data with a 2D slice that you move through.
#!/usr/bin/env python
import vtk
# Start by loading some data.
reader = vtk.vtkNrrdReader()
reader.SetFileName('Segmentation-label_2.nrrd')
reader.Update()
# Calculate the center of the volume
reader.Update()
(xMin, xMax, yMin, yMax, zMin, zMax) = reader.GetExecutive().GetWholeExtent(reader.GetOutputInformation(0))
(xSpacing, ySpacing, zSpacing) = reader.GetOutput().GetSpacing()
(x0, y0, z0) = reader.GetOutput().GetOrigin()
center = [x0 + xSpacing * 0.5 * (xMin + xMax),
y0 + ySpacing * 0.5 * (yMin + yMax),
z0 + zSpacing * 0.5 * (zMin + zMax)]
# Matrices for axial, coronal, sagittal, oblique view orientations
axial = vtk.vtkMatrix4x4()
axial.DeepCopy((1, 0, 0, center[0],
0, 1, 0, center[1],
0, 0, 1, center[2],
0, 0, 0, 1))
coronal = vtk.vtkMatrix4x4()
coronal.DeepCopy((1, 0, 0, center[0],
0, 0, 1, center[1],
0,-1, 0, center[2],
0, 0, 0, 1))
sagittal = vtk.vtkMatrix4x4()
sagittal.DeepCopy((0, 0,-1, center[0],
1, 0, 0, center[1],
0,-1, 0, center[2],
0, 0, 0, 1))
oblique = vtk.vtkMatrix4x4()
oblique.DeepCopy((1, 0, 0, center[0],
0, 0.866025, -0.5, center[1],
0, 0.5, 0.866025, center[2],
0, 0, 0, 1))
# Extract a slice in the desired orientation
reslice = vtk.vtkImageReslice()
reslice.SetInputConnection(reader.GetOutputPort())
reslice.SetOutputDimensionality(2)
reslice.SetResliceAxes(sagittal)
reslice.SetInterpolationModeToLinear()
# Create a greyscale lookup table
table = vtk.vtkLookupTable()
table.SetRange(0, 2000) # image intensity range
table.SetValueRange(0.0, 1.0) # from black to white
table.SetSaturationRange(0.0, 0.0) # no color saturation
table.SetRampToLinear()
table.Build()
# Map the image through the lookup table
color = vtk.vtkImageMapToColors()
color.SetLookupTable(table)
color.SetInputConnection(reslice.GetOutputPort())
# Display the image
actor = vtk.vtkImageActor()
actor.GetMapper().SetInputConnection(color.GetOutputPort())
renderer = vtk.vtkRenderer()
renderer.AddActor(actor)
window = vtk.vtkRenderWindow()
window.AddRenderer(renderer)
# Set up the interaction
interactorStyle = vtk.vtkInteractorStyleImage()
interactor = vtk.vtkRenderWindowInteractor()
interactor.SetInteractorStyle(interactorStyle)
window.SetInteractor(interactor)
window.Render()
# Create callbacks for slicing the image
actions = {}
actions["Slicing"] = 0
def ButtonCallback(obj, event):
if event == "LeftButtonPressEvent":
actions["Slicing"] = 1
else:
actions["Slicing"] = 0
def MouseMoveCallback(obj, event):
(lastX, lastY) = interactor.GetLastEventPosition()
(mouseX, mouseY) = interactor.GetEventPosition()
if actions["Slicing"] == 1:
deltaY = mouseY - lastY
reslice.Update()
sliceSpacing = reslice.GetOutput().GetSpacing()[2]
matrix = reslice.GetResliceAxes()
# move the center point that we are slicing through
center = matrix.MultiplyPoint((0, 0, sliceSpacing*deltaY, 1))
matrix.SetElement(0, 3, center[0])
matrix.SetElement(1, 3, center[1])
matrix.SetElement(2, 3, center[2])
window.Render()
else:
interactorStyle.OnMouseMove()
interactorStyle.AddObserver("MouseMoveEvent", MouseMoveCallback)
interactorStyle.AddObserver("LeftButtonPressEvent", ButtonCallback)
interactorStyle.AddObserver("LeftButtonReleaseEvent", ButtonCallback)
# Start interaction
interactor.Start()
del renderer
del window
del interactor
I am trying to draw conway's game of life with opengl. It works fine in regular pygame, but I've read that glTexImage2D is the way to go for fast drawing of stuff already in an array. I've checked out the examples and docs provided but not only are a fair amount of example links dead, but they were written for python 2 so I can't even run a lot of them without translating it. I've noticed that unlike most modern GUI packages, opengl doesn't really return anything so I think I'm just not applying the texture right. (as in, in pygame you'd generate the surface then apply the returned surface). The conways code works by taking in the alive and dead values and the dtype, then doing all the necessary checking based solely on the constructor arguments meaning that that I can change it from ubyte to float in the blink of an eye, so if that's the issue that's great.
At the moment it's just a black screen. When pygame.display.flip() is removed, it just stays white, so theoretically something is being drawn somewhere to change it to black. I have a feeling the problem lies around the glbindtexture method but honestly I don't know what the solution is.
I'll put the conways code in just in case anyone wants to run it. In the pygame mode I scale the image by the scale variable, but for opengl I just want it running first so the size of the conways array will be the window size for now, hence the 400. That means it'll take a bit, but once the window title is updated that's an indicator that the update has been completed.
opengl drawer:
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from random import random,seed
import numpy as np
from conways3 import Conways
from time import time
size = 400;
scale = 1;
conways = Conways(size,dead=0.0,alive=1.0,dtype=np.ubyte)
pygame.init()
flags = OPENGL|HWSURFACE|DOUBLEBUF
display = pygame.display.set_mode((size*scale, size*scale),flags)
########OPTIMIZATIONS##########
pygame.event.set_allowed([pygame.QUIT]);
###############################
running = True
clock = pygame.time.Clock()
t1 = t2 = t3 = 0
glEnable(GL_TEXTURE_2D)
try:
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
clock.tick()
t1 = time()
Z = conways.update()
t2 = time()
tid = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, tid)
glPixelStorei(GL_UNPACK_ALIGNMENT,1)
glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE, size,size,0,GL_LUMINANCE, GL_UNSIGNED_BYTE, Z)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
## surf = pygame.surfarray.make_surface(Z)
## display.blit(pygame.transform.scale(surf,(size*scale, size*scale)), (0, 0))
## pygame.display.update()
pygame.display.flip()
t3 = time()
pygame.time.wait(10)
pygame.display.set_caption("fps: {:.4f} calc: {:.4f} draw: {:.4f} tot: {:.4f}".format(clock.get_fps(), t2-t1, t3-t2,t3-t1))
## print(t2-t1)
except Exception as e:
print('-'*20)
print(e)
pygame.quit()
conways:
from random import random, seed
import numpy as np
from time import time
class Conways:
def __init__(self,size,dead=False,alive=True,dtype = np.bool8):
seed(1)
self.using1 = True;
self.size = size;
self.dead = dead;
self.alive = alive;
self.dtype = dtype;
self.arr1 = np.zeros((self.size,self.size),dtype=self.dtype);
self.arr2 = np.zeros((self.size,self.size),dtype=self.dtype);
for i in range(self.size):
for j in range(self.size):
self.arr1[i][j] = self.alive*(random() < 0.5);
def calcNeighbors(self,arr, i, j):
count = -1*arr[i][j];
for x in range(-1, 2):
for y in range(-1, 2):
count += (arr[(x+i)%self.size][(y+j)%self.size] == self.alive);
return count;
def calcEffi(self,arr, i, j):
count = 0
maxi = self.size - 1
if i > 0:
count += arr[i - 1][j] == self.alive
if i < maxi:
count += arr[i + 1][j] == self.alive
if j > 0:
count += arr[i][j - 1] == self.alive
if i > 0:
count += arr[i - 1][j - 1] == self.alive
if i < maxi:
count += arr[i + 1][j - 1] == self.alive
if j < maxi:
count += arr[i][j + 1] == self.alive
if i > 0:
count += arr[i - 1][j + 1] == self.alive
if i < maxi:
count += arr[i + 1][j + 1] == self.alive
return count;
def calc(self,arr1, arr2):
for i in range(self.size):
for j in range(self.size):
neighbors = self.calcEffi(arr1, i, j);
if neighbors < 2 or neighbors > 3:
arr2[i][j] = self.dead;
elif neighbors == 3:
arr2[i][j] = self.alive;
else:
arr2[i][j] = arr1[i][j];
def update(self):
if self.using1:
self.calc(self.arr1,self.arr2);
else:
self.calc(self.arr2,self.arr1);
self.using1 = not self.using1;
return self.arr2 if self.using1 else self.arr1;
Since the array values are in the range [0, 1], you have to use float32 respectively GL_FLOAT:
conways = Conways(size,dead=0.0,alive=1.0,dtype=np.float32)
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, size, size, 0, GL_LUMINANCE, GL_FLOAT, Z)
But the major issue is that you don't draw anything. You have to draw a quad on the entire viewport, with the texture wrapped on it:
glBindTexture(GL_TEXTURE_2D, tid)
glPixelStorei(GL_UNPACK_ALIGNMENT,1)
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, size,size, 0, GL_LUMINANCE, GL_FLOAT, Z)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glBegin(GL_TRIANGLE_FAN)
glTexCoord2f(0, 1)
glVertex2f(-1, -1)
glTexCoord2f(1, 1)
glVertex2f(1, -1)
glTexCoord2f(1, 0)
glVertex2f(1, 1)
glTexCoord2f(0, 0)
glVertex2f(-1, 1)
glEnd()
Or use glEnableClientState, glVertexPointer, glTexCoordPointer and glDrawArrays to draw onto the viewport:
vertices = np.array([-1, -1, 1, -1, 1, 1, -1, 1], dtype=np.float32)
tex_coords = np.array([0, 1, 1, 1, 1, 0, 0, 0], dtype=np.float32)
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(2, GL_FLOAT, 0, vertices)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords)
glDrawArrays(GL_TRIANGLE_FAN, 0, 4)
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
But note, that drawing by glBegin/glEnd sequences and also client-side capability of the OpenGL fixed function pipeline is deprecated since decades.
Read about Fixed Function Pipeline and see Vertex Specification and Shader for a state of the art way of rendering.
I've been trying to render a simple teapot with PyOpenGL, but have been running into strange issues. I can't seem to figure out exactly where the error originates from, despite the simplicity of the code.
Main.py
import pygame
from pygame.locals import *
from MV import *
import ctypes
from OpenGL.GL import *
from OpenGL.GL import shaders
from OpenGL.GLU import *
import teapot as tp
vertex_shader = '''
#version 420
in vec3 vpos_modelspace;
in vec3 vnorm_modelspace;
uniform mat4 mvp;
out vec4 vertcolor;
void main(){
vertcolor = vec4(vnorm_modelspace, 1.0);
gl_Position = mvp * vec4(vpos_modelspace, 1.0);
}
'''
fragment_shader = '''
#version 420
in vec4 vertcolor;
out vec4 fragcolor;
void main(){
fragcolor = vertcolor;
}
'''
model = tp.teapot
pygame.init()
canvas = pygame.display.set_mode((800, 600), DOUBLEBUF|OPENGL)
pygame.display.set_caption('Test')
glClearColor(.5, .5, .5, 1)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LESS)
glDisable(GL_CULL_FACE)
VERTEXSHADER = shaders.compileShader(vertex_shader, GL_VERTEX_SHADER)
FRAGMENTSHADER = shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER)
program = shaders.compileProgram(VERTEXSHADER, FRAGMENTSHADER)
glUseProgram(program)
vpos_loc = glGetAttribLocation(program, 'vpos_modelspace')
vnorm_loc = glGetAttribLocation(program, 'vnorm_modelspace')
mvp_loc = glGetUniformLocation(program, 'mvp')
eye = numpy.array([0, 0, 1], dtype=numpy.float32)
at = numpy.array([0, 0, 0], dtype=numpy.float32)
up = numpy.array([0, 1, 0], dtype=numpy.float32)
mvp = frustum(-1, 1, 1, -1, .1, 1000)#lookAt(eye, at, up)
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
vbo_pos = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo_pos)
vbo_norm = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo_norm)
verts = []
normals = []
for i in range(0, len(model.faces), 3):
index = model.faces[i:i+3]
verts.extend(model.vertices[3*index[0]:3*index[0]+3])
verts.extend(model.vertices[3*index[1]:3*index[1]+3])
verts.extend(model.vertices[3*index[2]:3*index[2]+3])
normals.extend(model.normals[3*index[0]:3*index[0]+3])
normals.extend(model.normals[3*index[1]:3*index[1]+3])
normals.extend(model.normals[3*index[2]:3*index[2]+3])
verts = numpy.array(verts, dtype=numpy.float32)
normals = numpy.array(normals, dtype=numpy.float32)
glBindBuffer(GL_ARRAY_BUFFER, vbo_pos)
glBufferData(GL_ARRAY_BUFFER, verts.size * verts.itemsize, verts, GL_STATIC_DRAW)
glVertexAttribPointer(vpos_loc, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
glEnableVertexAttribArray(vpos_loc)
glBindBuffer(GL_ARRAY_BUFFER, vbo_norm)
glBufferData(GL_ARRAY_BUFFER, normals.size * normals.itemsize, normals, GL_STATIC_DRAW)
glVertexAttribPointer(vnorm_loc, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
glEnableVertexAttribArray(vnorm_loc)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
while(True):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
glUseProgram(program)
rotation_matrix = rotate(.01, [0, 1, 0])
mvp = mvp # rotation_matrix
glUniformMatrix4fv(mvp_loc, 1, GL_FALSE, mvp.flatten())
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES, 0, int(verts.size/3))
glBindVertexArray(0)
glUseProgram(0)
pygame.display.flip()
main()
MV.py
import numpy
def normalize(vector):
return vector/numpy.linalg.norm(vector)
def translate(pos):
return numpy.array([[1, 0, 0, pos[0]],
[0, 1, 0, pos[1]],
[0, 0, 1, pos[2]],
[0, 0, 0, 1]], dtype=numpy.float32)
def rotate(angle, axis):
rads = angle * numpy.pi/180
v = normalize(axis)
c = numpy.cos(rads)
omc = 1-c
s = numpy.sin(rads)
return numpy.array([[v[0]*v[0]*omc + c, v[0]*v[1]*omc - v[2]*s, v[0]*v[2]*omc + v[1]*s, 0],
[v[0]*v[1]*omc + v[2]*s, v[1]*v[1]*omc + c, v[1]*v[2]*omc - v[0]*s, 0],
[v[0]*v[2]*omc - v[1]*s, v[1]*v[2]*omc + v[0]*s, v[2]*v[2]*omc + c, 0],
[0, 0, 0, 1]], dtype=numpy.float32)
def lookAt(eye, at, up):
n = normalize(at-eye)
u = normalize(numpy.cross(n, up))
v = normalize(numpy.cross(u, n))
rotate = numpy.array([[u[0], v[0], -n[0], 0],
[u[1], v[1], -n[1], 0],
[u[2], v[2], -n[2], 0],
[0, 0, 0, 1]], dtype=numpy.float32).transpose()
return rotate#translate(-eye)
def frustum(left, right, top, bottom, near, far):
rl = right-left
tb = top-bottom
fn = far-near
return numpy.array([[2*near/rl, 0, (right+left)/rl, 0],
[0, 2*near/tb, (top+bottom)/tb, 0],
[0, 0, -(far+near)/fn, -(2*far*near)/fn],
[0, 0, -1, 0]], dtype=numpy.float32)
The output shows the teapot being rotated (though not about the axis that I expected) and sort of shrinking and disappearing at rotations of 0, pi, 2pi, etc. I believe the teapot vertices are being processed correctly, as it does show up when rotated and is correctly shaded with normal values.
Output at 5 degrees - Model is 'growing'
Output at 30 degrees - Strange culling?
Output at 60 degrees - Relatively normal
Output at 170 degrees - Model is 'shrinking'
Output at 190 degrees - Model is 'growing' on the other side of the plane
At rotations 0, pi, 2pi, etc the model is completely invisible/too small to see.
Recently, I have started to learn openGL from this site => http://3dgep.com/?p=2365
and I encounter a problem. That's I didn't get the scene as the site shows.
I post my code on this site:
import pyglet
from pyglet.gl import *
from pyglet import clock, window
'''
http://www.learnersdictionary.com/search/aspect
a dictionary site
http://www.opengl.org/sdk/docs/man2/
opengl api reference
'''
def vector(type, *args):
'''
return a ctype array
GLfloat
GLuint
...
'''
return (type*len(args))(*args)
class model:
def __init__(self, vertices, colorMatrix, indice):
self.vertices = vertices
self.colorMatrix = colorMatrix
self.indice = indice
self.angle = 0
def update(self):
self.angle += 1
self.angle %= 360
def draw(self):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glRotatef(self.angle, 1, 1, 0)
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
glColorPointer(3, GL_FLOAT, 0, vector(GLfloat, *self.colorMatrix))
glVertexPointer(3, GL_FLOAT, 0, vector(GLfloat, *self.vertices))
glDrawElements(GL_QUADS, len(self.indice), GL_UNSIGNED_INT, vector(GLuint, *self.indice))
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_VERTEX_ARRAY)
class world:
def __init__(self):
self.element = []
def update(self, dt):
for obj in self.element:
obj.update()
def addModel(self, model):
self.element.append(model)
def draw(self):
for obj in self.element:
obj.draw()
def setup():
# look for GL_DEPTH_BUFFER_BIT
glEnable(GL_DEPTH_TEST)
win = window.Window(fullscreen=False, vsync=True, resizable=True, height=600, width=600)
mWorld = world()
cube = (
1, 1, 1, #0
-1, 1, 1, #1
-1, -1, 1, #2
1, -1, 1, #3
1, 1, -1, #4
-1, 1, -1, #5
-1, -1, -1, #6
1, -1, -1 #7
)
color = (
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
0, 1, 0,
0, 1, 0,
0, 0, 1,
0, 0, 1
)
indice = (
0, 1, 2, 3, # front face
0, 4, 5, 1, # top face
4, 0, 3, 7, # right face
1, 5, 6, 2, # left face
3, 2, 6, 7 # bottom face
#4, 7, 6, 5 #back face
)
obj = model(cube, color, indice)
mWorld.addModel(obj)
#win.event
def on_resize(width, height):
glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-10, 10, -10, 10, -10, 10)
glMatrixMode(GL_MODELVIEW)
return pyglet.event.EVENT_HANDLED
#win.event
def on_draw():
glClearColor(0.2, 0.2, 0.2, 0.8)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
mWorld.draw()
pyglet.clock.schedule(mWorld.update)
clock.set_fps_limit(30)
setup()
pyglet.app.run()
I think maybe I miss some important concepts so I can't get the correct result. Can anyone teach me what mistake I make? :(
Furthermore, there is something strange.
indice = (
0, 1, 2, 3, # front face
0, 4, 5, 1, # top face
4, 0, 3, 7, # right face
1, 5, 6, 2, # left face
3, 2, 6, 7 # bottom face
#4, 7, 6, 5 #back face
)
If I uncomment this line
from #4, 7, 6, 5 #back face to 4, 7, 6, 5 #back face
the screen will show nothing...
0.0 well, that's weird. I have tried to translate this code into C++ and it shows correctly.
I use opengl, glut, and c++. So, I think maybe that's the issue on pyglet. Whatever,
I can go on my studying about openGL :)
Finally, I find the way how to make this code run correctly!!
change the code here
self.vertices = vector(GLfloat, *vertices)
self.colorMatrix = vector(GLfloat, *colorMatrix)
self.indice = vector(GLuint, *indice)
and
glColorPointer(3, GL_FLOAT, 0, self.colorMatrix)
glVertexPointer(3, GL_FLOAT, 0, self.vertices)
glDrawElements(GL_QUADS, len(self.indice), GL_UNSIGNED_INT, self.indice)
well, Is the key point garbage collection?
I think
self.vertices = vector(GLfloat, *vertices)
this way makes there is an object to reference the vector so it won't be freed when I called glDrawElements(...) and others functions which need the c-type array
indice = (
0, 1, 2, 3, # front face
0, 4, 5, 1, # top face
4, 0, 3, 7, # right face
1, 5, 6, 2, # left face
3, 2, 6, 7 # bottom face
#4, 7, 6, 5 #back face )
You missed the last semicolon at 3,2,6,7[,] # bottom face.