I am trying to recreate the ripple effect from the following article in python with glsl shaders and window managment via pygame https://web.archive.org/web/20160418004149/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm I've found a few other sources which I've pulled from: https://www.youtube.com/watch?v=qm5cDNbtGig https://www.youtube.com/watch?v=BZUdGqeOD0w
The shader is supposed to create water ripples onto the screen were I click by changing the rgb values of the pixels between tw o buffers while also applying a dampening effect.
Theory is that there is a current buffer which stores pixel data of the current frame and a previous buffer which contains texel data from the previous frame. You change the rgb value for each pixel depending on the values of surrounding pixels while applying a dampening factor which reduces the brightness of the pixel.
Then you display the current buffer and swap the buffers
The implementation isn't supposed to use cosine or sine which makes it very intersting.
My problem is that the ripples do not get displayed.
I've tried two versions which are both unsucsessful: The first try mostly follows the 1st video where I use numpy arrays (float32) which are written into moderngl textures. The fragment shader graps the pixel of the shader and modifies it like in the video. Then spits out the result the same.
The second mostly abandons the fragment shader It uses two numpy arrays of float32 which are written into moderngl textures only for rendering. I follow the article to handle changing the pixels and re-write to the texture
first attempt
import numpy
import pygame, moderngl
from sys import exit
class Main():
def __init__(self):
self.vertex_shader: str = """
# version 460 core
in vec2 aPosition;
in vec2 aTexCoord;
out vec2 pos;
void main(){
pos = aTexCoord;
pos.y = 1.0 - pos.y;
gl_Position = vec4(aPosition, 0.0, 1.0);
}
"""
self.fragment_shader: str = """
#version 460 core
uniform vec2 resolution;
uniform float dampening;
uniform sampler2D currBuffer;
uniform sampler2D prevBuffer;
in vec2 pos;
out vec4 f_colour;
void main(){
vec2 pix = 1.0/resolution;
float prev = texture(prevBuffer, pos).r;
float u = texture(currBuffer, pos + vec2(0.0, pix.y)).r;
float d = texture(currBuffer, pos - vec2(0.0, pix.y)).r;
float r = texture(currBuffer, pos + vec2(pix.x, 0.0)).r;
float l = texture(currBuffer, pos - vec2(pix.x, 0.0)).r;
float next = ((u + d + l + r) / 2.0) - prev;
next = next * dampening;
f_colour = vec4(next, next/2.0 + 0.5, 1.0, 1.0);
}
"""
pygame.init()
self.screen: pygame.Surface = pygame.display.set_mode((1000,600), pygame.RESIZABLE | pygame.OPENGL | pygame.DOUBLEBUF)
self.clock: pygame.time.Clock = pygame.time.Clock()
pygame.display.set_caption("Render Engine Test")
self.ctx: moderngl.Context = moderngl.create_context()
self.program: moderngl.Program = self.ctx.program(vertex_shader=self.vertex_shader, fragment_shader=self.fragment_shader)
self.vbo_1: moderngl.Buffer = self.ctx.buffer(data=numpy.array([-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0], dtype="f4"))
self.vbo_2: moderngl.Buffer = self.ctx.buffer(data=numpy.array([-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0], dtype="f4"))
self.vao: moderngl.VertexArray = self.ctx.vertex_array(self.program, [(self.vbo_1, "2f", "aPosition"), (self.vbo_2, "2f", "aTexCoord")])
self.current_buffer = numpy.array([[0.0 for x in range(self.screen.get_size()[0])] for y in range(self.screen.get_size()[1])], dtype="f4")
self.previous_buffer = numpy.array([[0.0 for x in range(self.screen.get_size()[0])] for y in range(self.screen.get_size()[1])], dtype="f4")
self.current_buffer[500][300] = 0.1
self.program["resolution"] = self.screen.get_size()
self.program["dampening"] = 0.9
self.current_texture: moderngl.Texture = self.ctx.texture(size=self.screen.get_size(), components=4)
self.previous_texture: moderngl.Texture = self.ctx.texture(size=self.screen.get_size(), components=4)
self.current_texture.use(location=0)
self.previous_texture.use(location=1)
self.program["currBuffer"] = 0
self.program["prevBuffer"] = 1
def update(self):
self.current_texture.write(data=self.current_buffer)
self.previous_texture.write(data=self.previous_buffer)
temp_buffer = self.previous_buffer
self.previous_buffer = self.current_buffer
self.current_buffer = temp_buffer
def draw(self):
self.ctx.clear(0.0, 0.0, 0.0)
self.vao.render(mode=moderngl.TRIANGLE_STRIP)
pygame.display.flip()
def check_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.exit_garbage()
pygame.quit()
exit()
def exit_garbage(self):
self.vao.release()
self.program.release()
self.vbo_1.release()
self.vbo_2.release()
self.current_texture.release()
self.previous_texture.release()
def run(self):
while True:
self.check_events()
self.update()
self.draw()
self.clock.tick(60)
if __name__ == "__main__":
main: Main = Main()
main.run()
second attempt
import numpy
import pygame, moderngl
from sys import exit
class Main():
def __init__(self):
self.vertex_shader: str = """
# version 460 core
in vec2 aPosition;
void main(){
gl_Position = vec4(aPosition, 0.0, 1.0);
}
"""
self.fragment_shader: str = """
#version 460 core
uniform vec2 resolution;
uniform sampler2D myTexture;
uniform float dampening;
out vec4 f_colour;
void main(){
vec2 pos = vec2(gl_FragCoord.xy / resolution.xy);
float next = texture(myTexture, pos).r * dampening;
f_colour = vec4(next, next/2.0 + 0.5, 1.0, 1.0);
}
"""
pygame.init()
self.screen: pygame.Surface = pygame.display.set_mode((500,500), pygame.RESIZABLE | pygame.OPENGL | pygame.DOUBLEBUF)
self.clock: pygame.time.Clock = pygame.time.Clock()
pygame.display.set_caption("Render Engine Test")
self.ctx: moderngl.Context = moderngl.create_context()
self.program: moderngl.Program = self.ctx.program(vertex_shader=self.vertex_shader, fragment_shader=self.fragment_shader)
self.vbo_1: moderngl.Buffer = self.ctx.buffer(data=numpy.array([-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0], dtype="f4"))
self.vao: moderngl.VertexArray = self.ctx.vertex_array(self.program, [(self.vbo_1, "2f", "aPosition")])
# self.buffer_1 = numpy.array([1.0 for x in range(self.screen.get_width()) for x in range(self.screen.get_height())], dtype="f4")
self.buffer_1 = numpy.zeros(shape=(self.screen.get_height(), self.screen.get_width()), dtype="f4")
self.buffer_2 = numpy.zeros(shape=(self.screen.get_height(), self.screen.get_width()), dtype="f4")
self.texture: moderngl.Texture = self.ctx.texture(size=self.screen.get_size(), components=4)
self.texture.write(data=self.buffer_1.tobytes())
self.texture.use(location=0)
self.program["dampening"] = 0.9
self.program["resolution"] = self.screen.get_size()
self.program["myTexture"] = 0
def update(self):
try:
for y in range(len(self.buffer_2)):
for x in range(len(self.buffer_2)):
self.buffer_2[y][x] = (
self.buffer_1[y][x-1] +
self.buffer_1[y][x+1] +
self.buffer_1[y+1][x] +
self.buffer_1[y-1][x]
) / 2 - self.buffer_2[y][x]
except: pass
def draw(self):
self.ctx.clear(0.0, 0.0, 0.0)
self.vao.render(mode=moderngl.TRIANGLE_STRIP)
pygame.display.flip()
temp_buffer = self.buffer_2
self.buffer_2 = self.buffer_1
self.buffer_1 = temp_buffer
self.texture.write(data=self.buffer_1.tobytes())
def check_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.exit_garbage()
pygame.quit()
exit()
if pygame.mouse.get_pressed()[0]:
x: int; y: int
x, y = pygame.mouse.get_pos()
self.buffer_1[y-1][x-1] = 1
def exit_garbage(self):
self.ctx.release()
self.vao.release()
self.vbo_1.release()
self.texture.release()
def run(self):
while True:
self.check_events()
self.update()
self.draw()
self.clock.tick(60)
if __name__ == "__main__":
main: Main = Main()
main.run()
Could I have some assistance?