Pygame and PyOpenGL quad texturing problem - python

I'm trying to texturing a quad and to understand how this little sample works. My code is not original, it's mixed from various examples.
Texture: https://jamesmwake.files.wordpress.com/2015/10/uv_texture_map.jpg?w=660
My questions:
When I change GL_TEXTURE_MIN_FILTER to GL_TEXTURE_MAG_FILTER in
glTexParameteri the texture disappears. Why?
When I change GL_LINEAR to GL_NEAREST, nothing happens. The used
texture's resolution changed to 300x300px. Why is that?
How can I make mipmaps and then using them?
The loadImage() function make a texture. How knows PyOpenGL which
texture should be used in the makeQuad() function?
Code:
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
def loadImage():
img = pygame.image.load("checker_texture_downsized.jpg")
textureData = pygame.image.tostring(img, "RGB", 1)
width = img.get_width()
height = img.get_height()
bgImgGL = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, bgImgGL)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData)
glEnable(GL_TEXTURE_2D)
def makeQuad():
glBegin(GL_QUADS)
glTexCoord2f(0, 0)
glVertex2f(25, 25)
glTexCoord2f(0, 1)
glVertex2f(25, 775)
glTexCoord2f(1, 1)
glVertex2f(775, 775)
glTexCoord2f(1, 0)
glVertex2f(775, 25)
glEnd()
def main():
pygame.init()
display = (1280,800)
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
gluOrtho2D(0, 1280, 0, 800)
loadImage()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
makeQuad()
pygame.display.flip()
main()

Note, that drawing by glBegin/glEnd sequences, the fixed function pipeline matrix stack and fixed function pipeline per vertex light model, is deprecated since decades.
Read about Fixed Function Pipeline and see Vertex Specification and Shader for a state of the art way of rendering.
When I change GL_TEXTURE_MIN_FILTER to GL_TEXTURE_MAG_FILTER in glTexParameteri the texture disappears. Why?
The initial value of GL_TEXTURE_MIN_FILTER is GL_NEAREST_MIPMAP_LINEAR. If you don't change it and you don't create mipmaps, then the texture is not "complete" and will not be "shown". See glTexParameter.
See OpenGL 4.6 API Compatibility Profile Specification; 8.17 Texture Completeness; page 306
A texture is said to be complete if all the texture images and texture parameters required to utilize the texture for texture application are consistently defined.
... a texture is complete unless any of the following conditions hold true:
The minification filter requires a mipmap (is neither NEAREST nor LINEAR), and the texture is not mipmap complete.
When I change GL_LINEAR to GL_NEAREST, nothing happens. The used texture's resolution changed to 300x300px. Why is that?
If the texture is smaller than the region where the texture is wrapped to, the the minification filter has not effect, but the magnification would have an effect. If you set the value GL_NEAREST to the GL_TEXTURE_MAG_FILTER then the texels are not interpolated any more.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
How can I make mipmaps and then using them?
Mipmaps can be generated by glGenerateMipmap:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData)
glGenerateMipmap(GL_TEXTURE_2D)
The loadImage() function make a texture. How knows PyOpenGL which texture should be used in the makeQuad() function?
OpenGL is a state engine. Each state is kept until you change it again, even beyond frames. Since you have bound the texture in loadImage
glBindTexture(GL_TEXTURE_2D, bgImgGL)
the currently named texture object, which is bound to texture unit 0 is bgImgGL. This texture is used for drawing.

Related

Is there a way to display a pygame window over OpenGL?

I've been meddling around with PyOpenGL and pygame, and I managed to create an FPS-style camera object. Now I want to add a crosshairs in the middle of the screen, and potentially expand to display statistics on the sides of the window.
I've already looked into this, and it seems like you have to do some weird stuff with OpenGL like disabling depth test and changing the projection matrix, and until now none of that actually renders anything, and reduces performance.
It seems to me that it should be very easy, as all I want is something that is over everything else, and doesn't ever move. Is there really no way to tell pygame to draw over OpenGL so I can just draw two lines in the middle of the screen?
No there is no specified way to do that. Do it in OpenGL it is not that complicate.
According to your previous questions, I assume you want to do it in immediate mode using glBegin - glEnd sequences.
In the following I assume that width is the width of the window and height its height. You have to disable the depth test and back up the current matrices by glPushMatrix/glPopMatrix. Load the Identity matrix for the model view matrix and setup an orthographic projection corresponding to the window size (glOrtho):
cross_size = 100
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
glOrtho(0, width, height, 0, -1, 1)
glDisable(GL_DEPTH_TEST)
glColor3ub(128, 128, 128) # color of the crosshair
glBegin(GL_LINES)
glVertex2f(width/2 - cross_size/2, height/2)
glVertex2f(width/2 + cross_size/2, height/2)
glVertex2f(width/2, height/2 - cross_size/2)
glVertex2f(width/2, height/2 + cross_size/2)
glEnd()
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
Ensure that 2 dimensional texturing is disabled (glDisable(GL_TEXTURE_2D))

Mapping a texture to a quad opengl and pyglet

