If i open nrrd file using vtk, only black screen is output - python

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

Related

how to rotating triangle, not Triangular axis in opengl(python)?

I am working on rotating a triangle, under the premise that it does not touch the render part(i think this problem can't using camera)
I want the axis that rotates in the above state to be the center (based on world coordinates (0,0)) rather than local space. When the triangle is not at 0,0 .
this is my code
import glfw
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
def render(T):
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
# draw cooridnate
glBegin(GL_LINES)
glColor3ub(255, 0, 0)
glVertex2fv(np.array([0.,0.]))
glVertex2fv(np.array([1.,0.]))
glColor3ub(0, 255, 0)
glVertex2fv(np.array([0.,0.]))
glVertex2fv(np.array([0.,1.]))
glEnd()
# draw triangle
glBegin(GL_TRIANGLES)
glColor3ub(255, 255, 255)
glVertex2fv( (T # np.array([.0,.5,1.]))[:-1] )
glVertex2fv( (T # np.array([.0,.0,1.]))[:-1] )
glVertex2fv( (T # np.array([.5,.0,1.]))[:-1] )
glEnd()
def main():
if not glfw.init():
return
window = glfw.create_window(480,480,"1234", None,None)
if not window:
glfw.terminate()
return
glfw.make_context_current(window)
glfw.swap_interval(1)
while not glfw.window_should_close(window):
glfw.poll_events()
t = glfw.get_time()
s= np.sin(t)
q = np.array([[np.cos(t),-np.sin(t),.3],
[np.sin(t), np.cos(t),.3],
[0., 0., 0.]])
#th = np.radians(60)
#R = np.array([[np.cos(th), -np.sin(th),0.],
# [np.sin(th), np.cos(th),0.],
# [0., 0., 1.]])
#T = np.array([[1.,0.,.4],
# [0.,1.,.1],
# [0.,0.,1.]])
render(q)
glfw.swap_buffers(window)
glfw.terminate()
if __name__ == "__main__":
main()
-> This is my current state rotate, but not world space (0,0)
If you want to rotate and object around a pivot point:
Move the object so that the pivot point is at (0, 0).
Rotate the object
Move the object to its position in the world.
def main():
# [...]
while not glfw.window_should_close(window):
glfw.poll_events()
t = glfw.get_time()
pivot = (0.15, 0.15)
world_pos = (0.5, 0.5)
trans_pivot = np.array([[1, 0, -pivot[0]], [0, 1, -pivot[1]], [0, 0, 1]])
rotate = np.array([[np.cos(t),-np.sin(t), 0.0],
[np.sin(t), np.cos(t), 0.0],
[0, 0, 1]])
trans_world = np.array([[1, 0, world_pos[0]], [0, 1, world_pos[0]], [0, 0, 1]])
q = trans_world # rotate # trans_pivot
render(q)
glfw.swap_buffers(window)

Problem when resizing and saving a turtle screen

So I could save a big drawing and see its full size in an image visualizer, I resized my turtle window bigger then my monitor size. But the saved image is not being resized, so the drawing is being truncated:
from turtle import Screen, Turtle
import random
screen = Screen()
screen.setup(width=1200, height=2700, startx=None, starty=None)
t = Turtle(visible=False)
t.speed('fastest') # because I have no patience
t2 = Turtle(visible=False)
t2.speed('fastest') # because I have no patience
t3 = Turtle(visible=False)
t3.speed('fastest') # because I have no patience
def got(x, y, d): # to use goto more easily
t.penup()
t.goto(x, y)
t.pendown()
t.seth(d)
def flatoval(r): # Horizontal Oval
t.right(45)
for loop in range(2):
t.circle(r, 90)
t.circle(r / 2, 90)
got(0, -200, 0)
def elipse(r, a, b, c):
for extent in range(9):
rnd = random.randint(1, 20)
# if extent == 0 or extent == 3 or extent == 6 :
# t.color('red')
# if extent == 1 or extent == 4 or extent == 7 :
# t.color('yellow')
# if extent == 2 or extent == 5 or extent == 8 :
# t.color('blue')
t.circle(r, 10)
heading = t.heading()
if extent == 0 or extent == 1 or extent == 2:
# t.color('green')
t.setheading(0)
t.forward(rnd)
t.forward(a)
t.backward(rnd)
t.forward(c)
t.setheading(heading)
def canais(x, y, d, egnar):
for tog in range(egnar):
got(x, y, d)
elipse(100, 0, 0, 0)
elipse(50, 0, 0, 0)
elipse(100, 0, 0, 0)
elipse(50, 0, 0, 0)
d = d + 10
elipse(200, 0, 0, 0)
elipse(100, 0, 0, 0)
elipse(200, 0, 0, 0)
elipse(100, 0, 0, 0)
elipse(300, 0, 0, 0)
elipse(200, 0, 0, 0)
elipse(300, 0, 0, 0)
elipse(200, 0, 0, 0)
canais(0, -100, 0, 40)
ts = t.getscreen()
ts.getcanvas().postscript(file="canais_organizados_separadamente.eps")
I also tried this change:
screen = Screen()
screen.setup(width=1200, height=2700, startx=None, starty=None)
in place of:
screen = Screen()
screen.setup(400, 500)
Truncated image:
By default, the tkinter canvas postscript() method only captures the visible portion of the canvas. You need to tell it, via the width and height arguments, whether you want more than that. Below is your code reworked with that fix and several others to improve the performance and/or simplify the logic:
from turtle import Screen, Turtle
from random import randint
def got(x, y, d): # to use goto more easily
turtle.penup()
turtle.goto(x, y)
turtle.pendown()
turtle.setheading(d)
def flatoval(r): # Horizontal Oval
turtle.right(45)
for _ in range(2):
turtle.circle(r, 90)
turtle.circle(r / 2, 90)
def elipse(r, a, b, c):
for extent in range(9):
rnd = randint(1, 20)
turtle.circle(r, 10)
heading = turtle.heading()
if extent <= 2:
turtle.setheading(0)
turtle.forward(rnd)
turtle.forward(a)
turtle.backward(rnd)
turtle.forward(c)
turtle.setheading(heading)
def canais(x, y, d, egnar):
for _ in range(egnar):
got(x, y, d)
elipse(100, 0, 0, 0)
elipse(50, 0, 0, 0)
elipse(100, 0, 0, 0)
elipse(50, 0, 0, 0)
elipse(200, 0, 0, 0)
elipse(100, 0, 0, 0)
elipse(200, 0, 0, 0)
elipse(100, 0, 0, 0)
elipse(300, 0, 0, 0)
elipse(200, 0, 0, 0)
elipse(300, 0, 0, 0)
elipse(200, 0, 0, 0)
d += 10
screen = Screen()
screen.setup(1200, 1200)
turtle = Turtle(visible=False)
got(0, -200, 0)
screen.tracer(False)
canais(0, -100, 0, 36)
screen.tracer(True)
canvas = screen.getcanvas()
canvas.postscript(file="canais_organizados_separadamente.eps", width=1200, height=1200)

KalmanFilter always predict 0,0 in first time

The following code use to scan image from bottom to top. However, the prediction of Kalman filter always show 0,0 in first time. So that, it will draw line from bottom to 0,0. How to make path(Kalman filter) more similar to actual path?
The following code and image was updated.
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('IMG_4614.jpg',1)
img = cv2.resize(img, (600, 800))
hsv_image = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
low_yellow = np.array([18, 94, 140])
up_yellow = np.array([48, 255, 255])
hsv_mask = cv2.inRange(hsv_image, low_yellow, up_yellow)
hls_image = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)
low_yellow = np.array([0, 170, 24])
up_yellow = np.array([54, 255, 255])
hls_mask = cv2.inRange(hls_image, low_yellow, up_yellow)
mask = np.logical_or(hsv_mask,hls_mask)
offset = 100
height, width, _ = img.shape
previousPos = h
currentPos = h - offset
finalImg = img.copy()
is_first = True
initState = np.array([[np.float32(int(width/2))], [np.float32(h)]], np.float32)
last_measurement = current_measurement = initState
last_prediction = current_prediction = np.array((2, 1), np.float32)
kalman = cv2.KalmanFilter(4, 2)
kalman.measurementMatrix = np.array([[1, 0, 0, 0], [0, 1, 0, 0]], np.float32)
kalman.transitionMatrix = np.array([[1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]], np.float32)
while currentPos >= 0:
histogram = np.sum(mask[currentPos:previousPos,:], axis=0)
areas = np.where(histogram > 40)
if areas[0].size >= 2:
bottomLeft = areas[0][0]
topRight = areas[0][-1]
x = int((topRight-bottomLeft) / 2 + bottomLeft)
y = int((previousPos - currentPos) / 2 + currentPos)
last_prediction = current_prediction
last_measurement = current_measurement
current_measurement = np.array([[np.float32(x)], [np.float32(y)]], np.float32)
lmx, lmy = last_measurement[0], last_measurement[1]
cmx, cmy = current_measurement[0], current_measurement[1]
cv2.rectangle(finalImg, (bottomLeft,previousPos), (topRight,currentPos), (0,255,0), 5)
cv2.circle(finalImg,(x,y), 5, (0,0,255), -1)
cv2.line(finalImg, (lmx, lmy), (cmx, cmy), (255, 0, 0),5) #actual path
kalman.correct(current_measurement-initState)
current_prediction = kalman.predict()
lpx, lpy = last_prediction[0] + initState[0], last_prediction[1] + initState[1]
cpx, cpy = current_prediction[0] + initState[0], current_prediction[1] + initState[1]
cv2.line(finalImg, (lpx, lpy), (cpx, cpy), (255, 0, 255),5) # predict path
plt.figure(figsize=(10,10))
plt.imshow(cv2.cvtColor(finalImg, cv2.COLOR_BGR2RGB))
plt.show()
previousPos = currentPos
currentPos = currentPos - offset
This has already been answered here:
Kalman filter always predicting origin
OpenCV Kalman filter implementation does not let you set the an initial state.
You have to save your initial state and then when you call kalman.correct you have to subtract the initial state. And when you call kalman.predict you have to add your initial state.
Something like this pseudo-code:
initialState = (y,x)
....
kalman.correct(current_measurement - initialState)
...
prediction = kalman.predict()
prediction[0] = prediction[0] + initState[0]
prediction[1] = prediction[1] + initState[1]
I managed to change the initial state by changing statePost and statePre.
In init:
self.KF = cv2.KalmanFilter(nmbStateVars, nmbMeasts, nmbControlInputs)
A = self.KF.statePost
A[0:4] = self.measurement.reshape((4, 1))
# A[4:8] = 0.0
self.KF.statePost = A
self.KF.statePre = A
Then update as usual
self.updatedMeasts = self.KF.correct(self.measurement)

Inconsistent skybox rendering using different textures in Pygame + PyOpenGL

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!

Rendered model disappears at some asymptote

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.

Categories