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);
Related
I was trying to texture a cube in PyOpenGL when I ran into an issue. PyOpenGL only supports 1.10, 1.20, 1.30, 1.00 ES, and 3.00 ES. I need at least version 1.40 because in 1.40 layout was introduced and I need to use layout to get the texture coordinates. I'm trying to texture each side of a cube differently using array textures. Here are some of my code snippets if they are required.
Loading the array texture:
def load_texture_array(path,width,height):
teximg = pygame.image.load(path)
texels = teximg.get_buffer().raw
texture = GLuint(0)
layerCount = 6
mipLevelCount = 1
glGenTextures(1, texture)
glBindTexture(GL_TEXTURE_2D_ARRAY, texture)
glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipLevelCount, GL_RGBA8, width, height, layerCount)
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, layerCount, GL_RGBA, GL_UNSIGNED_BYTE, texels)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
My vertex shader:
#version 130
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 color;
out vec2 TexCoord;
void main() {
gl_Position = vec4(aPos, 1.0);
color = aColor;
TexCoord = aTexCoord;
}
my fragment shader:
#version 130
out vec4 FragColor;
in vec3 color;
in vec2 TexCoord;
uniform sampler2D texture;
void main()
{
FragColor = texture(texture, TexCoord);
}
If you need any more information, just ask in the comments and I will add it.
PyOpenGL only supports 1.10, 1.20, 1.30, 1.00 ES, and 3.00 ES [...]
No. PyOpenGL supports OpenGL ES versions from 1.0 to 3.1 and all desktop OpenGL version from 1.1 to 4.4 (See About PyOpenGL). You confused (desktop) OpenGL and OpneGL ES. Compare OpenGL specification - Khronos OpenGL registry and OpenGL ES Specification - Khronos OpenGL ES Registry.
In any case, the "supported" version only refers to OpenGL API. The OpenGL context version only depends on the graphics card. You can use any GLSL version which is supported by the current OpenGL context. PyOpenGL guarantees, that the OpenGL API is implemented up to version 4.4, but you can also use a higher version. It just means that OpenGL functions added later may be missing from the PyOpenGL API.
Log current version after creating the OpenGL window and making the context current:
print(glGetString(GL_VENDOR))
print(glGetString(GL_RENDERER))
print(glGetString(GL_VERSION))
print(glGetString(GL_SHADING_LANGUAGE_VERSION))
Vertex shader input Layout Qualifier are not supported in OpenGL Shading Language 1.30. You have to switch to OpenGL Shading Language 1.40:
#version 130
#version 140
Anyway you've mentioned that in your question - "I need at least version 1.40 because in 1.40 layout was introduced [...]"
The glsl sampler type has to match the texture target. The proper sampler type for (floating point) GL_TEXTURE_2D_ARRAY is sampler2Darray(See Sampler types):
uniform sampler2D texture;
uniform sampler2Darray texture;
For 2 dimensional array textures, 3 dimensional texture coordinates are required (See texture):
FragColor = texture(texture, TexCoord);
FragColor = texture(texture, vec3(TexCoord.st, index));
index is the index of the addressed texture in the array. You can provide the index by a Uniform variable or even by a 3rd component in the texture coordinate attribute.
I'm trying to make labels for some 3d objects, with an icon/triangle to show you where the object is, plus some text describing what the object is.
Essentially, I want to 1. display text using pyopengl and 2. have the text + icon stay at a constant size on the screen that can 3. still move around the screen.
(I looked around a bit and looked at orthographic projections, but I'm not sure that's what I should be using...)
I have not used opengl very much at all, so this might be a dumb question!
Any help is much appreciate.
A nice start would be to store your icon + text inside a quad.
There are plenty of good tutorials on "font rendering" which will help you to create the desired quad.
You can load your icon inside a openGL texture, once done you will have to create your quad and associate a vertex and fragment shader to it. The difficult part will be to set a fixed position to your quad, that won't be linked to your 3D scene.
In video-games when you want to draw the ui, or the hud (healthbar, minimap), you draw them at the end on top off everything. Thoose elements don't need the mvp matrices, or projection matrices you might be familiar with. All the magic will appen in the vertex shader, he will be responsible to to set the position of all your elements, the output of the vertex shader should be in the [-1, 1] range for all coordinates:
-1 -> left, top, near
1 -> right, bottom, far
We call this space ndc see diagram https://antongerdelan.net/opengl/raycasting.html
Your job will be to output values in this range. If you want your quad to be half the size of the width and a quarter of the height, centered in the middle, you can store this information in the vertices you sent to your shader.
GLfloat vertices[] = {-0.5, -0.25, 0, // bottom left corner
-0.5, 0.25, 0, // top left corner
0.5, 0.25, 0, // top right corner
0.5, -0.25, 0}; // bottom right corner
vertex shader, people might use pixel size quads with a ortho projection
in vec3 pos;
in vec2 uv;
out fuv;
void main()
{
//glm::mat4 translation = uTranslation might need to mvoe the quad on screen at runtime
gl_Position = vec4(pos.x, pos.y, -1.0, 1.0); //set it to near plane -1
fuv = uv;
}
fragment shader
in vec2 fuv;
out vec4 color;
uniform sampler2D renderedTexture;
void main(){
color = texture(renderedTexture, fuv);
}
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)
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.
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.