1

I have a 2-D numpy array that I have plotted in Pyopengl using Pyqt. Now I want to set The background of the plot greyscale such that when the line moves up or down, its background grey color changes intensity. I am attaching images for the behaviour I want.p

but all I am able to create till now is with white background like this enter image description here

the code that I have written is

import OpenGL.GL as gl
import OpenGL.arrays.vbo as glvbo
from PyQt5.Qt import *
import numpy as np
import sys


VS = '''
#version 450

layout(location = 0) in vec2 position;


uniform float right;
uniform float bottom;
uniform float left;
uniform float top;

void main() {
    const float far = 1.0;
    const float near = -1.0;

    mat4 testmat = mat4(
            vec4(2.0 / (right - left), 0, 0, 0),
            vec4(0, 2.0 / (top - bottom), 0, 0),
            vec4(0, 0, -2.0 / (far - near), 0),
            vec4(-(right + left) / (right - left), -(top + bottom) / (top - bottom), -(far + near) / (far - near), 1)
    );

    gl_Position = testmat * vec4(position.x, position.y, 0., 1.);

}
'''

FS = '''
#version 450
// Output variable of the fragment shader, which is a 4D vector containing the
// RGBA components of the pixel color.

uniform vec3 triangleColor;
out vec4 outColor;

void main()
{

    outColor = vec4(triangleColor, 1.0);

}


'''


def compile_vertex_shader(source):
    """Compile a vertex shader from source."""
    vertex_shader = gl.glCreateShader(gl.GL_VERTEX_SHADER)
    gl.glShaderSource(vertex_shader, source)
    gl.glCompileShader(vertex_shader)
    # check compilation error
    result = gl.glGetShaderiv(vertex_shader, gl.GL_COMPILE_STATUS)
    if not (result):
        raise RuntimeError(gl.glGetShaderInfoLog(vertex_shader))
    return vertex_shader


def compile_fragment_shader(source):
    """Compile a fragment shader from source."""
    fragment_shader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)
    gl.glShaderSource(fragment_shader, source)
    gl.glCompileShader(fragment_shader)

    result = gl.glGetShaderiv(fragment_shader, gl.GL_COMPILE_STATUS)
    if not (result):
        raise RuntimeError(gl.glGetShaderInfoLog(fragment_shader))
    return fragment_shader


def link_shader_program(vertex_shader, fragment_shader):
    """Create a shader program with from compiled shaders."""
    program = gl.glCreateProgram()
    gl.glAttachShader(program, vertex_shader)
    gl.glAttachShader(program, fragment_shader)
    gl.glLinkProgram(program)

    result = gl.glGetProgramiv(program, gl.GL_LINK_STATUS)
    if not (result):
        raise RuntimeError(gl.glGetProgramInfoLog(program))
    return program


