BGL simple triangle,what am I doing wrong?

ugh! I’ve been at this for days.What am I doing wrong?
If I hardcode the vertex positions in the vertex shader it works,if I pass it via VBO it doesn’t.
@GottfriedHofmann was kind enough to share his code for a basic triangle,and I wrote almost identical code and still I just get a one vertex showing in the bottom left corner of the viewport.

import bpy
from bgl import *

draw_handler = None

print(f'{" Program Compile Start ":=^40}')
vertex_shader_source = """
#version 330

layout(location = 0) in vec3 vPos;

void main()
{
    gl_Position = vec4(vPos.xy,0,1);
}
"""
fragment_shader_source = """
#version 330

out vec4 FragColor;

void main()
{
    FragColor = vec4(0,1,0,1);
}
"""
statusVert = Buffer(GL_INT, [1])
statusFrag = Buffer(GL_INT, [1])
statusProgram = Buffer(GL_INT, [1])
program    = glCreateProgram()
shaderVert = glCreateShader(GL_VERTEX_SHADER)
shaderFrag = glCreateShader(GL_FRAGMENT_SHADER)

glShaderSource(shaderVert,vertex_shader_source)
glShaderSource(shaderFrag,fragment_shader_source)

glCompileShader(shaderVert)
glCompileShader(shaderFrag)
glGetShaderiv(shaderVert,GL_COMPILE_STATUS,statusVert)
glGetShaderiv(shaderFrag,GL_COMPILE_STATUS,statusFrag)

if   statusVert[0] == GL_TRUE   : print('Vertex Shader compilation successfull')
if statusFrag[0]   == GL_TRUE   : print('Fragment Shader compilation successfull')
if statusVert[0]   == GL_FALSE  : print('Vertex Shader compilation failed')
if statusFrag[0]   == GL_FALSE  : print('Fragment Shader compilation failed')


glAttachShader(program,shaderVert)
glAttachShader(program,shaderFrag)
glLinkProgram(program)

glGetProgramiv(program, GL_LINK_STATUS, statusProgram)
if   statusProgram[0] == GL_TRUE  : print('Program Linking Successfull')
elif statusProgram[0] == GL_FALSE : print('Program Linking Failed')
else: print('Program Linking unknown')
print(f'{" Program Compile end ":=^40}')

# Data

vertex_pos = [(-0.52,-0.5, 0),
              (0.5,-0.52, 0),
              (0.02, 0.5, 0)]
vertex_buff = Buffer(GL_FLOAT,(len(vertex_pos),3), vertex_pos)
vB_size = 9*4

VAO,VBO = Buffer(GL_INT,1),Buffer(GL_INT,1)
glGenVertexArrays(1, VAO)
glGenBuffers(1, VBO)
print(f'VAO:{VAO[0]} || VBO:{VBO[0]}')

glBindVertexArray(VAO[0])
glBindBuffer(GL_ARRAY_BUFFER, VBO[0])
glBufferData(GL_ARRAY_BUFFER, vB_size, vertex_buff, GL_STATIC_DRAW)
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,12,None)
glEnableVertexAttribArray(0)

glBindBuffer(GL_ARRAY_BUFFER,0)
glBindVertexArray(0)

def draw():
    glUseProgram(program)
    glBindVertexArray(VAO[0])

    glPointSize(30)
    glDrawArrays(GL_POINTS,0,3)
    glDrawArrays(GL_LINE_LOOP,0,3)

    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glBindVertexArray(0)
    glUseProgram(0)



draw_handler = bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
def in_5_seconds():
    print(f'{" Ending ":=^40}')
    glDeleteBuffers(1,VBO)
    glDeleteVertexArrays(1,VAO)
    glDeleteShader(shaderVert)
    glDeleteShader(shaderFrag)
    glDeleteProgram(program)
    bpy.types.SpaceView3D.draw_handler_remove(draw_handler,'WINDOW')
bpy.app.timers.register(in_5_seconds, first_interval=5)

No one?
I recently made an error when I recompiled pyAssimp where I selected the check mark about double precision.And every model I loaded gave me this weird(vertices everywhere) result. I have tried the numpy.float32 and didn’t help with pyAssimp as didn’t help here.
It was when I recompiled pyAssimp without the double precision that I got it working.
Made me thinking that perhaps the data is not converted in the proper type of precision for the buffer.Which is odd because using @GottfriedHofmann 's file he gave me works fine.And I did the same.

