Search code examples
pythonopenglglslshaderpyopengl

Can't get glVertexAttribPointer to render colour from shaders


I am trying to render 3 points with different colours, using a single VBO and shaders. I am passing the location and colour values to a single VBO:

vertices = np.array([[0, 0.1, 0.6, 1, 1, 0], [0.3, 0.1, 0, 0, 1, 1], [0.8, 0.5, 0, 0, 1, 0]], dtype='f')

which I'm then trying to read into my shaders. I add alpha in the fragment shader. I'm using glVertexAttribPointer to differentiate between my x,y,z and my r,g,b,a. I am able to render my x,y,z:

glEnableVertexAttribArray(glGetAttribLocation(shader_program, "position"))
glVertexAttribPointer(glGetAttribLocation(shader_program, "position"), 3, GL_FLOAT, False, 6*4, None)

I am unable to load my r,g,b,a by using glVertexAttribPointer as I understand it from the docs:

glEnableVertexAttribArray(glGetAttribLocation(shader_program, "color"))
glVertexAttribPointer(glGetAttribLocation(shader_program, "color"), 3, GL_FLOAT, False, 6*4, 3*4)

This does not render any visible points. My shaders are definitely working, as when I change the byte offset of my Pointer to None:

glEnableVertexAttribArray(glGetAttribLocation(shader_program, "color"))    glVertexAttribPointer(glGetAttribLocation(shader_program, "color"), 3, GL_FLOAT, False, 6*4, None)

my points are coloured according to my normalized x,y,z coordinates (i.e glVertexAttribPointer interprets my x coordinate as r, y coordinate as g, z coordinate as b), which makes sense as the byte offset is 0. Surely the byte offset should be 3*4? My two thoughts were that I've either

  1. misunderstood the final argument of glVertexAttribPointer or
  2. somehow made the vertices transparent

but I've tried changing the ordering of my colours, adding alpha to the original dataset etc with no success. Any help appreciated.

Full code:

from OpenGL.arrays import vbo
import pygame
import numpy as np
from OpenGL.GL import *
from OpenGL.GL.shaders import *

def getFileContent(file):
    content = open(file, 'r').read()
    return content

def run():
    pygame.init()
    screen = pygame.display.set_mode((800,600), pygame.OPENGL | pygame.DOUBLEBUF)

    vertices = np.array([[0, 0.1, 0.6, 1, 1, 0], [0.3, 0.1, 0, 0, 1, 1], [0.8, 0.5, 0, 0, 1, 0]], dtype='f')
    vertexPositions = vbo.VBO(vertices)
    indices = np.array([[0,1,2]], dtype=np.int32)
    indexPositions = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)

    shader_program = glCreateProgram()
    vertex_source = compileShader(getFileContent("testing_vert_shader.vert"), GL_VERTEX_SHADER)
    fragment_source = compileShader(getFileContent("testing_frag_shader.frag"), GL_FRAGMENT_SHADER)
    glAttachShader(shader_program, vertex_source)
    glAttachShader(shader_program, fragment_source)
    glBindAttribLocation(shader_program, 0, "position")
    glBindAttribLocation(shader_program, 1, "color")
    glLinkProgram(shader_program)

    print(glGetProgramiv(shader_program, GL_LINK_STATUS))  # shader linked == 1

    while True:
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glUseProgram(shader_program)

        indexPositions.bind()
        vertexPositions.bind()

        glEnableVertexAttribArray(glGetAttribLocation(shader_program, "position"))
        glVertexAttribPointer(glGetAttribLocation(shader_program, "position"), 3, GL_FLOAT, False, 6*4, None)

        # red vertices rendered if these two lines are commented out
        glEnableVertexAttribArray(glGetAttribLocation(shader_program, "color"))
        glVertexAttribPointer(glGetAttribLocation(shader_program, "color"), 3, GL_FLOAT, False, 6*4, None)  # changing last argument of glVertexAttribPointer to None results in x,y,z being read as colours

        glDrawElements(GL_POINTS, 3, GL_UNSIGNED_INT, None)

        # Show the screen
        pygame.display.flip()

run()

Vertex shader:

#version 330 core
in vec3 position;
in vec3 color;

out vec3 vColor;

void main()
{
    gl_Position = vec4(position, 1.0);
    vColor = vec3(color);
}

Fragment shader:

#version 330 core
in vec3 vColor;

out vec4 outcolor;

void main()
{
    outcolor = vec4(vColor, 1.0f);
}

Solution

  • If a named buffer object is bound, then the 5th parameter of glVertexAttribPointer is treated as a byte offset into the buffer object's data store. However, the type of the parameter is still a pointer (c_void_p).

    Hence the offset must be cast with c_void_p. If the offset is 0, the 5th parameter can either be None or c_void_p(0):

    loc_col = glGetAttribLocation(shader_program, "color")
    glVertexAttribPointer(loc_col, 3, GL_FLOAT, False, 6*4, ctypes.c_void_p(3*4))