class GLPlotWidget(QGLWidget):

    def __init__(self, *args):
        super(GLPlotWidget, self).__init__()
        self.width, self.height = 100, 100
        self.e = np.load('two.npy', mmap_mode='r')
        self.vbo = glvbo.VBO(self.e)
        self.count = self.vbo.shape[1]
        self.right, self.left, self.top, self.bottom = self.e[0, -1, 0].min(), self.e[0, 0, 0].max(), self.e[0, :,
                                                                                                     1].max(), self.e[-1,
                                                                                                               :,
                                                                                                               1].min()

        # self.data = np.zeros((10, 2))


        self.showMaximized()

    def initializeGL(self):

        vs = compile_vertex_shader(VS)
        fs = compile_fragment_shader(FS)
        self.shaders_program = link_shader_program(vs, fs)

    def ortho_view(self):

        right = gl.glGetUniformLocation(self.shaders_program, "right")
        gl.glUniform1f(right, self.right)

        left = gl.glGetUniformLocation(self.shaders_program, "left")
        gl.glUniform1f(left, self.left)

        top = gl.glGetUniformLocation(self.shaders_program, "top")
        gl.glUniform1f(top, self.top)

        bottom = gl.glGetUniformLocation(self.shaders_program, "bottom")
        gl.glUniform1f(bottom, self.bottom)


    def paintGL(self):
        self.resizeGL(self.width, self.height)
        gl.glDisable(gl.GL_LINE_STIPPLE)
        gl.glClearColor(1, 1,1, 0)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)
        self.vbo.bind()
        gl.glEnableVertexAttribArray(0)
        gl.glVertexAttribPointer(0, 2, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
        gl.glUseProgram(self.shaders_program)
        self.ortho_view()

        uni_color = gl.glGetUniformLocation(self.shaders_program, "triangleColor")

        for i in range(0, self.vbo.data.shape[0]):
            gl.glUniform3f(uni_color, 0, 0, 0)
            # gl.glLineWidth(1.8)
            gl.glDrawArrays(gl.GL_LINE_STRIP, i * self.count, self.count)
        self.vbo.unbind()

    def resizeGL(self, width, height):
        self.width, self.height = width, height
        gl.glViewport(0, 0, width, height)



def main():
    app = QApplication(sys.argv)
    editor = GLPlotWidget()
    editor.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Can anyone suggest me any method to solve this problem?

file link https://drive.google.com/file/d/1y6w35kuMguR1YczK7yMJpXU86T6qtGSv/view?usp=sharing

edit:- I tried to increase linewidth of points and set color to them but the result is not even close to satisfactory.enter image description here. and I am unable to understand how to pass color in triangles?

AKS
  • 142
  • 10
  • Your shader is working correctly, by applying color to the fragments of the rasterized lines you are drawing. For what you want to do you need to work on screen space, i.e, drawing a screen quad and applying shading based on screen coordinates, for example. You can blend together the line drawing and the screen space as a post process. – Nadir Feb 18 '20 at 08:04
  • @Nadir this is the idea I originally had in mind but I am unable to understand how to implement it in frag shader. One thing that I am trying right now is I have made a different shader for quad but I am not able to understand how to give its value using line data and it gets more uneasy for multiple lines as well – AKS Feb 18 '20 at 08:34
  • Can you please provide an example file (link to `'two.npy'`) – Rabbid76 Feb 18 '20 at 11:34
  • sure its https://drive.google.com/file/d/1y6w35kuMguR1YczK7yMJpXU86T6qtGSv/view?usp=sharing – AKS Feb 18 '20 at 11:48
  • @Rabbid76 I have create a quad using two triangles but I am unable to understand how to use position data in fragment shader to get desired color? – AKS Feb 19 '20 at 05:53

1 Answers1

2

Got it...I have rendered triangles and set color to get the desired result..

import OpenGL.GL as gl
import OpenGL.arrays.vbo as glvbo
from PyQt5.Qt import *
import numpy as np
import sys
import copy


VS = '''
#version 450

attribute vec2 position;
attribute vec3 a_Color;

out vec3 g_color;
void main() {

    gl_Position = vec4(position.x, position.y, 0., 1.);
    g_color = a_Color;
}
'''

FS = '''
#version 450
// Output variable of the fragment shader, which is a 4D vector containing the
// RGBA components of the pixel color.

in vec3 g_color;
out vec4 outColor;

void main()
{

    outColor = vec4(g_color, 1.0);

}


'''

VS1 = '''
#version 450

layout(location = 0) in vec2 position;

void main() {

    gl_Position = vec4(position.x, position.y, 0.0, 1.);

}
'''

FS1 = '''
#version 450
// Output variable of the fragment shader, which is a 4D vector containing the
// RGBA components of the pixel color.

uniform vec3 triangleColor;
out vec4 outColor;

void main()
{   

    outColor = vec4(triangleColor, 1.0);

}


'''


def compile_vertex_shader(source):
    """Compile a vertex shader from source."""
    vertex_shader = gl.glCreateShader(gl.GL_VERTEX_SHADER)
    gl.glShaderSource(vertex_shader, source)
    gl.glCompileShader(vertex_shader)
    # check compilation error
    result = gl.glGetShaderiv(vertex_shader, gl.GL_COMPILE_STATUS)
    if not (result):
        raise RuntimeError(gl.glGetShaderInfoLog(vertex_shader))
    return vertex_shader


def compile_fragment_shader(source):
    """Compile a fragment shader from source."""
    fragment_shader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)
    gl.glShaderSource(fragment_shader, source)
    gl.glCompileShader(fragment_shader)

    result = gl.glGetShaderiv(fragment_shader, gl.GL_COMPILE_STATUS)
    if not (result):
        raise RuntimeError(gl.glGetShaderInfoLog(fragment_shader))
    return fragment_shader


def link_shader_program(vertex_shader, fragment_shader):
    """Create a shader program with from compiled shaders."""
    program = gl.glCreateProgram()
    gl.glAttachShader(program, vertex_shader)
    gl.glAttachShader(program, fragment_shader)
    gl.glLinkProgram(program)

    result = gl.glGetProgramiv(program, gl.GL_LINK_STATUS)
    if not (result):
        raise RuntimeError(gl.glGetProgramInfoLog(program))
    return program


