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; } ); } }