#version 450 layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoords; out vec3 FragPos; out vec2 TexCoords; out vec3 Normal; uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform float uTime; // Wave configuration constants const float AMPLITUDE = 0.15; const float FREQUENCY = 1.5; const float SPEED = 2.0; // Simple wave function that modifies elevation based on position and time float calculateWave(vec3 pos, float time, vec2 direction) { return AMPLITUDE * sin(dot(pos.xz, direction) * FREQUENCY + time * SPEED); } // Derivative of the wave function to calculate accurate dynamic surface normals vec3 calculateWaveNormal(vec3 pos, float time) { float dx = AMPLITUDE * FREQUENCY * cos(pos.x * FREQUENCY + time * SPEED); float dz = AMPLITUDE * FREQUENCY * cos(pos.z * FREQUENCY + time * SPEED); return normalize(vec3(-dx, 1.0, -dz)); } void main() { vec3 displacedPos = aPos; // Combine two wave directions for a less predictable, more organic look displacedPos.y += calculateWave(aPos, uTime, vec2(1.0, 0.0)); displacedPos.y += calculateWave(aPos, uTime * 1.2, vec2(0.5, 0.8)); FragPos = vec3(model * vec4(displacedPos, 1.0)); TexCoords = aTexCoords; // Pass transformed normals and position to the fragment shader Normal = mat3(transpose(inverse(model))) * calculateWaveNormal(displacedPos, uTime); gl_Position = projection * view * model * vec4(displacedPos, 1.0); }