-1

in a nutshell, im writing vertexes for a cube into a vbo that i have setup correctly with a glbuffersubdata() call every frame, i then use gldrawarrays() and opengl does its thing and sends pixel data to a doublebuffer screen setup which i cycle through using a pygame.dislplay.flip() call. however in some cases, my game will delete some cubes, so after a certain time, it will send less cube data through the glbuffersubdata() call then the previous frame, resulting in the deleted cubes pixel data not being fully overwritten, creating a wierd artifact, where the game is not updating this cube, so its just a static overlay that follows the users camera perspective. i want to get rid of this artifact, but i cant.

first thing that came into mind, was if the the deleted cubes vertex data is not being overwritten, then why dont i just clear the pixel buffer, the alegeed colourbuffer and then send the new vertex data after that, so that only the new and intended verticies are renderes, however, this leads to even more artifacts, such as screen flickering black.

another thing that i thought about was to use the nnumber of triangles to render preset on the gldrawarrays() method, where for those who dont know, the final arguement, takes in the number of triangles i want to render out of all the hundreds of triangels i have sent opengl, so if i have 100 trianglees in my vbo but i set this arguement to 10 it will only render 10, similllarly if i render 0 i should get a black screen, after which i can clear the pixel data and then send new verticies, adnd render the new ones, however none of these were without additional artifacts, and just messing around some of the things i tried added in artifacts without even fixing the original ghosting artifact.

heres the code snippet where the problem i think lies:

if len(models) > 0:
    models2 = (ctypes.c_float*len(models))(*models2)
    print(len(models))
    glBufferSubData(GL_ARRAY_BUFFER, 0, len(models)*4, models2)
    glDrawArrays(GL_TRIANGLES,0,len(models))
    pygame.display.flip()
    sleep(1/fps/2)
    
else:
    glDrawArrays(GL_TRIANGLES,0,0)
    pygame.display.flip()
    sleep(1/fps/2)
    glDrawArrays(GL_TRIANGLES,0,0)
    pygame.display.flip()

and here is my simple incomplete game at the moment:

import pygame
from PIL import Image
import math
import ctypes
import numpy as np
from pygame.locals import *
from OpenGL.GL import shaders
from OpenGL.GL import *
from time import sleep
import random

def createcube():
    global defaultdistance

    cube1 = [0,0,0]
    cube2 = []
    cube = []
    physics = []
    physics1 = []
    obj = []
    newcube = []

    randomx = random.uniform(-1,1)
    randomy = random.uniform(-0.75,0.75)

    for i in range (3):
        for y in range(2):
            for x in range(2):
                cube1[i] = 0
                cube1[i-1] = y 
                cube1[i-2] = x 
                cube1[0] += abs(randomx)
                cube1[1] += abs(randomy)
                cube1[2] += defaultdistance

                if randomx < 0:
                    cube1[0] = cube1[0]*-1
                if randomy < 0:
                    cube1[1] = cube1[1]*-1

                for f in cube1:
                    f -= 0.5
                

                cube1.extend([x,y])
                cube2.extend([cube1])
                cube1 = [0,0,0]
        
        cube2.insert(-1,cube2[1])
        cube2.insert(-2,cube2[2])
        cube.extend(cube2)
        physics.append(cube2)
        cube2 = []
        
        for x in cube:
            newcube.extend(x)
 
    for i in range(3):
        for t in range(3):
            if t != i:
                obj.extend([(physics[i][0][t],physics[i][5][t])])
        obj.extend([physics[i][0][i]])
        physics1.append(obj)
        obj = []

    return(newcube , physics1)

    
    
