Search code examples
pythonpython-3.xopenglpyqt5pyopengl

Problem making Sphere using PyOpenGL and PyQt5


I am trying to make sphere with OpenGL in Python but only single red dot appears in the center of screen. My code is as follow

def paintGL(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    try:
        glUseProgram(self.shader)
        glBindVertexArray(self.vao)
        glDrawArrays(GL_POINTS, 0, int(self.buff_size/3))
    finally:
        glBindVertexArray(0)
        glUseProgram(0)


def geometry(self):
    self.vao = glGenVertexArrays(1)
    self.vbo = glGenBuffers(1)

    lats = 180
    longs = 360
    r = 0.5

    data = []

    for i in range(lats):
        lat0 = np.pi * (-0.5 + (i - 1) / lats)
        z0 = np.sin(lat0)
        zr0 = np.cos(lat0)

        lat1 = np.pi * (-0.5 + i / lats)
        z1 = np.sin(lat1)
        zr1 = np.cos(lat1)

        for j in range(longs):
            lng = 2 * np.pi * (j - 1) / longs
            x_cord = np.cos(lng)
            y = np.sin(lng)

            data.extend([x_cord * zr0, y * zr0, z0])
            data.extend([r * x_cord * zr0, r * y * zr0, r * z0])
            data.extend([x_cord * zr1, y * zr1, z1])
            data.extend([r * x_cord * zr1, r * y * zr1, r * z1])

    data_input = np.array(data, dtype=np.float32)
    self.buff_size = len(data_input)
    vertex_size = int(data_input.nbytes / self.buff_size) * 3

    glUseProgram(self.shader)
    glBindVertexArray(self.vao)

    glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
    glBufferData(GL_ARRAY_BUFFER, data_input.nbytes, data_input, GL_DYNAMIC_DRAW)

    self.vertex_position = glGetAttribLocation(self.shader, 'position')
    glVertexAttribPointer(self.vertex_position, 3, GL_FLOAT, GL_FALSE, vertex_size, None)
    glEnableVertexAttribArray(self.vertex_position)

    glDisableVertexAttribArray(self.vertex_position)
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glBindVertexArray(0)
    glUseProgram(0)

Shader for my program is:

self.vertex_shader = shaders.compileShader(
    """
    #version 330
    in vec3 pos;
    uniform mat4 movement;
    void main() 
    { 
        gl_Position = movement * vec4(pos, 1.0);
    }
    """
    , GL_VERTEX_SHADER
)



self.fragment_shader = shaders.compileShader(
    """
    #version 330
    out vec4 gl_FragColor;
    void main() 
    {
        gl_FragColor = vec4(1.0,0.0,0.5,1.0);
    }
    """
    , GL_FRAGMENT_SHADER
)


self.shader = shaders.compileProgram(self.vertex_shader, self.fragment_shader)

I don't know where I'm having issue. I also tried using indices with GL_ELEMENT_ARRAY_BUFFER and glDrawElements as GL_QUAD_STRIP but no success. If you know how I can fix this then please share. Thanks


Solution

  • The name of the vertex attribute is pos:

    in vec3 pos;
    

    So the argument to glGetAttribLocation has to be 'pos' rather than 'position':

    self.vertex_position = glGetAttribLocation(self.shader, 'position')

    self.vertex_position = glGetAttribLocation(self.shader, 'pos')
    

    If the specification and enable state is stored in the Vertex Array Object. If you glDisableVertexAttribArray(self.vertex_position), this state is stored to the self.vao. Remove it:

    glBindVertexArray(self.vao)
    
    glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
    glBufferData(GL_ARRAY_BUFFER, data_input.nbytes, data_input, GL_DYNAMIC_DRAW)
    
    self.vertex_position = glGetAttribLocation(self.shader, 'pos')
    glVertexAttribPointer(self.vertex_position, 3, GL_FLOAT, GL_FALSE, vertex_size, None)
    glEnableVertexAttribArray(self.vertex_position)
    
    #glDisableVertexAttribArray(self.vertex_position) # <--- DELETE
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glBindVertexArray(0)
    

    A matrix unfiorm be default is initialized with all fields 0.

    uniform mat4 movement;
    

    If you do not need the uniform, then remove it. Or initialize it by the Identity matrix.

    glUseProgram(self.shader)
    
    ident4x4 = np.identity(4, np.float32)
    self.movement_loc = glGetUniformLocation(self.shader, 'movement')
    glUniformMatrix4fv(self.movement_loc, 1, GL_FALSE, ident4x4)
    

    Alternatively you can use a view and perspective projection matrix (self.width and self.height are the width and height of the window):

    # projection matrix
    aspect, ta, near, far = self.width/self.height, np.tan(np.radians(90.0) / 2), 0.1, 10
    proj = np.matrix(((1/ta/aspect, 0, 0, 0), (0, 1/ta, 0, 0), (0, 0, -(far+near)/(far-near), -1), (0, 0, -2*far*near/(far-near), 0)), np.float32)
    
    # view matrix
    view = np.matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, -3, 1)), np.float32)
    
    glUniformMatrix4fv(self.movement_loc, 1, GL_FALSE, view * proj)