I have a PyQt OpenGL widget which renders a square which fills the widget. The subclasses define the fragment shader.
The Vertex Shader is
#version 330in vec2 v_position;out vec2 f_position;void main() { f_position = v_position; gl_Position = vec4(v_position, 0.0, 1.0);}
The data is defined like this (some lines omitted, full source below)
vertices = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0] data = numpy.array(vertices, dtype=numpy.float32) vbo.allocate(data, data.nbytes) attr_loc = shader.attributeLocation("v_position") shader.enableAttributeArray(attr_loc) shader.setAttributeBuffer(attr_loc, GL_FLOAT, 0, 2, 2 * sizeof(GLfloat))
and renders using
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
One subclass uses a pixel shader which draws stuff in a circle, but the circle is partly cut off: it doesn't fit the window perfectly on all sides.
To debug this I added red shading for the x component of f_position
fragColor = vec4(pow(abs(f_position.x),12.0), ... // rest doesn't matter
which reveals the problem:
The red shading on the right-hand edge does not reach its full value. This means that at the right-most pixel f_position.x
is not reaching 1.0
.
Why does the widget not fully contain a square which has f_position
ranging from -1 to +1 in both directions?
Full source:
class SquareGLWidget(QOpenGLWidget): def __init__(self, parent=None): super().__init__(parent) self.clear_colour = [0.5, 0.5, 0.5, 1.0] self.shader = None def get_fragment_shader(self):''' :return: text Program must take `in vec2 f_position` as its only input.''' raise Exception ("Must implement in subclass") def assign_uniforms(self, shader):''' :param shader: will be bound already :return: None''' raise Exception ("Must implement in subclass") def initializeGL(self): self.shader = QOpenGLShaderProgram() if not self.shader.addShaderFromSourceCode(QOpenGLShader.Vertex, SquareGLWidget_VERTEX_SHADER): raise Exception ("Could not load SquareGLWidget_VERTEX_SHADER: " + self.shader.log()) if not self.shader.addShaderFromSourceCode(QOpenGLShader.Fragment, self.get_fragment_shader()): raise Exception ("Could not load get_fragment_shader(): " + self.shader.log()) if not self.shader.link(): raise Exception ("Shader program linking failed: " + self.shader.log()) self.vbo = QOpenGLBuffer() if not self.vbo.create(): raise Exception ("Could not create VBO") self.vbo.bind() self.vao = QOpenGLVertexArrayObject() if not self.vao.create(): raise Exception ("Could not create VAO: " + self.shader.log()) self.vao.bind() vertices = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0] data = numpy.array(vertices, dtype=numpy.float32) self.vbo.allocate(data, data.nbytes) self.shader.bind() attr_loc = self.shader.attributeLocation("v_position") self.shader.enableAttributeArray(attr_loc) self.shader.setAttributeBuffer(attr_loc, GL_FLOAT, 0, 2, 2 * sizeof(GLfloat)) self.shader.release() self.vbo.release() self.vao.release() def paintGL(self): if not QOpenGLContext.currentContext(): raise Exception ("No currentContext") if not self.context(): raise Exception ("No self.context") if not self.context().isValid(): raise Exception("Invalid self.context") # If we exit with an error before rendering, show magenta. glClearColor(1.0, 0.0, 1.0, 1.0) glClear(GL_COLOR_BUFFER_BIT) if not self.shader.isLinked(): print ("paintGL: shader is not linked") return self.shader.bind() glEnable(GL_DEPTH_TEST) glClearColor(*self.clear_colour) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) self.assign_uniforms(self.shader) self.vao.bind() glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) self.vao.release() self.shader.release() def resizeGL(self, w, h): side = min(w, h) x = (w - side) // 2 y = (h - side) // 2 glViewport(x, y, side, side)