Files
WebGPU-FFT-Ocean-Demo/passes/OceanSolidRenderPass.js
2025-12-31 14:33:18 +01:00

187 lines
4.0 KiB
JavaScript

import { RenderPass } from "../framework/RenderPass.js";
import Shader from "../framework/WebGpu.js";
import Matrix4 from "../framework/Matrix4.js";
export class OceanSolidRenderPass extends RenderPass {
async create( ) {
this.shader = new Shader( this.device );
this.shader.topology = "triangle-list";
this.shader.setCanvas( this.pipeline.canvas );
this._configureCanvasContext();
await this.shader.setup( "shaders/ocean_render.wgsl" );
this.shader.setAttribute( "position", this.pipeline.memory.positions );
this.shader.setAttribute( "uv", this.pipeline.memory.uvs );
this.shader.setIndices( this.pipeline.memory.indices );
await this._loadBottomTexture();
}
async bindBuffers( ) {
const memory = this.pipeline.memory;
const oceanBlock = this.pipeline.getBlockByName( "ocean" );
const colPass = oceanBlock.getPass( "ColFFT" );
const heightBuffer = colPass.shader.getBuffer( "heightField" );
const viewMatrixData = this.pipeline.camera.getViewMatrix();
const projectionMatrixData = Matrix4.createProjectionMatrix( this.pipeline.camera, this.pipeline.canvas );
const viewProjectionMatrix = Matrix4.multiply( projectionMatrixData, viewMatrixData );
const cameraWorldMatrix = Matrix4.invert( viewMatrixData );
const cameraPosition = Matrix4.getColumn( cameraWorldMatrix, 3 );
await this.shader.setBuffer( "heightField", heightBuffer );
this.shader.setVariable( "renderParams", memory.renderParams );
this.shader.setVariable( "viewProjection", viewProjectionMatrix );
this.shader.setVariable( "cameraPosition", cameraPosition );
this.shader.createBindGroups();
}
async execute( ) {
await this.shader.renderToCanvas( this.shader.indexCount, 1 );
}
_configureCanvasContext( ) {
if ( this.pipeline.canvasConfigured ) {
return;
}
const context = this.pipeline.canvas.getContext( "webgpu" );
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure( {
device: this.device,
format: format,
alphaMode: "opaque"
} );
this.pipeline.canvasConfigured = true;
}
async _loadBottomTexture( ) {
if ( this._bottomTextureLoaded ) {
return;
}
if ( typeof document === "undefined" ) {
return;
}
const canvas = document.createElement( "canvas" );
const ctx = canvas.getContext( "2d" );
if ( !ctx ) {
return;
}
const colorImage = await this._loadImage( "resources/textures/ground/Ground093C_2K-PNG_Color.png" );
const heightImage = await this._loadImage( "resources/textures/heightmap/Terrain003_1K_Height512.png" );
// COLOR
canvas.width = colorImage.width;
canvas.height = colorImage.height;
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.drawImage( colorImage, 0, 0 );
let imageData = ctx.getImageData( 0, 0, canvas.width, canvas.height );
let pixels = new Uint8Array( imageData.data );
const colorTex = this.shader.createTextureFromData( canvas.width, canvas.height, pixels );
this.shader.setVariable( "bottomColorTex", colorTex );
// HEIGHTMAP
canvas.width = heightImage.width;
canvas.height = heightImage.height;
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.drawImage( heightImage, 0, 0 );
imageData = ctx.getImageData( 0, 0, canvas.width, canvas.height );
pixels = new Uint8Array( imageData.data );
const heightTex = this.shader.createTextureFromData( canvas.width, canvas.height, pixels );
this.shader.setVariable( "bottomHeightTex", heightTex );
const sampler = this.device.createSampler( {
minFilter: "linear",
magFilter: "linear",
mipmapFilter: "linear",
addressModeU: "repeat",
addressModeV: "repeat"
} );
this.shader.setVariable( "bottomSampler", sampler );
this._bottomTextureLoaded = true;
}
async _loadImage( url ) {
return await new Promise( function( resolve, reject ) {
const img = new Image( );
img.onload = function( ) { resolve( img ); };
img.onerror = function( event ) { reject( event ); };
img.src = url;
} );
}
}