Clipping a VBO rendered with a shader - python

I'm developping a 2D game engine, using PyOpenGL.
For coherence with previous versions of my engine, that used SDL, the graphic elements are first stored in a VBO, with a 2D coordinate system where (0, 0) is top-left and (640, 448) is bottom-right (so y axis is reversed). Let's call it SDL-coordinates.
Since my graphics use palette effects, I rendered them with shaders. My vertex shader simply convert my 2D coordinate system to the [-1;1] cube.
Now, I need to clip the display. My first idea was to do it via the pixel shader, by sending all vertices outside the clipping zone to a point outside the [-1 ; 1] cube (I took (2.0, 0.0, 0.0, 1.0)) but it went wrong : it deformed squared tiles which had some of their edges outside the clipping zone but not all.
So I consider using glFrustum, but I don't understand in which coordinate system I must specify the params.
In fact, I tried to put more or less anything as parameters without noticing anything when running the code. What am I doing wrong ?
For the moment, my drawing routine looks like that :
def draw(self):
glClearColor(1.0, 1.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glEnable( GL_TEXTURE_2D )
glActiveTexture( GL_TEXTURE0 )
glBindTexture( GL_TEXTURE_2D, self.v_texture)
glEnable( GL_TEXTURE_1D )
glActiveTexture( GL_TEXTURE1 )
shaders.glUseProgram(self.shaders_program)
shaders.glUniform1i(self.texture_uniform_loc, 0)
shaders.glUniform1i(self.palette_uniform_loc, 1)
shaders.glUniform2f(self.offset_uniform_loc, 0, 0)
shaders.glUniform4f(self.color_uniform_loc, 1, 1, 1, 1)
# Draw layers
for layer in self.layers: #[0:1]:
layer.draw()
shaders.glUseProgram( 0 )
pygame.display.flip()
In class Layer:
def draw(self):
glFrustum(0.0, 0.5, 0.0, 0.5, 0.1, 1.0) # I tried anything here...
# offset is an offset to add to coordinates (in SDL_coordinates)
shaders.glUniform2f(self.vdp.offset_uniform_loc, self.x, self.y)
# color is likely irrelevant here
shaders.glUniform4f(self.vdp.color_uniform_loc, *self.color_modifier)
glBindTexture( GL_TEXTURE_1D, self.palette.get_id())
self.vbo.bind()
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glVertexPointer(3, GL_FLOAT, 20, self.vbo)
glTexCoordPointer(2, GL_FLOAT, 20, self.vbo + 12)
glDrawArrays(GL_QUADS, 0, len(self.vbo))
self.vbo.unbind()
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
glDisableClientState(GL_VERTEX_ARRAY)
Note : I must say that I'm new to OpenGL. I learnt by reading tutorials and was quite confused with the 'old' and 'new' OpenGL.
I felt like frustum was more 'old' OpenGL, like many of tranformation matrix manipulation (most of it can be handled by vertex shaders). I may be totally wrong at that and glFrustum (or something else) may be unavoidable in my case. I'd like to read an article about what can be totally forgotten in 'old' OpenGL.

Unless you are using the built-in matrices (gl_ModelViewProjectionMatrix etc.) in your shaders, glFrustum won't do anything. If you aren't using those matrices, don't start using them now (you are correct that this is part of 'old' OpenGL).
It sounds like you want to use glScissor which defines a clipping rectangle in window coordinates (note that the origin of these is at the lower-left of the window). You have to enable it with glEnable(GL_SCISSOR_TEST).
As far as articles about what can be totally forgotten in 'old' OpenGL: Googling "Modern OpenGL", or "OpenGL deprecated" should give you a few starting points.

Related

PyOpenGL Constant Size 2D Labels for 3D Objects

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);
}

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))

How to scale a PolyData in vtk without translating it?