defaultdistance = 3   
def main():
    reach = 5
    pointer1 = [0,0,reach]
    cubespawnrate = 0.5 #cubes spawned per second
    fps = 15 #target fps
    sensitivity = -300
    pygame.init()
    display = [1000,500]
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
    set_fov(45)
    set_render_distance(50)
    set_view_bearing(0,0,0)
    set_cam(0,0,0)
    set_aspect_ratio(display[0],display[1])
    models1 = []
    physics = []
    
        
    x , phys = createcube()
    models1.append(x)
    physics.append(phys)
    models = []
    for t in models1:
        models.extend(t)
    models2 = []
    models2.extend(models)
    
    
    for i in range(0,len(models),5):
            models2[i:i+3] = final_display_position(models[i:i+3],0,0,0,0,0,0,0,0,0)[0:3]



    
    
    

    
    
    vertex_shader="""
    #version 430
    in vec3 position;
    in vec2 texturecoordinate;
    out vec2 texcoordinate;
    void main()
    {
        gl_Position = vec4(position,1.0);
        texcoordinate = texturecoordinate;
        
    }
    """
    fragment_shader="""
    #version 430
    precision mediump float;
    in vec2 texcoordinate;
    uniform sampler2D texturergbadata;
    out vec4 colour;
    
    
    void main()
    {
        //g = 
        
        colour = texture2D(texturergbadata,texcoordinate);
        //colour = vec4(1.0f,1.0f,0.5f,1.0f);
    }
    """

    shader = OpenGL.GL.shaders.compileProgram(OpenGL.GL.shaders.compileShader(vertex_shader, GL_VERTEX_SHADER),
                                            OpenGL.GL.shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER))

    VBO=glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER,VBO)
    glBufferData(GL_ARRAY_BUFFER,18000,None,GL_STATIC_DRAW)

    position = glGetAttribLocation(shader,"position")
    glVertexAttribPointer(position, 3, GL_FLOAT, False, 20, ctypes.c_void_p(0))
    glEnableVertexAttribArray(position)

    width = 200
    height = 151

    image = Image.open('box.png')

    rgbadata = list(image.getdata())
    length = len(rgbadata)
    newlist = []
    
    for i in range(height):
        for s in range(width):
            newlist.extend(rgbadata[-2:-1])
            rgbadata.pop(-1)

    rgbadata.extend(newlist)         

    texture = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, texture)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, rgbadata)

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

    texturecoordinate = glGetAttribLocation(shader,"texturecoordinate")

    glEnableVertexAttribArray(texturecoordinate)

    glVertexAttribPointer(texturecoordinate, 2, GL_FLOAT, False, 20,ctypes.c_void_p(12))
    

    texture = glGetUniformLocation(shader, "texturergbadata")
    glActiveTexture(GL_TEXTURE0)
    timer = 0
    
    while True:
        while escape == 0:
            for event in pygame.event.get():
                pass
            pygame.mouse.set_visible(True)
            
            mousex , mousey = pygame.mouse.get_pos()

#           cube spawning

            if timer >= fps/cubespawnrate:
                x , phys = createcube()
                models1.append(x)
                physics.append(phys)
                models = []

                for t in models1:
                    models.extend(t)
                
                models2 = []
                models2.extend(models)
            
                timer = 0
            else:
                timer += 1      
                
#           collision detection
            objectindex = 0
            for i in physics:
                collision = 0
                print(pointer)
                for s in range(3):
                    list1 = [0,1,2]
                    list1.pop(s)
                    try:
                        t = i[s][2]/pointer[s]
                    except:
                        print('no colision')
                        continue
                        
                    intersection = list([t*x for x in pointer])
                    print(intersection)
                    h = 0
                    #if math.sqrt((math.sqrt(intersection[0]**2 + intersection[1]**2))**2 + intersection[2]**2) <= reach:
                    for p in list1:
                        g = intersection[p]
                        if g >= i[s][h][0] and g <= i[s][h][1]:
                            h += 1
                        if h == 2:
                            print('we have a collision')
                            collision += 1
                if collision >= 1:
                        print('deleting',objectindex,'th/nd/rd/st cube')
                        print(len(models1),len(physics))
                        models1.pop(objectindex) # this is where i delete some cubes
                        physics.pop(objectindex)
                        models = []
                        for t in models1:
                            models.extend(t)
                        streak += 1
                
                else:
                    streak = 0
                    multiplier = 1
                objectindex += 1
            
            models2 = []
            for i in range(0,len(models),5):
                                        
                    models2.extend(final_display_position(models[i:i+3],0,0,0,0,0,0,0,0,0)[0:3])
                    models2.extend(models[i+3:i+5]) # all this function does is take 3d coordinates and turn them into 2d display coordinates
                                                                        

            # here i am rendering the cubes
               
            if len(models) > 0:
                models2 = (ctypes.c_float*len(models))(*models2)
                print(len(models))
                glBufferSubData(GL_ARRAY_BUFFER, 0, len(models)*4, models2)
                glDrawArrays(GL_TRIANGLES,0,len(models))
                pygame.display.flip()
                sleep(1/fps/2)
        
            else:
                glDrawArrays(GL_TRIANGLES,0,0)
                pygame.display.flip()
                sleep(1/fps/2)
                glDrawArrays(GL_TRIANGLES,0,0)
                pygame.display.flip()
                
            

            
            
   
 
            
                
       
            pygame.mouse.set_pos(display[0]/2,display[1]/2)
            sleep(1/fps/2)
            
                            




        
        

            
           
            keyspressed = pygame.key.get_pressed()
            if keyspressed[K_ESCAPE]:
                print('escape pressed')
                debounce = 1
                print(debounce)
            #print(mousex , mousey)
            elif keyspressed[K_ESCAPE] == False and debounce > 0:
                print('debounce initiated,halt started')
                escape = 1
                debounce = 0
            else:
                debounce = 0
                
       
            
        while escape == 1:
           
            
            for event in pygame.event.get():
                pass
            pygame.mouse.set_visible(True)
            keyspressed = pygame.key.get_pressed()
            if keyspressed[K_ESCAPE]:
                print('escape pressed')
                debounce = 1
                print(debounce)
            #print(mousex , mousey)
            elif keyspressed[K_ESCAPE] == False and debounce > 0:
                print('debounce initiated,halt stopped')
                escape = 0
                debounce = 0
            else:
                debounce = 0
        