I am trying to map a texture to a quad in pyglet using the opengl functions but the quad is just showing up as white.
My code looks like this:
import pyglet
from pyglet.gl import *
glEnable(GL_TEXTURE_2D)
image = pyglet.image.load("redbrick.png")
texture = image.get_texture()
and my draw function:
def on_draw():
window.clear()
glBindTexture (GL_TEXTURE_2D, 13)
glBegin (GL_QUADS);
glTexCoord2i (0, 0)
glVertex2i (0, 0)
glTexCoord2i (1, 0)
glVertex2i (100, 0)
glTexCoord2i (1, 1)
glVertex2i (100, 100)
glTexCoord2i (0, 1)
glVertex2i (0, 100)
glEnd ()
Is there something I am doing wrong or missing out for it to be drawn as a white quad?
I see a few things that may be wrong. It would be nice to have the full code in like a pastebin but I can't comment to ask so...
your texture needs to be made into an opengl texture. You need to first convert your image into a raw data format. When you load the image do image.get_data(), compressed into a single line below. This isn't the most efficient way but a simple example.
After Binding the texture set drawing parameters.
Then hand the data to the video card in glTexImage2D.
data=pyglet.image.load("redbrick.png").get_data()
texture_id = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture_id)
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_RGBA, (image_width), (image_height), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
After that you should be able to use this texture if you bind with texture_id. 13 could be nothing for all I can see. Provide all the code and I could probably revise.

Render to texture - level of detail

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.

openGL/Pyglet texture graphics vanish when drawing sprites

My code draws a 3D world, with a 2D set of graphics on top of it. The 3D world is made out of textured quads and the textures are generated with the following code:
textures = []
image = pyglet.image.load(os.path.join(img_dir, "magic.png"))
textures.append(image.get_texture())
glEnable(textures[-1].target)
glBindTexture(textures[-1].target, textures[-1].id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height,
0, GL_RGBA, GL_UNSIGNED_BYTE,
image.get_image_data().get_data('RGBA',
image.width * 4))
The quads are then drawn with (the other 2 just have different coords):
glBindTexture(texture.target, texture.id)
glBegin(GL_QUADS)
glTexCoord2f(0.0, 0.0); glVertex3f(4.0, -2.0, 100.0+self.clock)
glTexCoord2f(1.0, 0.0); glVertex3f(4.0, -2.0, -100.0+self.clock)
glTexCoord2f(1.0, 1.0); glVertex3f(-4.0, -2.0, -100.0+self.clock)
glTexCoord2f(0.0, 1.0); glVertex3f(-4.0, -2.0, 100.0+self.clock)
glEnd()
I have set up the correct parameters when drawing the 3D and 2D graphics, and when I draw a 2D triangle on top of the 3D quad (with the following code) everything works fine:
glBegin(GL_TRIANGLES)
glVertex3f(0.0, 10, 0.0)
glVertex3f(-10, -10, 0)
glVertex3f(10, -10, 0)
glEnd()
However, I then try to draw a sprite and the 3D quads lose their texture and are drawn as white.
self.spr=pyglet.sprite.Sprite(pyglet.image.load(os.path.join(img_dir, "largebullet.png")).get_texture())
...
self.spr.draw()
Note that there's some fog in the background
I found a solution to this, by running glDisable(texture.target) on the enabled textures after they were drawn. It's not ideal because they have to be reenabled again, but for now it works ok.
I ran into a similar problem to this, and I found that the pyglet Sprite class tends to disable everything in the OpenGL state that it sets. You must reset a lot of things each time any sprite is drawn.

PIL Image.fromstring from PyOpengl buffer has the wrong size

I use PyOpenGL to draw a 2D Image. Then I want to use the Python Imaging Library (PIL) to store this image to disk. I use GLUT to display the image which works perfectly. But when I use PIL to store the image it extracts the wrong clipping. It has the wrong size.
Here is a minimal example which reproduces the effect and I also attach the output to make it more clear without running some code.
from OpenGL.GL import *
from OpenGL.GLUT import *
from PIL import Image
width, height = 640, 480
def DrawStuff():
poly1 = [(0,0), (640,0), (0,480)]
color = (0.5, 0.4, 0.3, 0.8)
glClear(GL_COLOR_BUFFER_BIT)
glPushMatrix()
glLineWidth(5.0)
glColor4f(*color)
glBegin(GL_POLYGON)
glVertex2f(poly1[0][0], poly1[0][1])
glVertex2f(poly1[1][0], poly1[1][1])
glVertex2f(poly1[2][0], poly1[2][1])
glVertex2f(poly1[0][0], poly1[0][1])
glEnd() # GL_POLYGON
glPopMatrix()
glPixelStorei(GL_PACK_ALIGNMENT, 1)
data = glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE)
image = Image.fromstring("RGBA", (width, height), data)
image.show()
image.save('out.png', 'PNG')
glutSwapBuffers()
# glut initialization
glutInit(sys.argv)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA)
glutCreateWindow("Draw Polygons")
glutInitWindowSize(width, height)
# set the function to draw
glutDisplayFunc(DrawStuff)
# enable the alpha blending
glEnable(GL_BLEND)
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
# prepare for 2D drawing
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, width, height, 0, 0, 1)
glDisable(GL_DEPTH_TEST)
glMatrixMode(GL_MODELVIEW)
# start the mainloop
glutMainLoop ()
this is how it looks int the GLUT window and how it is supposed to look like
and this is how the saved image looks like
I managed to solve my own Problem.
First I tried the following solution which might also help people with related problems:
solution1
But then, through extensive trial and error, I found that the solution is much simpler.
I simply had to swap two lines from:
glutCreateWindow("Draw Polygons")
glutInitWindowSize(width, height)
to
glutInitWindowSize(width, height)
glutCreateWindow("Draw Polygons")
Apparently the size has to be set before the window
You should consider that in OpenGL the coordinate system starts at different place than in PIL. Look at this.

Categories