class GLPlotWidget(QGLWidget):

    def __init__(self, *args):
        super(GLPlotWidget, self).__init__()
        self.width, self.height = 100, 100
        self.we = np.load('two.npy', mmap_mode='r')
        self.e = copy.deepcopy(self.we[:, :, :])

        self.e[:, :, 1] = np.interp(self.e[:, :, 1], (self.e[:, :, 1].min(), self.e[:, :, 1].max()),
                                                                                  (-1, 1))

        self.e[:, :, 0] = np.interp(self.e[:, :, 0], (self.e[:, :, 0].min(), self.e[:, :, 0].max()),
                                                                                  (-1, +1))

        self.vbo = glvbo.VBO(self.e)
        self.count = self.vbo.shape[1]

        self.showMaximized()

    def initializeGL(self):

        self.greyscale_data()
        vs = compile_vertex_shader(VS1)
        fs = compile_fragment_shader(FS1)
        self.shaders_program_plot = link_shader_program(vs, fs)

    def greyscale_data(self):
        self.color = np.zeros((self.e.shape[1]*self.e.shape[0], 3), dtype=np.float32)
        for i in range(0, 24):
            a = self.e[i, :, 1].min()
            b = self.e[i, :, 1].max()
            c = np.interp(self.e[i, :, 1], (a, b), (0.15, 0.5))
            self.color[self.e.shape[1] * i:self.e.shape[1] * (i + 1), 0] = c
            self.color[self.e.shape[1] * i:self.e.shape[1] * (i + 1), 1] = c
            self.color[self.e.shape[1] * i:self.e.shape[1] * (i + 1), 2] = c

        self.elems = []
        b = self.e.shape[1]  # number of points per line
        a = self.e.shape[0]  # total number of arms

        for i in range(0, a - 1):
            for j in range(0, b - 1):
                self.elems += [j + b * i, j + b * i + 1, j + b * (i + 1)]
                self.elems += [j + b * (i + 1), j + b * (i + 1) + 1, j + b * i + 1]

        self.elems = np.array(self.elems, dtype=np.int32)
        # print(self.elems[0:100])
        vs = compile_vertex_shader(VS)
        fs = compile_fragment_shader(FS)
        self.shaders_program = link_shader_program(vs, fs)

        self.vertexbuffer = gl.glGenBuffers(1)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertexbuffer)
        gl.glBufferData(gl.GL_ARRAY_BUFFER, self.e, gl.GL_DYNAMIC_DRAW)

        self.elementbuffer = gl.glGenBuffers(1)
        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.elementbuffer)
        gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, self.elems, gl.GL_DYNAMIC_DRAW)

        self.colorbuffer = gl.glGenBuffers(1)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.colorbuffer)
        gl.glBufferData(gl.GL_ARRAY_BUFFER, self.color, gl.GL_DYNAMIC_DRAW)

    def ortho_view(self, i):

        right = gl.glGetUniformLocation(i, "right")
        gl.glUniform1f(right, self.right)

        left = gl.glGetUniformLocation(i, "left")
        gl.glUniform1f(left, self.left)

        top = gl.glGetUniformLocation(i, "top")
        gl.glUniform1f(top, self.top)

        bottom = gl.glGetUniformLocation(i, "bottom")
        gl.glUniform1f(bottom, self.bottom)

    def greyscale(self):
        gl.glUseProgram(self.shaders_program)

        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertexbuffer)

        stride = 0  # 3*self.e.itemsize
        offset = None  # ctypes.c_void_p(0)
        loc = gl.glGetAttribLocation(self.shaders_program, 'position')
        gl.glEnableVertexAttribArray(loc)
        gl.glVertexAttribPointer(loc, 2, gl.GL_FLOAT, False, stride, offset)

        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.elementbuffer)

        loc = gl.glGetAttribLocation(self.shaders_program, 'a_Color')
        gl.glEnableVertexAttribArray(loc)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.colorbuffer)
        gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)
        gl.glDrawElements(gl.GL_TRIANGLE_STRIP, self.elems.size, gl.GL_UNSIGNED_INT, None)

    def paintGL(self):
        self.resizeGL(self.width, self.height)
        gl.glClearColor(1, 1, 1, 0)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        gl.glEnable(gl.GL_DEPTH_TEST)

        self.vbo.bind()
        gl.glEnableVertexAttribArray(0)
        gl.glVertexAttribPointer(0, 2, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
        gl.glUseProgram(self.shaders_program_plot)

        uni_color = gl.glGetUniformLocation(self.shaders_program_plot, "triangleColor")

        for i in range(0, self.vbo.data.shape[0]):
            gl.glUniform3f(uni_color, 0, 0, 0)
            gl.glLineWidth(1)
            gl.glDrawArrays(gl.GL_LINE_STRIP, i * self.count, self.count)
        self.vbo.unbind()
        self.greyscale()
        gl.glUseProgram(0)

    def resizeGL(self, width, height):
        self.width, self.height = width, height
        gl.glViewport(0, 0, width, height)


def main():
    app = QApplication(sys.argv)
    editor = GLPlotWidget()
    editor.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
AKS
  • 142
  • 10