Search code examples
pythonopenglglslshaderpyopengl

Pyopengl: Creating cube with GLSL fills screen


When I try to draw a cube using the GLSL programmable pipeline of OpenGL I get a fully yellow screen, which is the color of the cube, and even if I used glTranslatef() and tried to zoom out by any value, the screen is just fully yellow. How can I zoom out so I can see the entire cube, not just a pure yellow screen?

Full replicable code:

import time
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GL.shaders import *
import numpy

width = 500
height = 500
vertices = [(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1), (-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)]
faces = [(4, 0, 3, 7), (1, 0, 4, 5), (0, 1, 2, 3), (1, 5, 6, 2), (3, 2, 6, 7), (5, 4, 7, 6)]

def draw_shapes():
    cube = []

    for i, face in enumerate(faces):
        for vertex in face:
            cube.append(vertices[vertex])
    cube = numpy.array(cube, dtype=numpy.float32)
    vertex_shader = """
        #version 140
        in vec4 position;
        void main(){
            gl_Position = position;
            
        }
    """

    frag_shader = """
        #version 140
        void main(){
            gl_FragColor = vec4(1.0f, 1.0f, 0.0f, 1.0f);
        }
        
    """

    shaders = compileProgram(compileShader(vertex_shader, GL_VERTEX_SHADER),
                             compileShader(frag_shader, GL_FRAGMENT_SHADER))
    VBO = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, VBO)
    glBufferData(GL_ARRAY_BUFFER, 192, cube, GL_STATIC_DRAW)
    position = glGetAttribLocation(shaders, "position")
    glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 0, None)
    glEnableVertexAttribArray(position)

    glUseProgram(shaders)

def showScreen():
    global width, height
    glClearColor(0, 0, 0, 1)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glDrawArrays(GL_TRIANGLES, 0, 24)
    draw_shapes()

    glEnable(GL_DEPTH_TEST)
    glEnable(GL_LIGHTING)
    glEnable(GL_LIGHT0)
    glEnable(GL_COLOR_MATERIAL)
    glLightfv(GL_LIGHT0, GL_POSITION, (0, 0, 0, 1))  # point light from the left, top, front
    glLightfv(GL_LIGHT0, GL_AMBIENT, (1, 1, 1, 1))


    glutSwapBuffers()

def reshapeWindow(x, y):
    global width, height
    width = x
    height = y
    print(x, y)
    glutReshapeWindow(width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, (width / height), 0.0001, 1000)
    glViewport(0, 0, width, height)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    glTranslatef(0, 0, -5)
    glRotatef(3, 1, 0, 0)

glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(500, 500)
wind = glutCreateWindow("OpenGL")
glutDisplayFunc(showScreen)
glutIdleFunc(showScreen)
glutReshapeFunc(reshapeWindow)
gluPerspective(45, (width / height), 0.0001, 100)

while True:   
    glutMainLoopEvent()
    glutPostRedisplay()
    time.sleep(0.01)

Solution

  • As already indicated in the comments by user253751 and derhass, the code mixes the fixed pipeline OpenGL approach with the programmable shader stages approach of modern OpengL, while it should be either one or the other. The legacy functions currently have no effect. It's probably best to start over using a book or tutorial on modern OpenGL, instead of converting a legacy project.

    A modern OpenGL version of this project will involve:

    • Defining the transformation matrices (model/view/projection) on the CPU.
    • Uploading one or more transformation matrices as uniforms to the GPU.
    • Uploading the light parameters as uniforms to the GPU.
    • Multiplying each vertex with the transformation matrix using the vertex shader.
    • Manually programming the lighting/shading calculations in the vertex shader (for Gouraud shading) or fragment shader (for Blinn-Phong/Phong shading).