Ok,so I did some tests with glGetBufferSubData:

getting the data from the GL_ARRAY_BUFFER after I give the data to it, I get the everything looking fine:
[-0.52 -0.5 0. 0.5 -0.52 0. 0.02 0.5 0. ]
getting the data from the GL_ARRAY_BUFFER in the draw loop after assigning the VBO I get everything looking fine as well.
If I get the data from the GL_ARRAY_BUFFER in the draw loop without assigning the VBO i get:
[1514. 25. 1514. 0. 0. 0. 0. 25. 404.]
Considering I don’t have a projection setup,this explains why I don’t see anything except a vertex in lower left corner(the second vertex position is 0,0,0).
It’s odd…the VBO holds the correct data …but the VAO does not hold the correct VBO?

Hi okuma_10

It would be nice if you explain a bit what you want to achieve (visually).

Jeroen.

As I explained in blender.chat, I’m trying to draw a simple triangle using OpenGL via blender’s OpenGL python wrapper.
Why? - Why not.
Why not the gpu module? - because I want to do it “directly”.
And here is the visual explanation you requested.

Considering that the code is identical,except the pyOpenGL and BGL differences with one using ctypes and other bgl.Buffer(), it’s obviously not an issue with drivers,or hardware. But I think it’s an issue with bgl.
Again…it’s odd since GottfriedHofmann’s code works fine, and that’s pretty much the same code as mine.
Also as I said…hard coding the vertex positions in to the vertex shader works. Yet when I pass the same positions via the buffer and the VBO…it does not work.

I’ve update the code above so it does not use numpy as you had issues with it.
Also here is the same code but with the vertex positions hard coded in to the vertex shader:

import bpy
from bgl import *

draw_handler = None

# Shader Compilation
print(f'{" Program Compile Start ":=^40}')

vertex_shader_source = """
#version 330 core
layout(location = 0) in vec3 inColor;
const vec4 vertices[3] = vec4[3](vec4(0.5, -0.5, 0.0, 1.0),
                                 vec4(-0.5, -0.5, 0.0, 1.0),
                                 vec4(0, 0.5, 0.0, 1.0));
out vec3 Color;
void main()
{
    Color = inColor;
    gl_Position = vertices[gl_VertexID];
}
"""
fragment_shader_source = """
#version 330 core
in vec3 Color;
out vec4 FragColor;

void main()
{
    FragColor = vec4(Color,1);
}
"""

statusVert    = Buffer(GL_INT, 1)
statusFrag    = Buffer(GL_INT, 1)
statusProgram = Buffer(GL_INT, 1)
program       = glCreateProgram()
shaderVert    = glCreateShader(GL_VERTEX_SHADER)
shaderFrag    = glCreateShader(GL_FRAGMENT_SHADER)

glShaderSource(shaderVert, vertex_shader_source)
glShaderSource(shaderFrag, fragment_shader_source)

glCompileShader(shaderVert)
glCompileShader(shaderFrag)
glGetShaderiv(shaderVert, GL_COMPILE_STATUS, statusVert)
glGetShaderiv(shaderFrag, GL_COMPILE_STATUS, statusFrag)
if statusVert[0]   == GL_TRUE   : print('Vertex Shader compilation successfull')
if statusFrag[0]   == GL_TRUE   : print('Fragment Shader compilation successfull')
if statusVert[0]   == GL_FALSE  : print('Vertex Shader compilation failed')
if statusFrag[0]   == GL_FALSE  : print('Fragment Shader compilation failed')


glAttachShader(program, shaderVert)
glAttachShader(program, shaderFrag)
glLinkProgram(program)
glGetProgramiv(program, GL_LINK_STATUS, statusProgram)
if   statusProgram[0] == GL_TRUE  : print('Program Linking Successfull')
elif statusProgram[0] == GL_FALSE : print('Program Linking Failed')
else: print('Program Linking unknown')
print(f'{" Program Compile end ":=^40}')
# Shader Compilation End


# Data Setup
vertex_pos = [(-0.52,-0.5, 0),
              (0.5,-0.52, 0),
              (0.0, 0.5, 0)]
