Add Skybox class for rendering sky and update shaders for sky rendering
This commit is contained in:
61
index.html
61
index.html
@@ -405,6 +405,7 @@
|
||||
|
||||
void main(void) {
|
||||
vec4 worldPos = model * vec4(positionAttr.xyz, 1.0);
|
||||
// Grid is on XY plane, Z is up
|
||||
vec2 pos = worldPos.xy;
|
||||
float time = uTime * 0.0004 * uWaveSpeed;
|
||||
float heightMod = uWaveHeight;
|
||||
@@ -470,10 +471,10 @@
|
||||
// Foam appears where wave is high AND rising (leading edge / crest)
|
||||
v_foamFactor = clamp((foamFromHeight * waveRising * 1.2 + foamFromSlope * 0.3), 0.0, 1.0);
|
||||
|
||||
// Apply displacement
|
||||
// Apply displacement - Z is up, XY is horizontal plane
|
||||
worldPos.x += displacement.x;
|
||||
worldPos.y += displacement.z;
|
||||
worldPos.z += displacement.y;
|
||||
worldPos.z += displacement.y; // Height displacement
|
||||
|
||||
// Calculate normal from tangent and binormal
|
||||
vec3 normal = normalize(cross(binormal, tangent));
|
||||
@@ -488,10 +489,51 @@
|
||||
<script id="sky-fs" type="x-shader/x-fragment">
|
||||
precision mediump float;
|
||||
|
||||
varying vec3 fragPos;
|
||||
varying vec3 v_rayDir;
|
||||
|
||||
uniform vec3 uSunDirection;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(fragPos,1.0);
|
||||
vec3 rayDir = normalize(v_rayDir);
|
||||
|
||||
// Use Z as up (matches world space where ocean is on XY plane)
|
||||
float upAmount = rayDir.z;
|
||||
|
||||
// Sky gradient - from horizon to zenith
|
||||
float horizonBlend = pow(1.0 - max(upAmount, 0.0), 2.0);
|
||||
vec3 zenithColor = vec3(0.15, 0.35, 0.75); // Deep blue at top
|
||||
vec3 horizonColor = vec3(0.55, 0.7, 0.9); // Light blue at horizon
|
||||
vec3 skyColor = mix(zenithColor, horizonColor, horizonBlend);
|
||||
|
||||
// Add warm glow near horizon
|
||||
float horizonGlow = pow(max(1.0 - abs(upAmount), 0.0), 6.0);
|
||||
skyColor += vec3(0.4, 0.25, 0.1) * horizonGlow * 0.4;
|
||||
|
||||
// Sun direction already in correct coordinate system
|
||||
vec3 sunDir = normalize(uSunDirection);
|
||||
float sunAngle = max(dot(rayDir, sunDir), 0.0);
|
||||
|
||||
// Sun disk
|
||||
float sunDisk = smoothstep(0.9993, 0.9998, sunAngle);
|
||||
vec3 sunColor = vec3(1.0, 0.95, 0.85);
|
||||
|
||||
// Sun glow
|
||||
float sunGlow = pow(sunAngle, 48.0) * 0.6;
|
||||
float sunHalo = pow(sunAngle, 6.0) * 0.25;
|
||||
|
||||
// Combine sun effects
|
||||
skyColor += sunColor * sunDisk * 3.0;
|
||||
skyColor += vec3(1.0, 0.85, 0.5) * sunGlow;
|
||||
skyColor += vec3(1.0, 0.9, 0.7) * sunHalo;
|
||||
|
||||
// Below horizon - fade to darker color
|
||||
if (upAmount < 0.0) {
|
||||
float depth = -upAmount;
|
||||
vec3 deepColor = vec3(0.02, 0.08, 0.15);
|
||||
skyColor = mix(horizonColor * 0.7, deepColor, smoothstep(0.0, 0.5, depth));
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(skyColor, 1.0);
|
||||
}
|
||||
</script>
|
||||
<script id="sky-vs" type="x-shader/x-vertex">
|
||||
@@ -499,14 +541,15 @@
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
uniform mat4 testModel;
|
||||
|
||||
varying vec3 fragPos;
|
||||
varying vec3 v_rayDir;
|
||||
|
||||
void main(void) {
|
||||
gl_PointSize = 10.;
|
||||
gl_Position = projection * mat4(mat3(view)) * vec4(positionAttr, 1.0);
|
||||
fragPos = (view * vec4(positionAttr,1.0)).xyz; //This is wrong probably
|
||||
v_rayDir = positionAttr;
|
||||
// Remove translation from view matrix for skybox
|
||||
mat4 rotView = mat4(mat3(view));
|
||||
vec4 pos = projection * rotView * vec4(positionAttr, 1.0);
|
||||
gl_Position = pos;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
@@ -25,6 +25,7 @@ export class Grid {
|
||||
for (let j = 0; j <= this.size; ++j) {
|
||||
for (let i = 0; i <= this.size; ++i) {
|
||||
// Generate Vertices normalized to 0-1, then scale and offset
|
||||
// Grid is on XY plane (horizontal), Z is up
|
||||
const u = i / this.size;
|
||||
const v = j / this.size;
|
||||
const x = (u - 0.5) * this.scale + this.offsetX;
|
||||
|
||||
85
src/Skybox.ts
Normal file
85
src/Skybox.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/** Skybox cube for rendering the sky */
|
||||
export class Skybox {
|
||||
private vao: WebGLVertexArrayObject | null = null;
|
||||
private vbo: WebGLBuffer | null = null;
|
||||
private indexCount: number = 0;
|
||||
|
||||
constructor() {}
|
||||
|
||||
initVAO(gl: WebGL2RenderingContext): void {
|
||||
// Cube vertices - positions only
|
||||
const vertices = new Float32Array([
|
||||
// Front face
|
||||
-1, -1, 1,
|
||||
1, -1, 1,
|
||||
1, 1, 1,
|
||||
-1, 1, 1,
|
||||
// Back face
|
||||
-1, -1, -1,
|
||||
-1, 1, -1,
|
||||
1, 1, -1,
|
||||
1, -1, -1,
|
||||
// Top face
|
||||
-1, 1, -1,
|
||||
-1, 1, 1,
|
||||
1, 1, 1,
|
||||
1, 1, -1,
|
||||
// Bottom face
|
||||
-1, -1, -1,
|
||||
1, -1, -1,
|
||||
1, -1, 1,
|
||||
-1, -1, 1,
|
||||
// Right face
|
||||
1, -1, -1,
|
||||
1, 1, -1,
|
||||
1, 1, 1,
|
||||
1, -1, 1,
|
||||
// Left face
|
||||
-1, -1, -1,
|
||||
-1, -1, 1,
|
||||
-1, 1, 1,
|
||||
-1, 1, -1,
|
||||
]);
|
||||
|
||||
const indices = new Uint16Array([
|
||||
0, 2, 1, 0, 3, 2, // front
|
||||
4, 6, 5, 4, 7, 6, // back
|
||||
8, 10, 9, 8, 11, 10, // top
|
||||
12, 14, 13, 12, 15, 14, // bottom
|
||||
16, 18, 17, 16, 19, 18, // right
|
||||
20, 22, 21, 20, 23, 22, // left
|
||||
]);
|
||||
|
||||
this.indexCount = indices.length;
|
||||
|
||||
this.vao = gl.createVertexArray();
|
||||
gl.bindVertexArray(this.vao);
|
||||
|
||||
this.vbo = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
|
||||
const ibo = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
||||
|
||||
// Position attribute
|
||||
gl.enableVertexAttribArray(0);
|
||||
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindVertexArray(null);
|
||||
}
|
||||
|
||||
draw(gl: WebGL2RenderingContext): void {
|
||||
if (!this.vao) return;
|
||||
|
||||
// Disable face culling for skybox (we're inside the cube)
|
||||
gl.disable(gl.CULL_FACE);
|
||||
|
||||
gl.bindVertexArray(this.vao);
|
||||
gl.drawElements(gl.TRIANGLES, this.indexCount, gl.UNSIGNED_SHORT, 0);
|
||||
gl.bindVertexArray(null);
|
||||
|
||||
gl.enable(gl.CULL_FACE);
|
||||
}
|
||||
}
|
||||
35
src/main.ts
35
src/main.ts
@@ -1,6 +1,7 @@
|
||||
import { vec3, mat4 } from 'gl-matrix';
|
||||
import { Camera } from './Camera';
|
||||
import { OceanLOD } from './OceanLOD';
|
||||
import { Skybox } from './Skybox';
|
||||
import { createProgram } from './Shader';
|
||||
import * as Config from './constants';
|
||||
|
||||
@@ -89,10 +90,12 @@ function initGeometry() {
|
||||
var perlinNoiseProgram: WebGLProgram | null;
|
||||
var defaultProgram: WebGLProgram | null;
|
||||
var textureProgram: WebGLProgram | null;
|
||||
var skyProgram: WebGLProgram | null;
|
||||
function initShaders() {
|
||||
perlinNoiseProgram = createProgram(gl, "ndc-vs", "noise-fs", "Perlin Noise");
|
||||
defaultProgram = createProgram(gl, "default-vs", "default-fs", "Default");
|
||||
textureProgram = createProgram(gl, "texture-vs", "texture-fs", "Texture");
|
||||
skyProgram = createProgram(gl, "sky-vs", "sky-fs", "Sky");
|
||||
}
|
||||
|
||||
/** Init an FBO used for the first render pass / perlin noise */
|
||||
@@ -142,6 +145,7 @@ var keysPressed: Set<string> = new Set();
|
||||
/** Objects and states*/
|
||||
var camera: Camera;
|
||||
var oceanLOD: OceanLOD;
|
||||
var skybox: Skybox;
|
||||
var curRotX = Config.CAMERA_DEFAULT_ROT_X;
|
||||
var curRotY = Config.CAMERA_DEFAULT_ROT_Y;
|
||||
var wireframeMode = false;
|
||||
@@ -172,17 +176,18 @@ function drawScene() {
|
||||
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
|
||||
|
||||
// Sun direction (matches the one in ocean shader)
|
||||
const sunDirection = vec3.fromValues(0.3, 0.5, 0.8);
|
||||
vec3.normalize(sunDirection, sunDirection);
|
||||
|
||||
//--- Render pass -> Ocean with Gerstner wave displacement ---
|
||||
//--- Render pass -> Skybox first (no depth write) ---
|
||||
{
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null); //Bind default framebuffer
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
gl.viewport(0, 0, viewportWidth, viewportHeight);
|
||||
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
var projection = mat4.create();
|
||||
mat4.identity(projection);
|
||||
mat4.perspective(projection, Config.FOV, viewportWidth / viewportHeight, Config.NEAR_PLANE, Config.FAR_PLANE);
|
||||
|
||||
camera.setOffset(Config.CAMERA_DEFAULT_OFFSET + keyboardZoom);
|
||||
@@ -190,6 +195,22 @@ function drawScene() {
|
||||
camera.setRotationY((curRotY += mouseXVel * Config.MOUSE_SENSITIVITY + keyboardRotationY));
|
||||
var view = camera.getViewMatrix();
|
||||
|
||||
// Draw skybox first with depth test disabled (always behind everything)
|
||||
gl.depthMask(false);
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
gl.useProgram(skyProgram);
|
||||
|
||||
let sky_view_loc = gl.getUniformLocation(<WebGLProgram>skyProgram, "view");
|
||||
gl.uniformMatrix4fv(sky_view_loc, false, view);
|
||||
let sky_projection_loc = gl.getUniformLocation(<WebGLProgram>skyProgram, "projection");
|
||||
gl.uniformMatrix4fv(sky_projection_loc, false, projection);
|
||||
let sky_sun_loc = gl.getUniformLocation(<WebGLProgram>skyProgram, "uSunDirection");
|
||||
gl.uniform3fv(sky_sun_loc, sunDirection);
|
||||
|
||||
skybox.draw(gl);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.depthMask(true);
|
||||
|
||||
// Update LOD based on camera position and view direction
|
||||
oceanLOD.updateLOD(gl, camera.pos, camera.target);
|
||||
|
||||
@@ -346,6 +367,10 @@ function main() {
|
||||
oceanLOD = new OceanLOD();
|
||||
oceanLOD.initVAO(gl);
|
||||
console.log(`Ocean LOD initialized with ${oceanLOD.getGridCount()} patches`);
|
||||
|
||||
skybox = new Skybox();
|
||||
skybox.initVAO(gl);
|
||||
console.log('Skybox initialized');
|
||||
|
||||
camera = new Camera();
|
||||
//Check if any errors apeared during init.
|
||||
|
||||
Reference in New Issue
Block a user