I am using VTK in python to import .stl files. then what i want to do is to scale down the mesh and making it smaller without changing the orientation matrix.
I tried vtkTransform with a scale tuple but the problem is the scaled polydata is getting rotated.
Here is the code:
def scaleSTL(filenameSTL, opacity=0.75, scale=(1,1,1), mesh_color="gold"):
colors = vtk.vtkNamedColors()
reader = vtk.vtkSTLReader()
reader.SetFileName(filenameSTL)
reader.Update()
transform = vtk.vtkTransform()
transform.Scale(scale)
transformFilter = vtk.vtkTransformPolyDataFilter()
transformFilter.SetInputConnection(reader.GetOutputPort())
transformFilter.SetTransform(transform)
transformFilter.Update()
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(transformFilter.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d(mesh_color))
actor.GetProperty().SetOpacity(opacity)
return actor
def render_scene(my_actor_list):
renderer = vtk.vtkRenderer()
for arg in my_actor_list:
renderer.AddActor(arg)
namedColors = vtk.vtkNamedColors()
renderer.SetBackground(namedColors.GetColor3d("SlateGray"))
window = vtk.vtkRenderWindow()
window.SetWindowName("Oriented Cylinder")
window.AddRenderer(renderer)
interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(window)
# Visualize
window.Render()
interactor.Start()
if __name__ == "__Main__":
filename = "400_tri.stl"
scale01 = (1, 1, 1)
scale02 = (0.5, 0.5, 0.5)
my_list = []
my_list.append(scaleSTL(filename, 0.75, scale01, "Gold"))
my_list.append(scaleSTL(filename, 0.75, scale02, "DarkGreen"))
render_scene(my_list)
I used my mesh file kidney.stl (yellow one) but what i getting is the scaled and rotated mesh. i set opacity to 0.75 to see both meshes. In the picture below you can see that the green one is completely moved but i want to scale so the green one is completely inside the original yellow mesh.
Simple answer (no explanation) can be found here: Scaling 3D models, finding the origin
That is because the scaling transformation is defined simply as multiplying the coordinates by a given factor (see e.g. https://www.tutorialspoint.com/computer_graphics/3d_transformation.htm). This intrinsically means that it is done with respect to a certain reference point. Your transform.Scale() call will use the origin (0,0,0) as this reference point and since your object is apparently not centered around origin, you get the translation (not rotation as you claim btw).
To get a locally centered scaling, you need to choose a reference point R on your object around which you want to scale (in your case, since you want the scaled object to be inside the original, you want some kind of center - since the object is "almost convex", centroid - average of all points - could be good enough). Translate the object by -R to align it with the coordinate system, scale and then translate back by +R.
Try a little exercise to visualize this: simple 2D example - draw yourself a square made of points with coordinates (2,2), (2,3), (3,3), (3,2) and "scale it by 2" - you get (4,4), (4,6),(6,6), (6,4) - draw it as well. Now try the alternative - first translate by the square's center (2.5,2.5), you get (-0.5,-0.5), (-0.5,0.5), (0.5,0.5), (0.5,-0.5) (draw it), scale by two, you get (-1,-1), (-1, 1), (1,1), (1,-1) (draw) and finally translate back by 2.5: (1.5, 1.5), (1.5,3.5), (3.5,3.5), (3.5, 1.5) and draw - see the difference?

PyOpenGL, issue with 3D graphics on a screen using glFrustum and glTranslate

Overview:
I am trying to create a 3D application similar to this:
www.youtube.com/watch?v=h9kPI7_vhAU.
I am using OpenCV2.2, Python2.7 and pyOpenGL.
This can be achieved by this background maths and code snippet where x, y, z are the positions of the viewers eye (as grabbed from a webcam!)
Issue:
When I do this, the object (a cube) that I have rendered becomes stretched along the z axis (into the screen) and I'm not too sure why. It is likened to looking down a very tall skyscraper from above (as opposed to a cube). The cube's position changes very rapidly in the z direction as the z position of the eye changes. This is a frame of the result, it has been stretched!
Code (with bigD's edit):
def DrawGLScene():
#get some parameters for calculating the FRUSTUM
NEAR_CLIPPING_PLANE = 0.01
FAR_CLIPPING_PLANE = 2
window = glGetIntegerv(GL_VIEWPORT)
WINDOW_WIDTH = window[2]
WINDOW_HEIGHT= window[3]
#do facial detection and get eye co-ordinates
eye = getEye()
#clear window
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
#before any projection transformation command comes these 2 lines:
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
#transform projection to that of our eye
glFrustum(NEAR_CLIPPING_PLANE*(-WINDOW_WIDTH /2 - eye[0])/eye[2],
NEAR_CLIPPING_PLANE*( WINDOW_WIDTH /2 - eye[0])/eye[2],
NEAR_CLIPPING_PLANE*(-WINDOW_HEIGHT/2 - eye[1])/eye[2],
NEAR_CLIPPING_PLANE*( WINDOW_HEIGHT/2 - eye[1])/eye[2],
NEAR_CLIPPING_PLANE, FAR_CLIPPING_PLANE)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(-eye[0],-eye[1],-eye[2])
drawCube()
glutSwapBuffers()
an example of the data getEye() returns is:
[0.25,0.37,1] if viewers is has their face near lower left of screen and is 1m away
[-0.5,-0.1,0.5] if viewers is has their face near upper right of screen and is 0.5m away
The cube when drawn has height, width, depth of 2 and its centre at (0,0,0).
I will provide the full code if anyone wants to do a similar project and wants a kickstart or thinks that the issue lies somewhere else than code provided.
The reason why you're getting strange results is because of this:
glTranslatef(-eye[0],-eye[1],-eye[2])
This call should be made after
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
Because the projection matrix is ready as it is with your glFrustum call, if you multiply it by a translation matrix that won't make it a perspective projection matrix anymore. The modelview matrix has to describe all world AND camera transformations.
Also bear in mind that if the only transformation you do on your modelview matrix is a translation, then you will always be staring down the negative-Z axis.

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.

Categories