I am having a strange problem where I am trying to use two different VBOs and using glDrawArrays(). However, both glDrawArrays() draws exactly the same figure, even though the VBO data is different
import ctypes
import numpy as np
import pygame as pg
from pygame.locals import *
from array import array
SCREEN_SIZE = (800, 600)
from OpenGL.GL import *
from OpenGL.GLU import *
def resize(width, height):
glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(60.0, float(width)/height, .1, 1000.)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def create_shader(shader_type,source):
shader = glCreateShader(shader_type)
glShaderSource(shader,source)
glCompileShader(shader)
print(glGetShaderiv(shader, GL_COMPILE_STATUS, None))
return shader
pg.init()
screen = pg.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF)
resize(*SCREEN_SIZE)
#creating and compiling fragment shader
fragment = create_shader(GL_FRAGMENT_SHADER,"""
#version 130
out vec4 finalColor;
void main()
{
finalColor = vec4(.0,0.0,1.0,1.0);
}
""")
#creating and compiling vertex shader
vertex = create_shader(GL_VERTEX_SHADER,"""
#version 130
in vec3 pos;
void main()
{
gl_Position=gl_ProjectionMatrix*gl_ModelViewMatrix *vec4(pos,1.0);
}
""")
#create and link program
program = glCreateProgram()
glAttachShader(program, fragment)
glAttachShader(program, vertex)
glLinkProgram(program)
#get location of the position variable in vertex shader
posAttrib = glGetAttribLocation(program, "pos")
vbo=glGenBuffers(1)
vbo2=glGenBuffers(1)
#vao=glGenVertexArrays(1)
#glBindVertexArray(vao)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
vertices=[1.5, -0.5, -4.0, 0.5, -0.5, -4.0, 0.5, 0.5, -4.0]
nparray = np.array(vertices,dtype=np.dtype('<f4'))
glBufferData(GL_ARRAY_BUFFER, nparray, GL_STATIC_DRAW)
vertices=[2., -0.5, -4.0, -0.5, -0.5, -2.0, 0.5, 0.5, -1.0]
#vertices=[1.5, -0.5, -4.0, 0.5, -0.5, -4.0, 0.5, 0.5, -4.0]
nparray = np.array(vertices,dtype=np.dtype('<f4'))
glBindBuffer(GL_ARRAY_BUFFER, vbo2)
glBufferData(GL_ARRAY_BUFFER, nparray, GL_STATIC_DRAW)
#specify how the variable pos gets data from the buffer data
glEnableVertexAttribArray(posAttrib)
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, False, 3*4,ctypes.cast(0, ctypes.c_void_p))
while 1:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(1.0, 1.0, 1.0, 0.0)
glUseProgram(program)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
print('vbo 1:',glGetInteger(GL_ARRAY_BUFFER_BINDING))
print('vbo data 1:',glGetBufferSubData(GL_ARRAY_BUFFER,0,3*3*4,None))
# glDrawArrays(GL_TRIANGLES, 0, 3)
glBindBuffer(GL_ARRAY_BUFFER, vbo2)
print('vbo 2:',glGetInteger(GL_ARRAY_BUFFER_BINDING))
print('vbo data 2:',glGetBufferSubData(GL_ARRAY_BUFFER,0,3*3*4,None))
glDrawArrays(GL_TRIANGLES, 0, 3)
pg.display.flip()
pg.time.delay(100)
The important parts of the code are where I upload the data with glBufferData and the draw commands glDrawArrays. If you try to comment away one or the other glDrawArrays(), you will see that the code actually draws the exact same figure. However, I have uploaded different data to the two VBOs (as can be seen by printing the data obtained with glGetBufferSubData). This is very strange. Why are the two glDrawArrays producing the same result when the VBO data is clearly different? Is this a bug in PyOpenGL or am I missing something fundamental here?
It is not the bound vertex buffer object, which define an array of generic vertex attribute data, but it is the state which is stored in the default vertex array object.
When you call glVertexAttribPointer the the array of generic vertex attribute data is defined. If at this point, an array buffer is bound, then the array definition refers to the buffer object.
This means you have to switch the array definition, before you draw the object:
# define an array of generic vertex attribute data which refers to "vbo"
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, False, 0,ctypes.cast(0, ctypes.c_void_p))
glEnableVertexAttribArray(posAttrib)
glDrawArrays(GL_TRIANGLES, 0, 3)
# define an array of generic vertex attribute data which refers to "vbo2"
glBindBuffer(GL_ARRAY_BUFFER, vbo2)
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, False, 0,ctypes.cast(0, ctypes.c_void_p))
glEnableVertexAttribArray(posAttrib)
glDrawArrays(GL_TRIANGLES, 0, 3)
As an alternative you can use 2 Vertex Array Objects:
# vertex data 1
vertices=[1.5, -0.5, -4.0, 0.5, -0.5, -4.0, 0.5, 0.5, -4.0]
nparray = np.array(vertices,dtype=np.dtype('<f4'))
vbo=glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, nparray, GL_STATIC_DRAW)
# vertex data 2
vertices=[2., -0.5, -4.0, -0.5, -0.5, -2.0, 0.5, 0.5, -1.0]
nparray = np.array(vertices,dtype=np.dtype('<f4'))
vbo2=glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo2)
glBufferData(GL_ARRAY_BUFFER, nparray, GL_STATIC_DRAW)
# vertex array object 1
vao=glGenVertexArrays(1)
glBindVertexArray(vao)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glEnableVertexAttribArray(posAttrib)
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, False, 3*4,ctypes.cast(0, ctypes.c_void_p))
# vertex array object 2
vao2=glGenVertexArrays(1)
glBindVertexArray(vao2)
glBindBuffer(GL_ARRAY_BUFFER, vbo2)
glEnableVertexAttribArray(posAttrib)
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, False, 3*4,ctypes.cast(0, ctypes.c_void_p))
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
# draw vertex array 1
glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES, 0, 3)
# draw vertex array 2
glBindVertexArray(vao2)
glDrawArrays(GL_TRIANGLES, 0, 3)
Related
I have a working camera in a 3D scene in OpenGL, and I decided I want to draw a crosshair in the middle. To do that, I want to use a separate shader for the crosshair (and eventually, HUD) and the 3D scene. I managed to make something work using glDrawArrays and a VBO with the line vertices in it.
The problem with this setup is that the cross extends all the way to the borders of the window, even though I specified small coordinates so that it is just in the center of the screen.
Here are my shaders (in order, vertex shader and fragment shader):
#version 450 core
layout (location = 0) in vec2 aPos;
void main() {
gl_Position = vec4(aPos, 0, 0);
}
#version 450 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
As you can see, they are extremely simple and do literally nothing with the coordinates. The drawing process looks something like this:
crosshair = np.array([
-0.02, 0,
0.02, 0,
0, -0.02,
0, 0.02], dtype = 'float32')
vbo_2d, vao_2d = glGenBuffers(1), glGenVertexArrays(1)
glBindVertexArray(vao_2d)
glBindBuffer(GL_ARRAY_BUFFER, vbo_2d)
glBufferData(GL_ARRAY_BUFFER, crosshair, GL_STATIC_DRAW)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
while not window.check_if_closed():
#render 3d stuff with another shader program and other vbos ad vaos
glBindVertexArray(0)
shader_program_2d.use()
glBindVertexArray(vao_2d)
glDrawArrays(GL_LINES, 0, 4)
#swap buffers etc.
This is what my window looks like:
Thanks in advance!
The issue is the computation of the Homogeneous clip space coordinate in the vertex shader. The normalized device space coordinate is computed by dividing the .xyz components of gl_Position by it's .w component (Perspective divide).
Since the .w component is 0, the resulting coordinates become infinite (except the coordinate itself is 0).
gl_Position = vec4(aPos, 0, 0);
gl_Position = vec4(aPos, 0, 1.0);
I am trying to set one stationary light source. In my program I have a cube which can be rotated.It seems like the light source is rotating too. But it should not
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
lightType = [-2.0, 0.0, -3.0, 1.0]
glLightfv(GL_LIGHT0, GL_POSITION, lightType)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
m = accum.get_rotation_matrix()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
glLoadMatrixf(m)
draw_cube()
UPD. Another example
def init():
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
lightType = [0, 0, 4, 1]
glLightfv(GL_LIGHT0, GL_POSITION, lightType)
glLightfv(GL_LIGHT0, GL_AMBIENT, (0, 0, 0, 0))
glLightfv(GL_LIGHT0, GL_DIFFUSE, (0, 0, 0, 0))
glLightfv(GL_LIGHT0, GL_SPECULAR, (1, 1, 1, 1))
glLightfv(GL_LIGHT0, GL_SPOT_CUTOFF, 180)
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, (0, 0, 1, 1))
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
I expect that one part of the cube will always be lighted, but it os not: after some rotation cube is not lighted (pictures 1 and 2). I would like, for example, some part of the front face of the cube to be always lighted, regardless cube's rotation
When the light position is set, then the position is transformed by the current model view matrix (GL_MODELVIEW). See glLight and glMatrixMode.
If the position is set, before the view matrix is set (model view is the Identity matrix), then the light position is not transformed and is set in view space. Hence the light moves with the point of view and is anchored to the camera.
If you want to set the light position in world space, then you have to set the view matrix before the light position is set. Thus the the light position is transformed by the view matrix (The vie matrix transforms form world space to view space).
glMatrixMode(GL_MODELVIEW)
glLoadMatrixf(viewMatrix)
lightType = [-2.0, 0.0, -3.0, 1.0]
glLightfv(GL_LIGHT0, GL_POSITION, lightType)
glMultMatrixf(modelMatrix)
draw_cube()
I'm working with PyOpenGL and GLUT at the moment and just want to visualize some points on the screen with VBO.
I'm pretty new to programming with PyOpenGL, so maybe the code isn't good at all. But should work in my opinion.
But I always get just one point. Anyone an idea, why?
Thanks for helping!!
import sys
import random #for random numbers
from OpenGL.GL import * #for definition of points
from OpenGL.GLU import *
from OpenGL.GLUT import * #for visualization in a window
import numpy as np
AMOUNT = 10
DIMENSION = 3
def changePoints(points):
for i in range(0, 3*AMOUNT):
x = random.uniform(-1.0, 1.0)
points[i]= points[i]*x
print(points)
return points
def displayPoints(points):
vbo=GLuint(0) # init the Buffer in Python!
glGenBuffers(1, vbo) # generate a buffer for the vertices
glBindBuffer(GL_ARRAY_BUFFER, vbo) #bind the vertex buffer
glBufferData(GL_ARRAY_BUFFER,sys.getsizeof(points), points, GL_STREAM_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, vbo) #bind the vertex buffer
glEnableClientState(GL_VERTEX_ARRAY) # enable Vertex Array
glVertexPointer(DIMENSION, GL_FLOAT,0, ctypes.cast(0, ctypes.c_void_p))
glBindBuffer(GL_ARRAY_BUFFER, vbo) #bind the vertex buffer
glDrawArrays(GL_POINTS, 0, AMOUNT)
glDisableClientState(GL_VERTEX_ARRAY) # disable the Vertex Array
glDeleteBuffers(1, vbo)
##creates Points
def Point():
points = np.arange(AMOUNT*3)
np.ascontiguousarray(points, dtype = np.float32)
points = changePoints(points)
#Visualization
displayPoints(points)
##clears the color and depth Buffer, call Point() and swap the buffers of the current window
def display():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
Point()
glutSwapBuffers()
def main():
##initials GLUT
glutInit(sys.argv)
#sets the initial display mode (selects a RGBA mode window; selects a double buffered window; selects a window with a depth buffer)
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH)
#defines the size of the Window
glutInitWindowSize(800, 1600)
#creates a window with title
glutCreateWindow(b'Points') #!string as title is causing a error, because underneath the PyOpenGL call is an old-school C function expecting ASCII text. Solution: pass the string in byte format.
glutDisplayFunc(display) #sets the display callback for the current window.
glutMainLoop() #enters the GLUT event processing loop.
main()
If you don't use any projection matrix, then you have to set up the vertex coordinates of the points in normalized device space, which is from (-1, -1, -1) to (1, 1, 1). This volume is projected to the viewport.
To solve your issue, points has to be an array of elements with data type np.float32:
def Point():
points = np.arange(AMOUNT*3, dtype = np.float32)
points = changePoints(points)
#Visualization
displayPoints(points)
and the coordinates of the points have to be in the range [-1.0, 1.0]:
def changePoints(points):
for i in range(len(points)):
points[i] = random.uniform(-1.0, 1.0)
print(points)
return points
The following Python program should draw a white triangle in the upper right quadrant of the window.
import pygame
from OpenGL.GL import *
from ctypes import *
pygame.init ()
screen = pygame.display.set_mode ((800,600), pygame.OPENGL|pygame.DOUBLEBUF, 24)
glViewport (0, 0, 800, 600)
glClearColor (0.0, 0.5, 0.5, 1.0)
glEnableClientState (GL_VERTEX_ARRAY)
vertices = [ 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0 ]
vbo = glGenBuffers (1)
glBindBuffer (GL_ARRAY_BUFFER, vbo)
glBufferData (GL_ARRAY_BUFFER, len(vertices)*4, (c_float*len(vertices))(*vertices), GL_STATIC_DRAW)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
glClear (GL_COLOR_BUFFER_BIT)
glBindBuffer (GL_ARRAY_BUFFER, vbo)
glVertexPointer (3, GL_FLOAT, 0, 0)
glDrawArrays (GL_TRIANGLES, 0, 3)
pygame.display.flip ()
It won't throw any errors, but unfortunately it doesn't draw the triangle.
I also tried to submit the buffer data as a NumPy-array:
glBufferData (GL_ARRAY_BUFFER, len(vertices)*4, np.array (vertices, dtype="float32"), GL_STATIC_DRAW)
Also no triangle is drawn. PyOpenGL... Y U NO draw VBOs ?
My system: Python 2.7.3 ; OpenGL 4.2.0 ; Linux Mint Maya 64 bit
Okay, I just found it:
The 4th parameter of the glVertexPointer call must be None representing a NULL pointer
glVertexPointer (3, GL_FLOAT, 0, None)
I swear, I searched for hours last night and didn't see that.
I've got a problem with my render to texture process. When I render the scene with width = height = 512 it has nearly no errors but the smaller the texture and scene gets, the more errors it gets.
The error is that regions of the texture are black, what makes no sense.
Here are some screenshots
512*512: http://www.ld-host.de/uploads/images/d9452fa0ba28494830fd96f0f15b9eba.png
128*128: http://www.ld-host.de/uploads/images/a39c141282a622f086d4a96b070a56a3.png
Here is my code how I render to texture and use the texture later
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(0,0,200,0,0,-1,0,1,0)
self.fbos = glGenFramebuffersEXT(1)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, self.fbos)
self.depthbuffers = (glGenRenderbuffersEXT(1)
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, self.depthbuffers)
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height)
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, self.depthbuffers)
self.textures = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.textures)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, None);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, self.textures, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, self.fbos);
glEnable(GL_CLIP_PLANE0)
glEnable(GL_CLIP_PLANE1)
glClipPlane(GL_CLIP_PLANE0, (0,0,1,-1 * self.start + self.diff))
glClipPlane(GL_CLIP_PLANE1, (0,0,-1,self.start))
# render the mesh
glTranslatef(-64,-64,-64)
glEnableClientState(GL_VERTEX_ARRAY) # Enable something in OpenGL
glEnableClientState(GL_COLOR_ARRAY) # Enable something in OpenGL
glBindBuffer(GL_ARRAY_BUFFER,self.vbo[1])
glColorPointer(3,GL_FLOAT,0,None) # Tell OpenGL that it contains only ColorValues
#Now the vertex Buffer with positions
glBindBuffer(GL_ARRAY_BUFFER,self.vbo[0])
glVertexPointer(3,GL_FLOAT,0,None) # Tell OpenGL that it contains the Positions for each Points
glDrawArrays(GL_TRIANGLES,0,len(self.verts)+len(self.color)) # Merge both
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_CLIP_PLANE0)
glDisable(GL_CLIP_PLANE1)
glBindTexture(GL_TEXTURE_2D,0)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
#Plane for showing texture
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(0,0,1,0,0,0,0,1,0)
glClearColor(1,1,1,0)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, self.textures)
glBegin(GL_QUADS)
glNormal3f(0.0, 1.0, 0.0)
glTexCoord2f(0.0, 0.0)
glVertex3f(-1, 1, -1)
glTexCoord2f(1.0, 0.0)
glVertex3f(1, 1, -1)
glTexCoord2f(1.0, 1.0)
glVertex3f(1, -1, -1)
glTexCoord2f(0.0, 1.0)
glVertex3f(-1, -1, -1)
glEnd();
glDisable(GL_TEXTURE_2D)
pygame.display.flip()
How can I achieve a better quality for the smaller texture? The mesh has no holes but the texture sometimes has.
I don't know what exactly you're expecting to see. You're drawing triangles/cubes/etc that are clearly smaller than the size of a pixel/sample size. Therefore, not all of them are going to be visible.
This is a standard aliasing problem: triangles that don't cover the center of a pixel/sample will not be visible. That's the nature of rasterization. And the only way to fix aliasing is to increase the number of samples you use. You can render at a higher resolution and downscale, or you could use MSAA or another real aliasing technique.