0

Question: What is wrong with this simple OpenGL/GLSL program? It is supposed to produce a quad with a red-green color gradient. But I just get a black window, and no error messages.

import numpy
from OpenGL.GL import *
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtOpenGL import *

_vertexShaderSource = (
    'attribute vec2 a_position;'
    'attribute vec3 a_color;'
    'varying vec3 v_color;'
    ''
    'void main()'
    '{'
    '   gl_Position = vec4(a_position, 0.0, 1.0);'
    '   v_color = a_color;'
    '}'
)

_fragmentShaderSource = (
    'varying vec3 v_color;'
    ''
    'void main()'
    '{'
    '    gl_FragColor = vec4(v_color, 1.0);'
    '}'
)

_POSITION = 0
_COLOR = 1

class HelloWidget(QGLWidget):

    def __init__(self):
        QGLWidget.__init__(self)

    def initializeGL(self):
        self._shaderProgram = QGLShaderProgram(self.context())
        self._shaderProgram.addShaderFromSourceCode(QGLShader.Vertex, _vertexShaderSource)
        self._shaderProgram.addShaderFromSourceCode(QGLShader.Fragment, _fragmentShaderSource)
        self._shaderProgram.link()
        self._shaderProgram.bind()

        glBindAttribLocation(self._shaderProgram.programId(), _POSITION, 'a_position')
        glBindAttribLocation(self._shaderProgram.programId(), _COLOR, 'a_color')

    def paintGL(self):
        glViewport(0, 0, self.width(), self.height())
        glClear(GL_COLOR_BUFFER_BIT)

        position = numpy.ndarray([-0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5], numpy.float32)
        glVertexAttribPointer(_POSITION, 2, GL_FLOAT, False, 0, position)
        glEnableVertexAttribArray(_POSITION)

        color = numpy.ndarray([1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0], numpy.float32)
        glVertexAttribPointer(_COLOR, 3, GL_FLOAT, False, 0, color)
        glEnableVertexAttribArray(_COLOR)

        glDrawArrays(GL_QUADS, 0, 4)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    w = HelloWidget()
    w.show()
    app.exec_()

Answer: There are two bugs in the code.

  1. The calls to glBindAttribLocation must come before self._shaderProgram.link(), as pointed out by Brett Hale.

  2. numpy.ndarray should be replaced by numpy.array.

If you fix these two problems, then the code works as expected.

Johan Råde
  • 20,480
  • 21
  • 73
  • 110

3 Answers3

2

From the man page for glBindAttribLocation:

Any attribute binding that occurs after the program object has been linked will not take effect until the next time the program object is linked.

Associate the attribute indices first, then link the program.

Alternatively, assign a value to _POSITION, _COLOR, etc., using glGetAttribLocation.


As @KillianDS mentions, you can specify the index (location) in the shader, provided you have GL 3.3 or above. This is a source of frustration on OS X, which only guarantees GL 3.2.


It might also be worth pointing out that GL_QUADS is not supported in the core profile, so it may be necessary to work around that, e.g., with 2 triangles.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Brett Hale
  • 21,653
  • 2
  • 61
  • 90
  • 1
    or even better, just use fixed locations with `layout(location = i)`, in the end this usually saves you a lot of trouble. – KillianDS Dec 01 '12 at 13:12
1

I'd rather have posted a comment, but it got long. Hopefully this helps you:

Since your using the attribute keyword (a compatibility keyword) in your vertex shader, could you clarify what version of OpenGL and GLSL you are using? I don't see anything referring to the version in your code; did I miss it? It may help a bit to understand what the issue is.

The layout formatting would certainly simplify things if you have it available. Also, your shader(s) requires it's #version to be specified (at least in the latest spec, though I think it holds for all versions). Not necessary for 1.10 (though still supported), which the OP is using/intends to use.

Hydronium
  • 793
  • 1
  • 11
  • 28
  • I'm testing the code on a machine that runs OpenGL 3.3. We will probably target OpenGL 2.0. (Currently all our products are written with OpenGL 1.5. Many of our customers have old Intel graphics cards. But we are thinking of moving to OpenGL 2.0.) – Johan Råde Dec 01 '12 at 22:18
  • Well, assuming OpenGL 2.0, shader version 1.1, you won't have `layout` available. I don't have much time at the posting of this comment, but i'll double check if there's anything about your code that conflicts with the [OpenGL 2.0 spec](http://www.opengl.org/registry/doc/glspec20.20041022.pdf) and the [GLSL 1.1 spec](http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.10.59.pdf) in a few hours. Also, you don't _need_ the `#version 110` directive for shader version 1.10, as it is the default. Given this, I don't see any issues at the moment. – Hydronium Dec 02 '12 at 14:57
  • @user763305 Also, have you thrown in some `glGetError` calls, or have you checked your shader/program for compiling/linking errors? – Hydronium Dec 02 '12 at 18:00
  • QtOpenGL and PyOpenGl check for errors automatically. There is no need to call `glGetError`. – Johan Råde Dec 02 '12 at 18:35
  • What do you mean by "It seems like you're mixing modern and legacy"? I've tried to keep everything OpenGL 2.0 / GLSL 1.10. – Johan Råde Dec 02 '12 at 18:58
  • That was my own misinterpretation, i'll edit that out. When looking through other people's comments they were mentioning `layout` and other such things, so I thought you were using those or were using things from a newer version of OpenGL. – Hydronium Dec 02 '12 at 19:48
0

I'm not familiar with Qt under Python but I'm pretty sure that you need to set your shader program as active using glUseProgram.

Johan Råde
  • 20,480
  • 21
  • 73
  • 110
  • The line `self._shaderProgram.bind()` activates the shader program. See http://doc.qt.digia.com/qt/qglshaderprogram.html#bind. – Johan Råde Dec 01 '12 at 11:53