Implement Ocean LOD management and enhance grid generation with wireframe support

This commit is contained in:
2026-01-31 22:22:00 +01:00
parent dedcec547d
commit d61c91a267
5 changed files with 307 additions and 121 deletions

View File

@@ -1,6 +1,6 @@
import { vec3, mat4 } from 'gl-matrix';
import { Camera } from './Camera';
import { Grid } from './Grid';
import { OceanLOD } from './OceanLOD';
import { createProgram } from './Shader';
import * as Config from './constants';
@@ -112,8 +112,8 @@ function initFBO() {
// set the filtering so we don't need mips
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textureFBO, 0);
@@ -131,6 +131,7 @@ var lastTime = new Date().getTime();
var counter = 0.0;
var fps = 0;
var fpsDisplay: HTMLElement | null = null;
var lodStatsTimer = 0;
/** Input states*/
var mouseXVel = 0;
var mouseYVel = 0;
@@ -140,14 +141,17 @@ var keyboardZoom = 0;
var keysPressed: Set<string> = new Set();
/** Objects and states*/
var camera: Camera;
var oceanGrid: Grid;
var oceanLOD: OceanLOD;
var curRotX = Config.CAMERA_DEFAULT_ROT_X;
var curRotY = Config.CAMERA_DEFAULT_ROT_Y;
var wireframeMode = false;
function drawScene() {
fps++;
let now = new Date();
let delta = now.getTime() - lastTime;
timeSpent += delta;
lodStatsTimer += delta;
if ((counter += delta) >= Config.FPS_UPDATE_INTERVAL) {
counter = 0;
if (fpsDisplay) {
@@ -155,62 +159,38 @@ function drawScene() {
}
fps = 0;
}
lastTime = now.getTime();
// Two Rendering passes. The first one generates a perlin noise
// texture. Second one uses the textur for vertex displacement
// of a grid representing the water surface.
//--- First render pass -> Perlin Noise (it updates the perlin noise texture)
{
gl.bindFramebuffer(gl.FRAMEBUFFER, perlinNoiseFBO);
gl.viewport(0, 0, perlinNoiseFBOWidth, perlinNoiseFBOHeight);
//Clear buffer content
gl.clearColor(1.0, 1.0, 1.0, 1);
gl.clear(gl.COLOR_BUFFER_BIT); //No depth buffer
//draw a fullscreen quad
gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
// There are 7 floating-point values per vertex
let stride = 5 * Float32Array.BYTES_PER_ELEMENT;
// Set up position stream
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, stride, 0);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, stride, 3 * Float32Array.BYTES_PER_ELEMENT);
gl.enableVertexAttribArray(1);
gl.useProgram(perlinNoiseProgram);
let uTime = gl.getUniformLocation(<WebGLProgram>perlinNoiseProgram, "uTime");
gl.uniform1f(uTime, timeSpent);
gl.drawArrays(gl.TRIANGLES, 0, 6); // Draw fullscreen quad
// Log LOD stats every 5 seconds
if (lodStatsTimer >= 5000) {
lodStatsTimer = 0;
const stats = oceanLOD.getLODStats();
console.log(`LOD Stats - High:${stats[0]} Med:${stats[1]} Low:${stats[2]} VeryLow:${stats[3]}`);
}
lastTime = now.getTime();
// Single render pass with Gerstner waves computed in vertex shader
//--- Second render pass -> Geomtry with displacement by perlin noise texture ---
//--- Render pass -> Ocean with Gerstner wave displacement ---
{
gl.bindFramebuffer(gl.FRAMEBUFFER, null); //Bind default framebuffer
gl.viewport(0, 0, viewportWidth, viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.activeTexture(gl.TEXTURE0); //Binds the texture to 0
gl.bindTexture(gl.TEXTURE_2D, textureFBO);
var projection = mat4.create();
mat4.identity(projection);
mat4.perspective(projection, Config.FOV, viewportWidth / viewportHeight, Config.NEAR_PLANE, Config.FAR_PLANE); //projection mode should actually be camera specific
mat4.perspective(projection, Config.FOV, viewportWidth / viewportHeight, Config.NEAR_PLANE, Config.FAR_PLANE);
camera.setOffset(Config.CAMERA_DEFAULT_OFFSET + keyboardZoom);
camera.setRotationX((curRotX += mouseYVel * Config.MOUSE_SENSITIVITY + keyboardRotationX));
camera.setRotationY((curRotY += mouseXVel * Config.MOUSE_SENSITIVITY + keyboardRotationY));
var view = camera.getViewMatrix();
// Update LOD based on camera position
oceanLOD.updateLOD(gl, camera.pos);
var model = mat4.create();
mat4.identity(model);
let translationCentering = vec3.create();
vec3.set(translationCentering, -0.5, -0.5, 0.0);
mat4.translate(model, model, translationCentering); //1. First Center the Surface in the origin.
// No centering needed - grids are already positioned correctly in world space
gl.useProgram(defaultProgram);
let view_loc = gl.getUniformLocation(<WebGLProgram>defaultProgram, "view");
@@ -221,11 +201,9 @@ function drawScene() {
gl.uniformMatrix4fv(projection_loc, false, projection);
let eye_loc = gl.getUniformLocation(<WebGLProgram>defaultProgram, "eyePos");
gl.uniform3fv(eye_loc, camera.pos);
//let uTime_loc = gl.getUniformLocation(<WebGLProgram>defaultProgram, "uTime");
//gl.uniform1f(uTime_loc, timeSpent);
let displacementMap_loc = gl.getUniformLocation(<WebGLProgram>defaultProgram, "displace_map");
gl.uniform1i(displacementMap_loc, 0); //Get texture from slot 0
oceanGrid.draw(gl);
let uTime_loc = gl.getUniformLocation(<WebGLProgram>defaultProgram, "uTime");
gl.uniform1f(uTime_loc, timeSpent);
oceanLOD.draw(gl, wireframeMode);
}
requestAnimationFrame(drawScene);
}
@@ -317,13 +295,24 @@ function main() {
window.addEventListener('resize', () => {
updateCanvasSize(canvas);
});
// Wireframe toggle handler
window.addEventListener('toggleWireframe', () => {
wireframeMode = !wireframeMode;
const wireframeBtn = document.getElementById('wireframe-toggle');
if (wireframeBtn) {
wireframeBtn.textContent = `Wireframe: ${wireframeMode ? 'ON' : 'OFF'}`;
}
console.log(`Wireframe mode: ${wireframeMode ? 'ON' : 'OFF'}`);
});
initShaders();
initGeometry();
initFBO();
oceanGrid = new Grid(Config.GRID_SIZE);
oceanGrid.initVAO(gl);
oceanLOD = new OceanLOD();
oceanLOD.initVAO(gl);
console.log(`Ocean LOD initialized with ${oceanLOD.getGridCount()} patches`);
camera = new Camera();
//Check if any errors apeared during init.