Issue
I want to make cyclic rotation of these 2-D lines such that when mouse is scrolled the lines move in upward or downward direction. For ex:- if mouse wheel is scrolled upward line at the top moves to the bottom and all lines move upward. I am able to do this by changing data but it is a slow process. Is there any way to do this using shader in OpenGl.
code:-
import OpenGL.GL as gl
import OpenGL.arrays.vbo as glvbo
from PyQt5.Qt import *
import numpy as np
import sys
import copy
VS1 = '''
#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.);
}
'''
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.w = copy.deepcopy(self.we[:, :, :])
for i in range(0, 24):
self.w[i, :, 1] = self.e[i, :, 1] - np.array(9999 * i)
# 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.right, self.left, self.top, self.bottom = self.e[0, -1, 0], self.e[
0, 0, 0], self.e[0, :, 1].max(), self.e[-1, :, 1].min()
self.vbo = glvbo.VBO(self.e)
self.count = self.vbo.shape[1]
self.scroll = 0
self.number_of_arm = 24
self.sensor_list_const = np.array(range(0, self.number_of_arm))
self.sensor_list_roll = copy.deepcopy(self.sensor_list_const)
self.showMaximized()
def initializeGL(self):
vs = compile_vertex_shader(VS1)
fs = compile_fragment_shader(FS1)
self.shaders_program_plot = link_shader_program(vs, fs)
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 paintGL(self):
self.resizeGL(self.width, self.height)
gl.glClearColor(0.75, 0.75, 0.75, 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)
self.ortho_view(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 reset_vbo(self):
self.wex = copy.deepcopy(self.w)
for i, j in zip(self.sensor_list_roll, self.sensor_list_const):
self.wex[j, :, 1] = self.w[j, :, 1] + np.array(9999 * (self.number_of_arm - i))
self.vbo.set_array(self.wex)
self.right, self.left, self.top, self.bottom = self.wex[0, -1, 0], self.wex[
0, 0, 0], self.wex[:, :, 1].max(), self.wex[:, :, 1].min()
self.update()
def resizeGL(self, width, height):
self.width, self.height = width, height
gl.glViewport(0, 0, width, height)
def wheelEvent(self, *args, **kwargs):
event = args[0]
# print(event.angleDelta().y())
if event.angleDelta().y() > 0:
self.scroll = self.scroll - 1
else:
self.scroll = self.scroll + 1
if self.scroll > self.number_of_arm - 1 or self.scroll < -(self.number_of_arm - 1):
self.scroll = 0
self.sensor_list_roll = np.roll(self.sensor_list_const, self.scroll)
# self.patch_move(event)
self.reset_vbo()
def main():
app = QApplication(sys.argv)
editor = GLPlotWidget()
editor.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
data file :- https://drive.google.com/file/d/1y6w35kuMguR1YczK7yMJpXU86T6qtGSv/view?usp=sharing
Solution
You can use the uniforms top
and bottom
to scroll the view. You have to set the individually for each line.
Compute the scroll factor self.scroll
dependent on the y scale of the projection:
class GLPlotWidget(QGLWidget):
# [...]
def wheelEvent(self, *args, **kwargs):
event = args[0]
scroll_scale = 0.01
size = self.top - self.bottom
if event.angleDelta().y() > 0:
self.scroll = self.scroll - size * scroll_scale
if self.scroll < 0:
self.scroll += size
else:
self.scroll = self.scroll + size * scroll_scale
if self.scroll > size:
self.scroll -= size
Further more you have to now the y range of each line. Compute the minimum and maximum for each line and store it to a list (self.linerange
):
class GLPlotWidget(QGLWidget):
def __init__(self, *args):
# [...]
self.linerange = [(self.e[li, :, 1].max(), self.e[-li, :, 1].min()) for li in range(self.vbo.shape[0])]
Shift self.top
and self.bottom
by self.scroll
for each line:
top, bottom = self.top+self.scroll, self.bottom+self.scroll
If the line would "fall out" of the window at the bottom, then lift it to the top of the view:
if self.linerange[i][0]-self.scroll < self.bottom:
top, bottom = top-size, bottom-size
Complete paintGL
method:
class GLPlotWidget(QGLWidget):
# [...]
def paintGL(self):
self.resizeGL(self.width, self.height)
gl.glClearColor(0.75, 0.75, 0.75, 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)
self.ortho_view(self.shaders_program_plot)
uni_color = gl.glGetUniformLocation(self.shaders_program_plot, "triangleColor")
loc_top = gl.glGetUniformLocation(self.shaders_program_plot, "top")
loc_bottom = gl.glGetUniformLocation(self.shaders_program_plot, "bottom")
for i in range(0, self.vbo.data.shape[0]):
size = self.top - self.bottom
top, bottom = self.top+self.scroll, self.bottom+self.scroll
if self.linerange[i][0]-self.scroll < self.bottom:
top, bottom = top-size, bottom-size
gl.glUniform1f(loc_top, top)
gl.glUniform1f(loc_bottom, bottom)
gl.glUniform3f(uni_color, 0, 0, 0)
gl.glLineWidth(1)
gl.glDrawArrays(gl.GL_LINE_STRIP, i * self.count, self.count)
self.vbo.unbind()
Answered By - Rabbid76
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.