main()
    

updates:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

if len(models) > 0:
    models2 = (ctypes.c_float*len(models))(*models2)
    print(len(models),len(models2))
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glBufferSubData(GL_ARRAY_BUFFER, 0, len(models)*4, models2)

glDrawArrays(GL_TRIANGLES,0,len(models))
pygame.display.flip()
sleep(1/fps/2)

here is a video showing the issue:
https://drive.google.com/file/d/1m-16b7FkOxaAHrGsF0uSKqVN1bl5EjQQ/view?usp=sharing

vallentin
  • 23,478
  • 6
  • 59
  • 81
Tabeeb
  • 19
  • 4
  • Why is this question tagged with c++? – πάντα ῥεῖ Dec 26 '20 at 02:08
  • I didn't read through everything, but I think you answered it yourself. You receive "ghost triangles" due to your lack of `glClear`. So if you don't clear the color buffer or draw a fullscreen quad, then you'll be able to see all the previous content you didn't draw on top of. – vallentin Dec 26 '20 at 02:12
  • because most of the problem lies with opengl, i dont need help with the python side of thigs and most python users dont seem to have a lot of experience with opengl, i assumed that since opengl is a heavily c++ based api, and since pyopengl is simply opengl ported to c++, and i have a primarily opengl based problem, i assumed that the c++ forum would be able to help me out more then python im sorry if this annoyed you @πάνταῥεῖ – Tabeeb Dec 26 '20 at 02:13
  • ive been messing around @vallentin and i have been unsuccessful in fixing the issue this way, plenty of different artifacts show, the only instance that i can do glclear, is every time i switch buffers, but that happens after i send the bufferdata, and these ghost triangles, seem to persist even after clearing the buffer, which has me baffled – Tabeeb Dec 26 '20 at 02:14

2 Answers2

0

these ghost triangles, seem to persist even after clearing the buffer

It's because given your current code, then a single glClear is not enough. For instance consider the following:

If you have a single glClear then the subsequent frame is not cleared. Which is why you get the "flickering" of "ghost triangles".

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

if len(models) > 0:
    ...
else:
    glDrawArrays(GL_TRIANGLES,0,0)
    pygame.display.flip()
    sleep(1/fps/2)

    glDrawArrays(GL_TRIANGLES,0,0)
    pygame.display.flip()

Unless I'm misinterpreting something (as you provided quite a lot of code). Then you'll be able to clear and thus fix the flickering by simplifying it down to this:

Since if your len(models) is 0 then there's no need for the specialized else branch, as len(models) already would evaluate to 0.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

if len(models) > 0:
    models2 = (ctypes.c_float*len(models))(*models2)
    print(len(models))
    glBufferSubData(GL_ARRAY_BUFFER, 0, len(models)*4, models2)

glDrawArrays(GL_TRIANGLES,0,len(models))
pygame.display.flip()
sleep(1/fps/2)

You can also simplify your image loading, as well as obtaining width and height from the image, by doing this:

I'll reuse the names, so you know what is what.

image = Image.open('box.png').convert('RGBA')
width, height = image.size
rgbadata = np.fromstring(image.tobytes(), np.uint8)

Depending on the orientation of your image, you might need .transpose(Image.FLIP_TOP_BOTTOM), e.g.:

image = Image.open('box.png').transpose(Image.FLIP_TOP_BOTTOM).convert('RGBA')
width, height = image.size
rgbadata = np.fromstring(image.tobytes(), np.uint8)

You might also need a call to glPixelStorei(GL_UNPACK_ALIGNMENT, 1) once prior to any glTexImage2D calls. See this question/answer for more information.


Also, in your shader, note that texture2D(...) is deprecated as of OpenGL 3.3, and today it's just texture(...), i.e. colour = texture(texturergbadata, texcoordinate);


The glClear should fix the "flickering" and "ghost triangles". The rest was just some things I caught while reading through the code. So be clear, I'm not saying I combed over all the code.

