我正在尝试通过 pygame 使用 glsl 着色器和窗口管理在 python 中重新创建以下文章中的连锁反应 https://web.archive.org/web/20160418004149/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm 我发现了一些其他来源: https://www.youtube.com/watch?v=qm5cDNbtGig https://www.youtube.com/watch?v=BZUdGqeOD0w
如果我单击更改两个缓冲区之间像素的 RGB 值,同时还应用阻尼效果,着色器应该会在屏幕上创建水波纹。
理论上来说,有一个当前缓冲区存储当前帧的像素数据,还有一个先前缓冲区包含来自前一帧的纹素数据。您可以根据周围像素的值更改每个像素的 RGB 值,同时应用阻尼因子来降低像素的亮度。
然后显示当前缓冲区 并交换缓冲区
该实现不应该使用余弦或正弦,这使得它非常有趣。
我的问题是波纹没有显示出来。
我尝试过两个版本,但都不成功: 第一次尝试主要是在第一个视频之后 我使用 numpy 数组(float32),它们被写入 Moderngl 纹理中。 片段着色器捕获着色器的像素并像视频中一样对其进行修改。然后吐出结果是一样的。
第二种大部分放弃了片段着色器 它使用两个 float32 的 numpy 数组,这些数组被写入 Moderngl 纹理中,仅用于渲染。 我按照这篇文章来处理更改像素并重新写入纹理
第一次尝试
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()
第二次尝试
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()
我可以帮忙吗?
哭吧你这个悲伤的小男孩