187 lines
4.0 KiB
JavaScript
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;
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|