vallentin
  • 23,478
  • 6
  • 59
  • 81
  • Wow thanks alot, this has been very helpful, when i saw that i got 2 downvotes (my fault sorry) i thought i was on my own. I appreciate it @vallentin . The reason why i have the else statement is because the function that converts the python list models2 inti a c type list that opengl needs, throws an error if it is len(models2) == 0 so i cant convert the list into a ctype array for this instance, if i was writing in c this wouldnt be a problem. Also with the depreciated texture setup is because there is not really a good way to get all the c variables needed for modernTextures in python – Tabeeb Dec 26 '20 at 13:34
  • i just tried the code, the triangle ghosting still exists, even after your code – Tabeeb Dec 26 '20 at 14:18
  • @Tabeeb I'm not entirely sure I follow why replacing the if/else with what I suggested is an issue. As if `len(models) == 0` then the `glDrawArrays()` is still called with `0`. However, the clear issue is still mainly related to you needing to clear twice, due to your 2 `flip()` calls in the `else` branch. But feel free to update your question with any new code and or an image of the artifacts. Also if you're unsure you likely got the two -2, it's likely due to your question containing a A LOT of code and a wall of text :) – vallentin Dec 26 '20 at 15:40
  • made things alot shorter and also added a video showing the issue – Tabeeb Dec 26 '20 at 16:27
  • @Tabeeb Just to be clear, the issue or "artifact" is the fact that the cube sticks in place sometimes, right? – vallentin Dec 26 '20 at 16:34
  • yes because i have code that moves all the cubes acording to my mouse position, and deletes any cube that i look at, however this part of code is quite buggy, so youll see in the video that at one point 3 cubes are spawned, i hover over one, this one gets deleted, then when i move my mouse, the third cube that should now be deleted, is just hovering in one static place on the screen. since everything else is moving relative to it, it kind of looks like iits moving but in reality its not – Tabeeb Dec 26 '20 at 16:44
  • @Tabeeb Reading through it once more, my guess is it's due to you having `physics`, `models`, `models1`, and `models2` which (more or less) represent the same data. You seem to be creating `models` from `models2` and then later do the opposite, etc. Most of what you're doing, you can do on the GPU in your shader. Take a look at the ["Transformations"](https://learnopengl.com/Getting-started/Transformations) tutorial on LearnOpenGL, i.e. adding a `uniform mat4 mvp` to your vertex shader. – vallentin Dec 26 '20 at 20:03
  • @Tabeeb Also careful doing [`for i in physics` while removing from `physics`](https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating) – vallentin Dec 26 '20 at 20:04
  • thanks for your comments, physics represents a plane equation with 2 xy cooordinates on that plane that mark out the edges of the cubes in 3d pspace, in order for the cubes to have a hitbox. ill try to put some stuff on the shader, but once again, the shortcommings of the python binding of opengl makes it very hard to use a large portion of opengl functions, as there is no way to make ctype floats in python – Tabeeb Dec 27 '20 at 21:53
  • what i think is likely the problem is that the vbo that i am sending data to needs to be cleared, i need a way to do this, because glclear, only clears the pixel data, but when i call gldrawarrays, the old delleted triangles keep being rerendered – Tabeeb Dec 27 '20 at 22:07
0

Guys i have finally fixed the issue, instead of messing with the vbo, all i changed was the gldrawarrays() function. basically i only set opengl to draw the number of triangles that account for all the cubes i want drawn.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    if len(models) > 0:
        models2 = (ctypes.c_float*len(models))(*models2)
        print(len(models))
        glBufferSubData(GL_ARRAY_BUFFER, 0, len(models)*4, models2)

    print(len(models))
    glDrawArrays(GL_TRIANGLES,0,18*len(models1))
    pygame.display.flip()
    sleep(1/fps/2)

the reason why it didnt work before was because of a logic error in my cube generator code, which was essentially making 2 duplicate cubes whenever i tried to spawn in a new one, since i forgot to dedent a line of code, so it was running in a for loop that loops twice.

for i in range(2):
    some code
    for x in cube:
        newcube.extend(x)

vs

for i in range(2):
    some code
for x in cube:
        newcube.extend(x)
    

the final arguement in a gldrawarray() call is only the number of verticies not the array length of the vertex array object that i want to render, so when i set it to 180 before it was drawing all of the deleted but not overwritten cubes in the vertex buffer. so now by fixing the duplication bug, and appropriately setting the gldrawarray() call the cube ghosting bug is gone. key takeaways:

  • Dont make the mistake of issueing a gldrawarray() call where the final arguement is anything other than the number of vertexes you want drawn,
  • logic errors suck.

huge thanks to @valentin and all other contributors you have been a huge help, i hope this thread is of some use to anyone reading. the graphics rendering and the coding community in general is awesome, keep up the good work!

Tabeeb
  • 19
  • 4