我是图形开发的新手,我正在尝试使用Gerstner wave来显示wave。我的代码主要来自Youtube GetIntoGameDev,除了我创建飞机的部分。我有一些优化的问题和一些想法,它可以从哪里来。
我在更新顶点的位置时遇到了一些问题。首先,我发送一个三角形的顶点缓冲区,每个点至少存在于一个三角形中,最多4个,所以当我必须更新我改变4个点。由于这是指数级的网格大小,我得到了很多延迟。有没有办法只发送顶点,然后面作为一个三元组的列表,然后只更新顶点。
我的下一个问题是关于调用'update':有没有办法让它每帧调用一次?
main.py
import glfw
import glfw.GLFW as GLFW_CONSTANTS
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram,compileShader
import numpy as np
import pyrr
from PIL import Image
from math import cos,sin
SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 720
RETURN_ACTION_CONTINUE = 0
RETURN_ACTION_END = 1
def initialize_glfw():
glfw.init()
glfw.window_hint(GLFW_CONSTANTS.GLFW_CONTEXT_VERSION_MAJOR, 3 )
glfw.window_hint(GLFW_CONSTANTS.GLFW_CONTEXT_VERSION_MINOR, 3 )
glfw.window_hint(GLFW_CONSTANTS.GLFW_OPENGL_PROFILE, GLFW_CONSTANTS.GLFW_OPENGL_CORE_PROFILE )
glfw.window_hint(GLFW_CONSTANTS.GLFW_OPENGL_FORWARD_COMPAT, GLFW_CONSTANTS.GLFW_TRUE )
glfw.window_hint(GLFW_CONSTANTS.GLFW_DOUBLEBUFFER, GLFW_CONSTANTS.GLFW_FALSE )
window = glfw.create_window(SCREEN_WIDTH, SCREEN_HEIGHT, "Water simulation", None, None)
glfw.make_context_current(window)
glfw.set_input_mode(window, GLFW_CONSTANTS.GLFW_CURSOR, GLFW_CONSTANTS.GLFW_CURSOR_HIDDEN)
return window
p=0.05
ky =[1,23, 5, 8]
kx =[-1,-7, -13, -12]
A= [0.05,0.001,0.002, 0.002]
w = [200,800,500, 550]
omega= [i*0.005 for i in w]
def genVertices():
vertices = []
N = int(1/p)
for i in range(N):
for j in range(N):
x,y,z= p*i,p*j,0
vertices.append([x,y,z])
return vertices
def genEdges(vertices):
edges = []
N = int(1/p)
for i in range(len(vertices)):
if i%N != 0:
edges.append([i-1,i])
for i in range(N):
for j in range(N-1):
edges.append([j*N+i,(j+1)*N+i])
return edges
def genFacesTri(vertices):
faces = []
N=int(1/p)
for i in range(N-1):
for j in range(N-1):
faces += vertices[N*j+i]
faces += [0,1]
faces += vertices[i+1+N*j]
faces += [1,0]
faces += vertices[i+N+N*j]
faces += [1,1]
for i in range(1,N):
for j in range(N-1):
faces+= vertices[N*j+i]
faces += [1,1]
faces += vertices[i-1+N+N*j]
faces += [0,1]
faces += vertices[i+N+N*j]
faces += [1,0]
return faces
faces = genFacesTri(genVertices())
class Player:
def __init__(self, position):
self.position = np.array(position, np.float32)
self.theta = 0
self.phi = 0
self.update_vectors()
def update_vectors(self):
self.forwards = np.array(
[
np.cos(np.deg2rad(self.theta)) * np.cos(np.deg2rad(self.phi)),
np.sin(np.deg2rad(self.theta)) * np.cos(np.deg2rad(self.phi)),
np.sin(np.deg2rad(self.phi))
]
)
globalUp = np.array([0,0,1], np.float32)
self.right = np.cross(self.forwards, globalUp)
self.up = np.cross(self.right, self.forwards)
class Scene:
def __init__(self):
#mis cube 7:40
self.player = Player(position=[0,0,0])
self.plane = Plane(
position = [0,0,0],
eulers = [0,0,0]
)
self.entities = [
self.plane
]
def update(self,t):
pass
def move_player(self,dPos):
dPos = np.array(dPos, np.float32)
self.player.position += dPos
def spin_player(self, dTheta, dPhi):
self.player.theta += dTheta
if self.player.theta >360:
self.player.theta -= 360
if self.player.theta < 0:
self.player.theta += 360
self.player.phi = min(89, max(self.player.phi + dPhi, -89))
self.player.update_vectors()
class GraphicsEngine:
def __init__(self):
self.mesh = PlaneMesh(vertices=faces)
self.water_texture = Material("gfx/water_texture.jpg")
glClearColor(0.1, 0.2, 0.2, 1)
self.shader = self.create_shader(vertex_filepath = "shaders/vertex.txt", fragment_filepath = "shaders/fragment.txt")
glUseProgram(self.shader)
glUniform1i(glGetUniformLocation(self.shader, "imageTexture"), 0)
glEnable(GL_DEPTH_TEST)
projection_transform = pyrr.matrix44.create_perspective_projection(
fovy = 45, aspect = 1280/720,
near = 0.001, far = 10, dtype=np.float32
)
glUniformMatrix4fv(
glGetUniformLocation(self.shader,"projection"),
1, GL_FALSE, projection_transform
)
self.modelMatrixLocation = glGetUniformLocation(self.shader,"model")
self.viewMatrixLocation = glGetUniformLocation(self.shader,"view")
def create_shader(self,vertex_filepath: str, fragment_filepath: str) -> int:
with open(vertex_filepath,'r') as f:
vertex_src = f.readlines()
with open(fragment_filepath,'r') as f:
fragment_src = f.readlines()
shader = compileProgram(compileShader(vertex_src, GL_VERTEX_SHADER),
compileShader(fragment_src, GL_FRAGMENT_SHADER))
return shader
def render(self,scene):
#refresh screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.shader)
view_transform = pyrr.matrix44.create_look_at(
eye = scene.player.position,
target = scene.player.position+scene.player.forwards,
up = scene.player.up,
dtype=np.float32)
glUniformMatrix4fv(self.viewMatrixLocation, 1, GL_FALSE, view_transform)
self.water_texture.use()
glBindVertexArray(self.mesh.vao)
for entity in scene.entities:
model_transform = pyrr.matrix44.create_identity(np.float32)
model_transform = pyrr.matrix44.multiply(
m1=model_transform,
m2=pyrr.matrix44.create_from_eulers(
eulers = entity.eulers,
dtype = np.float32
)
)
model_transform = pyrr.matrix44.multiply(
m1=model_transform,
m2=pyrr.matrix44.create_from_translation(
vec=np.array(entity.position),dtype=np.float32
)
)
glUniformMatrix4fv(
self.modelMatrixLocation, 1, GL_FALSE,
model_transform)
glDrawArrays(GL_TRIANGLES, 0, self.mesh.vertex_count)
glFlush()
def quit(self):
self.mesh.destroy()
self.water_texture.destroy()
glDeleteProgram(self.shader)
class Plane:
def __init__(self, position: list[float], eulers: list[float]):
self.position = np.array(position, dtype=np.float32)
self.eulers = np.array(eulers, dtype=np.float32)
class App:
def __init__(self, window):
self.window = window
self.renderer = GraphicsEngine()
self.scene = Scene()
self.lastTime = glfw.get_time()
self.currentTime = 0
self.numFrames = 0
self.frameTime = 0
self.walk_offset_lookup = {
1: 0,
2: 90,
3: 45,
4: 180,
6: 135,
7: 9,
8: 270,
9: 315,
11: 0,
12: 225,
13: 270,
25: 180,
}
self.run()
def run(self) -> None:
""" Run the app """
running = True
while (running):
if glfw.window_should_close(self.window) or glfw.get_key(self.window, GLFW_CONSTANTS.GLFW_KEY_ESCAPE) == GLFW_CONSTANTS.GLFW_PRESS:
running = False
self.handleKeys()
self.handleMouse()
glfw.poll_events()
self.scene.update(self.frameTime / 16.7)
self.renderer.render(self.scene)
#timing
self.calculateFramerate()
self.quit()
def handleKeys(self):
"""
z : 1 -> 0 degrees
q : 2 -> 90 degrees
z & q : 3 -> 45 degrees
s : 4 -> 180 degrees
z & s : 5 -> x
q & s : 6 -> 135 degrees
z & q & s : 7 -> 90 degrees
d : 8 -> 270 degrees
z & d : 9 -> 315 degrees
q & d : 10 -> x
z & q & d : 11 -> 0 degrees
s & d : 12 -> 225 degrees
z & s & d : 13 -> 270 degrees
q & s & d : 14 -> 180 degrees
w & a & s & d: 15 -> x
"""
combo = 0
directionModifier = 0
if glfw.get_key(self.window, GLFW_CONSTANTS.GLFW_KEY_W) == GLFW_CONSTANTS.GLFW_PRESS:
combo += 1
if glfw.get_key(self.window, GLFW_CONSTANTS.GLFW_KEY_A) == GLFW_CONSTANTS.GLFW_PRESS:
combo += 2
if glfw.get_key(self.window, GLFW_CONSTANTS.GLFW_KEY_S) == GLFW_CONSTANTS.GLFW_PRESS:
combo += 4
if glfw.get_key(self.window, GLFW_CONSTANTS.GLFW_KEY_D) == GLFW_CONSTANTS.GLFW_PRESS:
combo += 8
if glfw.get_key(self.window, GLFW_CONSTANTS.GLFW_KEY_SPACE) == GLFW_CONSTANTS.GLFW_PRESS:
self.scene.player.position[2] += 0.01
if glfw.get_key(self.window, GLFW_CONSTANTS.GLFW_KEY_LEFT_CONTROL) == GLFW_CONSTANTS.GLFW_PRESS:
self.scene.player.position[2] -= 0.01
if combo in self.walk_offset_lookup:
directionModifier = self.walk_offset_lookup[combo]
self.scene.player.update_vectors()
dPos = [
0.05* self.frameTime / 16.7 * np.cos(np.deg2rad(self.scene.player.theta + directionModifier)),
0.05 *self.frameTime / 16.7 * np.sin(np.deg2rad(self.scene.player.theta + directionModifier)),
0
]
self.scene.move_player(dPos)
def handleMouse(self):
(x,y) = glfw.get_cursor_pos(self.window)
rate = 0.2* self.frameTime / 16.7
theta_increment = rate * ((SCREEN_WIDTH/2) - x)
phi_increment = rate * ((SCREEN_HEIGHT/2) - y)
self.scene.spin_player(theta_increment,phi_increment)
glfw.set_cursor_pos(self.window, SCREEN_WIDTH/2, SCREEN_HEIGHT/2)
def calculateFramerate(self):
self.currentTime = glfw.get_time()
delta = self.currentTime - self.lastTime
if (delta >= 1):
framerate = max(1, int( self.numFrames / delta))
glfw.set_window_title(self.window, f"Ocean : {framerate} fps")
self.lastTime = self.currentTime
self.numFrames = - 1
self.frameTime = float(1000/max(1,framerate))
self.numFrames+=1
#Problem
if (delta >= .1):
#update plane
self.renderer.mesh.update(glfw.get_time())
def quit(self):
self.renderer.quit()
class PlaneMesh:
def __init__(self, vertices):
# x, y, z, s, t
self.vertex_count = len(vertices)//5
self.vertices = np.array(vertices, dtype=np.float32)
self.initialVertices = self.vertices.copy()
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
self.vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 20, ctypes.c_void_p(0))
glEnableVertexAttribArray(1)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 20, ctypes.c_void_p(12))
def update(self,t):
for i in range(0,len(self.vertices),5):
x0,y0,z0 = self.initialVertices[i],self.initialVertices[i+1],self.initialVertices[i+2]
x,y,z = x0,y0,z0
for j in range(len(kx)):
xi = kx[j]*x0+ky[j]*y0-omega[j]*t
x += kx[j]*A[j]*cos(xi)
z += A[j]*sin(xi)
y += ky[j]*A[j]*cos(xi)
self.vertices[i], self.vertices[i+1], self.vertices[i+2] = x,y,z
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
glBufferSubData(GL_ARRAY_BUFFER, 0, self.vertices.nbytes, self.vertices)
def destroy(self) -> None:
glDeleteVertexArrays(1,(self.vao,))
glDeleteBuffers(1,(self.vbo,))
class Material:
def __init__(self, filepath: str):
self.texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.texture)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
with Image.open(filepath, mode = 'r') as image:
image_width,image_height = image.size
image = image.convert("RGBA")
img_data = bytes(image.tobytes())
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,image_width,image_height,0,GL_RGBA,GL_UNSIGNED_BYTE,img_data)
glGenerateMipmap(GL_TEXTURE_2D)
def use(self) -> None:
"""
Arm the texture for drawing.
"""
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D,self.texture)
def destroy(self) -> None:
"""
Free the texture.
"""
glDeleteTextures(1, (self.texture,))
window = initialize_glfw()
my_app = App(window)
字符串
vertex.txt
#version 330 core
layout (location=0) in vec3 vertexPos;
layout (location=1) in vec2 vertexTexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec2 fragmentTexCoord;
void main()
{
gl_Position = projection * model * view * vec4(vertexPos, 1.0);
fragmentTexCoord = vertexTexCoord;
}
型
fragment.txt
#version 330 core
in vec2 fragmentTexCoord;
uniform sampler2D imageTexture;
out vec4 color;
void main()
{
color = texture(imageTexture, fragmentTexCoord);
color = color * vec4(1,1,1,0.5);
}
型
我这样做的方式适用于小网格p = 0.1
到p = 0.04
,但它开始滞后,我认为有优化的空间。
谢谢你,我很好
1条答案
按热度按时间vktxenjb1#
简答:
通常,当人们希望网格以可预测的方式更新每一帧时,他们使用GPU,即着色器来修改它们。然而,大多数时候,您只需要传递一个矩阵,并将其与顶点着色器中的顶点相乘。
详情:
在这里,你可以指定
t
和你的其他变量作为一个统一变量,类似于你指定MVP矩阵的方式,并将整个update
函数移动到你的顶点着色器:字符串
但在这种情况下,就像在许多其他情况下一样,你不需要执行创建一个单独的函数的麻烦,实际上可以将变换表示为一个矩阵,这样你就可以做这样的事情,这就简单得多:
型
然后你只需要在CPU代码中指定矩阵。如果你的效率更高,你可以将你的变换矩阵乘以
model
,这样你就不必处理额外的统一,但在几乎所有的情况下,它在着色器代码中并不是什么麻烦。当然,还有更精确和更高级的方法来更新网格,比如Geometry Shaders,Geometry Shaders是顶点和片段着色器之间的中间阶段,控制primitives的创建。