vertex_buff = Buffer(GL_FLOAT, (len(vertex_pos),3), vertex_pos)
vB_size = 9*4

VAO,VBO = Buffer(GL_INT,1), Buffer(GL_INT,1)
glGenVertexArrays(1, VAO)
glGenBuffers(1, VBO)
print(f'VAO:{VAO[0]} || VBO:{VBO[0]}')

glBindVertexArray(VAO[0])
glBindBuffer(GL_ARRAY_BUFFER, VBO[0])
glBufferData(GL_ARRAY_BUFFER, vB_size, vertex_buff, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0)
glEnableVertexAttribArray(0)

glBindVertexArray(0)
# Data Setup End

# Draw Function
def draw():
    glUseProgram(program)
    glBindVertexArray(VAO[0])

    glPointSize(30)
    glDrawArrays(GL_POINTS,0, 3)
    glLineWidth(10)
    glDrawArrays(GL_LINE_LOOP, 0, 3)

    # glBindBuffer(GL_ARRAY_BUFFER, 0)
    glBindVertexArray(0)
    glUseProgram(0)
# Draw Function End

# Add Draw Handler
draw_handler = bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')

# Remove and Cleanup after 5 sec.
def in_5_seconds():
    print(f'{" Ending ":=^40}')
    glDeleteBuffers(1,VBO)
    glDeleteVertexArrays(1,VAO)
    glDeleteShader(shaderVert)
    glDeleteShader(shaderFrag)
    glDeleteProgram(program)
    bpy.types.SpaceView3D.draw_handler_remove(draw_handler,'WINDOW')

# Add Remove function
bpy.app.timers.register(in_5_seconds, first_interval=5)

You can see that now the colors are off(I’ll add an image as well in a bit). But because I’m directly feeding the GPU with the data via the vertex shader,I get my triangle.But the data that has been passed by the gl.Buffer() (now going to the color of the fragment shader) is again… not what I had passed to the VBO.

Edit: Something to add .While trying to get the code working on my AMD GPU I saw an old stackoverflow thread that some guy had to use the glGetAttribLocation() function to pass his data properly. So I tested it and found something odd. When I have different location to layou(location = ?) it changed result. Like locations from 0-4 gave no result on my AMD GPU. Location 5 ,gave me the proper result but theglPointSize() was not working. If I coded the gl_PointSize in the shader I had exactly what we see on the image above on my AMD GPU(the image was taken on my laptop with an intel/Nvidia GPU).
All cool and fine…but when I tested that same code with location = 5 on the laptop, blender crashed. And I think almost all locations except 0 crashed blender.

As explained in T65208. The draw callback runs in a different OpenGL context. And the VAO only works in the context in which it is created. So you should call these lines inside a drawing calback:

glGenVertexArrays(1, VAO)

glBindVertexArray(VAO[0])
glBindBuffer(GL_ARRAY_BUFFER, VBO[0])
glBufferData(GL_ARRAY_BUFFER, vB_size, vertex_buff, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0)
glEnableVertexAttribArray(0)

glBindVertexArray(0)

There is more than one way to solve this problem.
One way is:

def draw():
    if not vao_is_initialized:
        vao_is_initialized = True
        (create and init vao...)
    (...)

Or something like:

def daw_initialize():
    (initialize...)
    bpy.types.SpaceView3D.draw_handler_remove(daw_initialize,'WINDOW')
    global draw_handler
    draw_handler = bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
2 Likes

ah I see…
so basically:
1 - either to create a switch in the draw function,for the initialization part,
2 - or add a handler that creates the VAO,VBO,and links the data and then remove it.Once it’s been created in the proper context.
Right?

Only the VAO needs to be created in the context that will be used. (VBO is shared between contexts).

worked perfectly
the only issue now is that I have to figure out a way to clean up the VAO and VBO from OpenGL when there is no draw handler.

The VBO you can delete in any context. But the VAO has to be deleted in offscreen context. That is, in the callback.

Well after more than a month of this issue I finally managed to do it.
Thanks to @mano-wii for explaining what I was doing wrong in relation to how blender works.

2019-06-11_00h25_19
the result I was after! :slight_smile:

1 Like