Related
I'm trying to create a relatively simple particle simulation, which should account for gravity, drag, the collision with other particles (inelastic collision) and the collision with walls (perfectly elastic). I got the gravity and drag part working with the velocity Verlet algorithm but its as of right now not capable of setting the particles to an equilibrium state. Furthermore if I add multiple particles they sometimes climb on each other which is due (as I believe) to them still having very small velocity components which asymptotically drives to zero. I tried to cut off the velocity of the particles if the energy of a particle gets sufficiently small but it wouldn't look realistic. Could somebody maybe point out some advice how to fix these issues. I got a particle Object:
import pygame
import random
import numpy as np
import operator
from itertools import combinations
class Particle:
def __init__(self):
self.mass = 10
self.radius = random.randint(10, 50)
self.width, self.height = 700, 500
self.pos = np.array((self.width/2, self.height/2))
self.v = np.array((0.0, 0.0))
self.acc = np.array((0.0, 0.0))
self.bounce = 0.95
I use the Verlet-Integration to account for gravity and drag forces:
def update(self, ball, dt):
new_pos = np.array((ball.pos[0] + ball.v[0]*dt + ball.acc[0]*(dt*dt*0.5), ball.pos[1] + ball.v[1]*dt + ball.acc[1]*(dt*dt*0.5)))
new_acc = np.array((self.apply_forces(ball))) # only needed if acceleration is not constant
new_v = np.array((ball.v[0] + (ball.acc[0]+new_acc[0])*(dt*0.5), ball.v[1] + (ball.acc[1]+new_acc[1])*(dt*0.5)))
ball.pos = new_pos;
ball.v = new_v;
ball.acc = new_acc;
def apply_forces(self, ball):
grav_acc = [0.0, 9.81]
drag_force = [0.5 * self.drag * (ball.v[0] * abs(ball.v[0])), 0.5 * self.drag * (ball.v[1] * abs(ball.v[1]))] #D = 0.5 * (rho * C * Area * vel^2)
drag_acc = [drag_force[0] / ball.mass, drag_force[1] / ball.mass] # a = F/m
return (-drag_acc[0]),(grav_acc[1] - drag_acc[1])
And here I calculate the collision part:
def collision(self):
pairs = combinations(range(len(self.ball_list)), 2)
for i,j in pairs:
part1 = self.ball_list[i]
part2 = self.ball_list[j]
distance = list(map(operator.sub, self.ball_list[i].pos, self.ball_list[j].pos))
if np.hypot(*distance) < self.ball_list[i].radius + self.ball_list[j].radius:
distance = part1.pos - part2.pos
rad = part1.radius + part2.radius
slength = (part1.pos[0] - part2.pos[0])**2 + (part1.pos[1] - part2.pos[1])**2
length = np.hypot(*distance)
factor = (length-rad)/length;
x = part1.pos[0] - part2.pos[0]
y = part1.pos[1] - part2.pos[1]
part1.pos[0] -= x*factor*0.5
part1.pos[1] -= y*factor*0.5
part2.pos[0] += x*factor*0.5
part2.pos[1] += y*factor*0.5
u1 = (part1.bounce*(x*part1.v[0]+y*part1.v[1]))/slength
u2 = (part2.bounce*(x*part2.v[0]+y*part2.v[1]))/slength
part1.v[0] = u2*x-u1*x
part1.v[1] = u1*x-u2*x
part2.v[0] = u2*y-u1*y
part2.v[1] = u1*y-u2*y
def check_boundaries(self, ball):
if ball.pos[0] + ball.radius > self.width:
ball.v[0] *= -ball.bounce
ball.pos[0] = self.width - ball.radius
if ball.pos[0] < ball.radius:
ball.v[0] *= -ball.bounce
ball.pos[0] = ball.radius
if ball.pos[1] + ball.radius > self.height:
self.friction = True
ball.v[1] *= -ball.bounce
ball.pos[1] = self.height - ball.radius
elif ball.pos[1] < ball.radius:
ball.v[1] *= -ball.bounce
ball.pos[1] = ball.radius
I am currently learning the basics of pyglet and opengl. I have watched a number of tutorials and websites to learn pyglet and opengl, and I thought I would make a simple 3d game. However I am stuck on the camera movement controls and I can't seem to figure out what is wrong with the code. When I press S the camera moves back normally, but then when I press W it doesn't move, and if I press it again it moves but a little faster than before. Why is this happening?
from pyglet.gl import *
from pyglet.window import key
import math
class Triangle:
def __init__(self):
self.vertices = pyglet.graphics.vertex_list(3, ('v3f', [-0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.0, 0.5, 0.0]),
('c3B', [100, 200, 220, 200, 110, 100, 100, 250, 100]))
class Quad:
def __init__(self):
self.vertices = pyglet.graphics.vertex_list_indexed(4, [0, 1, 2, 2, 3, 0],
('v3f', [-0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.5, 0.5, 0.0, -0.5, 0.5, 0.0]),
('c3f', [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0]))
class Player:
def __init__(self):
self.position = [0, 0, 0]
self.rotation = [0, 0]
self.change_x = 0
self.change_z = 0
self.speed = 1
def controls(self, keys):
self.change_x = 0
self.change_z = 0
if keys == key.W:
self.change_z = self.speed
if keys == key.S:
self.change_z = -self.speed
if keys == key.A:
self.change_x = -self.speed
if keys == key.D:
self.change_x = self.speed
self.position[0] += self.change_x
self.position[2] += self.change_z
class Window(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_minimum_size(300, 300)
glClearColor(0.2, 0.3, 0.2, 1.0)
gluPerspective(90, 0.5, 0.01, 1000)
self.quad = Quad()
self.player = Player()
def on_key_press(self, KEY, MOD):
self.player.controls(KEY)
def on_draw(self):
self.clear()
glTranslatef(*self.player.position)
self.quad.vertices.draw(GL_TRIANGLES)
def on_resize(self, width, height):
glViewport(0, 0, width, height)
if __name__ == '__main__':
window = Window(500, 500, 'My Window', resizable = True)
pyglet.app.run()
You probably misunderstood the glTranslate function.
glTranslate works in such a way, that it takes the current matrix and multiplies with a translation matrix given by parameters of glTranslate.
So, calling glTranslate(x, y, z) results in the following equation:
new_position = old_position + (x, y, z)
Which makes (x, y, z), if you call glTranslate every frame, the rate of change (i.e. the velocity).
Why does it stop when I press S and the W?
Well, let's take a look at the code and the equation. You use self.position as parameter for glTranslate. So, your self.position is not the position but the current velocity (or rate of change) of the camera.
If you press S once, on_key_press will be called, with following effects:
self.change_z = -self.speed
self.position[2] += self.change_z # self.position[2] equals now -self.speed
Now press W once, yet again on_key_press will be called again, resulting in this:
self.change_z = +self.speed
self.position[2] += self.change_z # self.position[2] equals now 0, because self.position[2] was -self.speed; -self.speed + self.speed = 0
The implication is that every time you press either control key, you accelerate your camera discretely.
Assuming you pressed S twice after starting the program would cause self.position[2] to equal to 2 * -self.speed.
How to fix this?
Change the following
self.position[0] += self.change_x
self.position[2] += self.change_z
to
self.position[0] = self.change_x
self.position[2] = self.change_z
I really hope I made it clear, why self.position is actually a velocity and not the position. I suggest you rename self.position to self.velocity or something else along the lines.
Alternatively you can rebuild the matrix every time you change your position by doing the following:
def on_draw(self):
self.clear()
glLoadIdentity()
gluPerspective(90, 0.5, 0.01, 1000)
glTranslatef(*self.player.position)
self.quad.vertices.draw(GL_TRIANGLES)
I cannot help but give a further step. The fact that self.position is just a velocity (in your original version!), becomes apparent if you add a another method:
class Window(...):
# ...
def update(self, dt):
self.clear()
glTranslatef(*self.player.position)
self.quad.vertices.draw(GL_TRIANGLES)
and extend Window.__init__ with the following lines:
class Window(...):
def __init__(...):
# ...
import pyglet.clock
pyglet.clock.schedule_interval(self.update, 1/60)
# pyglet now calls update(...) 60 times a second
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.
but all I am able to create till now is with white background like this
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.. and I am unable to understand how to pass color in triangles?
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()
I have created a 3-d numpy array consisting of X, Y, and Z axis coordinates. Now I am trying to create a surface using these points in opengl but all I have got success is in creating wire model something like this below . Can anyone suggest changes in my code to form actual 3-D surface from data?
Data file used link https://drive.google.com/open?id=1PWbNIt3xbchtQ9HIIS96k7ZjblzPO_wO
Code:-
import OpenGL.GL as gl
import OpenGL.arrays.vbo as glvbo
from PyQt5.Qt import *
import numpy as np
import Backend_algo as Sb
import sys
import ctypes
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
VS = '''
attribute vec3 position;
uniform float right;
uniform float bottom;
uniform float left;
uniform float top;
uniform float far;
uniform float near;
void main() {
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, 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);
}
'''
class GLPlotWidget3D(QGLWidget):
def __init__(self, *args):
# QGLWidget.__init__(self)
super(GLPlotWidget3D, self).__init__()
# self.parent = args[0]
self.width, self.height = 100, 100
self.right, self.left, self.top, self.bottom = 21000, -21000, 10, -10
self.data = np.zeros((3, 10, 2))
self.vbo = glvbo.VBO(self.data)
self.showMaximized()
def initializeGL(self):
vs = Sb.compile_vertex_shader(VS)
fs = Sb.compile_fragment_shader(FS)
self.shaders_program = link_shader_program(vs, fs)
self.e = np.load(('three.npy'), mmap_mode='r')
self.e = np.array(self.e, dtype=np.float32)
self.right, self.left, self.top, self.bottom, self.far, self.near = self.e[:, :, 1].min(), self.e[:, : , 1].max(), self.e[:, : , 0].min(), self.e[:, : , 0].max(), self.e[:, : , 2].max(), self.e[:, : , 2].min()
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)
far = gl.glGetUniformLocation(self.shaders_program, "far")
gl.glUniform1f(far, self.far)
near = gl.glGetUniformLocation(self.shaders_program, "near")
gl.glUniform1f(near, self.near)
def paintGL(self):
self.resizeGL(self.width, self.height)
gl.glClearColor(0.2, 0.2, 0.2, 0)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
gl.glUseProgram(self.shaders_program)
buffer = gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buffer)
stride = self.e.strides[0]
offset = ctypes.c_void_p(1)
loc = gl.glGetAttribLocation(self.shaders_program, "position")
gl.glEnableVertexAttribArray(loc)
gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)
gl.glBufferData(gl.GL_ARRAY_BUFFER, self.e.nbytes, self.e, gl.GL_DYNAMIC_DRAW)
gl.glDrawArrays(gl.GL_LINE_LOOP, 0, self.e.shape[0])
self.ortho_view()
uni_color = gl.glGetUniformLocation(self.shaders_program, "triangleColor")
gl.glUniform3f(uni_color, 0.9, 0.9, 0.9)
def resizeGL(self, width, height):
self.width, self.height = width, height
gl.glViewport(0, 0, width, height)
def main():
app = QApplication(sys.argv)
editor = GLPlotWidget3D()
editor.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
'three.npy' contains a 3 dimensional array (7782 x 24 x3) with the vertex coordinates of a tube. The 3rd dimension with a size of 3 contains the x, y and z coordinates of the vertices. The vertices are organized in 7782 rings with 24 point around the circumference.
Read the vertex coordinates to a flatten buffer (the numpy array is flatten automatically by glBufferData).
Generate an array of indices (indices of the vertex buffer). The indices describe GL_TRIANGLE_STRIP primitives which stack up 7781 rings. Each ring consisting of 24 quads around the circumference.
self.e = np.load(('three.npy'), mmap_mode='r')
self.e = np.array(self.e, dtype=np.float32)
self.elems = []
ring_c = self.e.shape[1]
slice_c = self.e.shape[0]
for si in range(slice_c-1):
self.elems += [si*ring_c, si*ring_c]
for ri in range(ring_c+1):
ie = ri % ring_c
self.elems += [ie+si*ring_c, ie+(si+1)*ring_c]
self.elems = np.array(self.elems, dtype=np.int32)
The x and y component of the vertices are in range [-10, 10], but the z component is in range [3, 29724672].
x min x max y min y max z min z max
-10.589109 10.517833 -10.464569 10.594374 29724672.0 3.1618009
I recommend to define a scale for the z coordinate:
self.scaleZ = 0.000001
Create a Vertex Buffer Object (GL_ARRAY_BUFFER) for the vertices and an Index buffer Object (GL_ELEMENT_ARRAY_BUFFER) for the indices:
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)
Specif the array of vertex coordinates. See Vertex Specification.
The stride and offset parameter of glVertexAttribPointer have to be 0.
stride specifies the byte offset between consecutive generic vertex attributes it has to be either 3*self.e.itemsize (12) or 0. 0 has a special meaning and and interprets the attributes as to be tightly packed. If stride is 0 it is computed by the size and type parameter.
offset hast to be ctypes.c_void_p(0) or None, because the offset of the 1 attribute in the array is 0.
In any case the unit of stride and offset is bytes.
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertexbuffer)
stride = 0 # 3*self.e.itemsize
offset = None # ctypes.c_void_p(0)
loc = self.attrib['position']
gl.glEnableVertexAttribArray(loc)
gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)
The primitive type is GL_TRIANGLE_STRIP and the index buffer has to be bound before the elements are drawn by glDrawElements:
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.elementbuffer)
self.perspective_view()
gl.glUniform3f(self.uniform['triangleColor'], 1, 1, 1)
gl.glDrawElements(gl.GL_TRIANGLE_STRIP, self.elems.size, gl.GL_UNSIGNED_INT, None)
Instead of specifying an Orthographic projection matrix in the vertex shader, I recommend to use matrix Uniform variable for the projection, respectively model and view transformation.
The projection matrix defines the projection of the 3 dimensional viewing volume to the 2 dimensional viewport. The view matrix defines the viewing position of view and viewing direction onto the scene. The model matrix defines the scale and animation of the mode.
attribute vec3 position;
uniform mat4 u_proj;
uniform mat4 u_view;
uniform mat4 u_model;
void main() {
gl_Position = u_proj * u_view * u_model * vec4(position, 1.0);
}
Get the attribute index and uniform locations after the shader program is linked:
vs = compile_vertex_shader(VS)
fs = compile_fragment_shader(FS)
self.shaders_program = link_shader_program(vs, fs)
self.attrib = { a : gl.glGetAttribLocation (self.shaders_program, a) for a in ['position'] }
print(self.attrib)
self.uniform = { u : gl.glGetUniformLocation (self.shaders_program, u) for u in ['u_model', 'u_view', 'u_proj', "triangleColor"] }
print(self.uniform)
For a 3D look, I recommend to use Perspective projection rather than Orthographic projection.
Use numpy.array or numpy.matrix to set the matrices.
# projection matrix
aspect, ta, near, far = self.width/self.height, np.tan(np.radians(90.0) / 2), 0.1, 50
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, 0, -1, 0), (0, 1, 0, 0), (0, 0, -30, 1)), np.float32)
# model matrix
c, s = math.cos(self.angle), math.sin(self.angle)
model = scale
gl.glUniformMatrix4fv(self.uniform['u_proj'], 1, gl.GL_FALSE, proj)
gl.glUniformMatrix4fv(self.uniform['u_view'], 1, gl.GL_FALSE, view)
gl.glUniformMatrix4fv(self.uniform['u_model'], 1, gl.GL_FALSE, model)
Full example:
(The fragment shader tints the fragments dependent on their depth)
import OpenGL.GL as gl
import OpenGL.arrays.vbo as glvbo
from PyQt5.Qt import *
import numpy as np
#import Backend_algo as Sb
import sys
import ctypes
import os
import math
sourceFileDir = os.path.dirname(os.path.abspath(__file__))
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
VS = '''
attribute vec3 position;
uniform mat4 u_proj;
uniform mat4 u_view;
uniform mat4 u_model;
void main() {
gl_Position = u_proj * u_view * u_model * vec4(position, 1.0);
}
'''
FS = '''
#version 450
out vec4 outColor;
uniform vec3 triangleColor;
void main()
{
float d = 1.0 - gl_FragCoord.z;
outColor = vec4(triangleColor * d, 1.0);
}
'''
class GLPlotWidget3D(QGLWidget):
def __init__(self, *args):
# QGLWidget.__init__(self)
super(GLPlotWidget3D, self).__init__()
# self.parent = args[0]
self.width, self.height = 100, 100
self.right, self.left, self.top, self.bottom = 21000, -21000, 10, -10
self.data = np.zeros((3, 10, 2))
self.vbo = glvbo.VBO(self.data)
self.showMaximized()
def initializeGL(self):
vs = compile_vertex_shader(VS)
fs = compile_fragment_shader(FS)
self.shaders_program = link_shader_program(vs, fs)
self.attrib = { a : gl.glGetAttribLocation (self.shaders_program, a) for a in ['position'] }
print(self.attrib)
self.uniform = { u : gl.glGetUniformLocation (self.shaders_program, u) for u in ['u_model', 'u_view', 'u_proj', "triangleColor"] }
print(self.uniform)
self.e = np.load((os.path.join(sourceFileDir,'three.npy')), mmap_mode='r')
self.e = np.array(self.e, dtype=np.float32)
print(self.e.shape)
self.elems = []
ring_c = self.e.shape[1]
slice_c = self.e.shape[0]
for si in range(slice_c-1):
self.elems += [si*ring_c, si*ring_c]
for ri in range(ring_c+1):
ie = ri % ring_c
self.elems += [ie+si*ring_c, ie+(si+1)*ring_c]
self.elems = np.array(self.elems, dtype=np.int32)
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.scaleZ = 0.000001
self.right, self.left, self.top, self.bottom, self.far, self.near = self.e[:, :, 1].min(), self.e[:, : , 1].max(), self.e[:, : , 0].min(), self.e[:, : , 0].max(), self.e[:, : , 2].max(), self.e[:, : , 2].min()
print(self.right, self.left, self.top, self.bottom, self.far, self.near)
self.far *= self.scaleZ
self.near *= self.scaleZ
self.angle = 0.0
self.GLtimer = QTimer()
self.GLtimer.setInterval(10)
self.GLtimer.timeout.connect(self.redraw)
self.GLtimer.start()
def redraw(self):
self.angle += 0.01
self.update()
def perspective_view(self):
# projection matrix
aspect, ta, near, far = self.width/self.height, np.tan(np.radians(90.0) / 2), 10, 50
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, 0, -1, 0), (0, 1, 0, 0), (0, 0, -30, 1)), np.float32)
# model matrix
c, s = math.cos(self.angle), math.sin(self.angle)
scale = np.matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, self.scaleZ, 0), (0, 0, 0, 1)), np.float32)
rotZ = np.array(((c, s, 0, 0), (-s, c, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)), np.float32)
rotY = np.matrix(((0, 0, 1, 0), (0, 1, 0, 0), (-1, 0, 0, 0), (0, 0, 0, 1)), np.float32)
trans = np.matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, (self.near - self.far)/2, 1)), np.float32)
model = scale * trans * rotY * rotZ
gl.glUniformMatrix4fv(self.uniform['u_proj'], 1, gl.GL_FALSE, proj)
gl.glUniformMatrix4fv(self.uniform['u_view'], 1, gl.GL_FALSE, view)
gl.glUniformMatrix4fv(self.uniform['u_model'], 1, gl.GL_FALSE, model)
def paintGL(self):
self.resizeGL(self.width, self.height)
gl.glClearColor(0.2, 0.2, 0.2, 0)
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
gl.glEnable(gl.GL_DEPTH_TEST)
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 = self.attrib['position']
gl.glEnableVertexAttribArray(loc)
gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.elementbuffer)
self.perspective_view()
gl.glUniform3f(self.uniform['triangleColor'], 1, 1, 1)
#gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE)
gl.glDrawElements(gl.GL_TRIANGLE_STRIP, self.elems.size, gl.GL_UNSIGNED_INT, None)
def resizeGL(self, width, height):
self.width, self.height = width, height
gl.glViewport(0, 0, width, height)
def main():
app = QApplication(sys.argv)
editor = GLPlotWidget3D()
editor.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I am pretty new to Python and are trying to make a option pricing class with three functions: call, put and graph. The call and put function work fine, but I can't figure out the graph function. I want the p.append to get the values from the call function, hold all the variables constant except for S0 which is equal to i.
import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt
class Option():
def __init__(self, S0, K, T, r, sigma, start, stop, N):
self.S0 = S0
self.K = K
self.T = T
self.r = r
self.sigma = sigma
self.start = start
self.stop = stop
self.N = N
def call(self):
d1 = (np.log(self.S0/self.K) + \
(self.r + 0.5*self.sigma**2)*self.T)/(self.sigma*np.sqrt(self.T))
d2 = d1 - self.sigma*np.sqrt(self.T)
price = (self.S0 * norm.cdf(d1, 0.0, 1.0) - \
self.K * np.exp(-self.r * self.T) * norm.cdf(d2, 0.0, 1.0))
return price
def put(self):
d1 = (np.log(self.S0/self.K) + \
(self.r + 0.5*self.sigma**2)*self.T)/(self.sigma*np.sqrt(self.T))
d2 = d1 - self.sigma*np.sqrt(self.T)
price = (self.K * np.exp(-self.r * self.T) * norm.cdf(-d2, 0.0, 1.0) - \
self.S0 * norm.cdf(-d1, 0.0, 1.0))
return price
def graphCall(self):
S = np.linspace(self.start, self.stop, self.N)
p = []
for i in S:
p.append()
plt.plot(S, p)
x = Option(100, 50, 3, 0.05, 0.40, 100, 200, 500)
print(x.call())
x.graphCall()
You could decide to use self.S0 as the default value for calls to call and put, but allow for a other arguments as well.
import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt
class Option():
def __init__(self, S0, K, T, r, sigma, start, stop, N):
self.S0 = S0
self.K = K
self.T = T
self.r = r
self.sigma = sigma
self.start = start
self.stop = stop
self.N = N
def call(self, s=None):
if s is None:
s=self.S0
d1 = (np.log(s/self.K) + \
(self.r + 0.5*self.sigma**2)*self.T)/(self.sigma*np.sqrt(self.T))
d2 = d1 - self.sigma*np.sqrt(self.T)
price = (s * norm.cdf(d1, 0.0, 1.0) - \
self.K * np.exp(-self.r * self.T) * norm.cdf(d2, 0.0, 1.0))
return price
def put(self, s=None):
if s is None:
s=self.S0
d1 = (np.log(s/self.K) + \
(self.r + 0.5*self.sigma**2)*self.T)/(self.sigma*np.sqrt(self.T))
d2 = d1 - self.sigma*np.sqrt(self.T)
price = (self.K * np.exp(-self.r * self.T) * norm.cdf(-d2, 0.0, 1.0) - \
s * norm.cdf(-d1, 0.0, 1.0))
return price
def graphCall(self):
S = np.linspace(self.start, self.stop, self.N)
plt.plot(S, self.call(S))
plt.show()
x = Option(100, 50, 3, 0.05, 0.40, 100, 200, 500)
print(x.call())
x.graphCall()