first commit
This commit is contained in:
42
shaders/cube_render.wgsl
Normal file
42
shaders/cube_render.wgsl
Normal file
@@ -0,0 +1,42 @@
|
||||
@group(0) @binding(0) var<storage, read> viewProjection : array<f32>;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position : vec4<f32>,
|
||||
@location(0) color : vec3<f32>,
|
||||
};
|
||||
|
||||
fn loadViewProjection( ) -> mat4x4<f32> {
|
||||
|
||||
return mat4x4<f32>(
|
||||
vec4<f32>( viewProjection[ 0 ], viewProjection[ 1 ], viewProjection[ 2 ], viewProjection[ 3 ] ),
|
||||
vec4<f32>( viewProjection[ 4 ], viewProjection[ 5 ], viewProjection[ 6 ], viewProjection[ 7 ] ),
|
||||
vec4<f32>( viewProjection[ 8 ], viewProjection[ 9 ], viewProjection[ 10 ], viewProjection[ 11 ] ),
|
||||
vec4<f32>( viewProjection[ 12 ], viewProjection[ 13 ], viewProjection[ 14 ], viewProjection[ 15 ] )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn v_main(
|
||||
@location(0) position : vec3<f32>,
|
||||
@location(1) color : vec3<f32>
|
||||
) -> VertexOutput {
|
||||
|
||||
var output : VertexOutput;
|
||||
|
||||
let vp : mat4x4<f32> = loadViewProjection();
|
||||
|
||||
output.position = vp * vec4<f32>( position, 1.0 );
|
||||
|
||||
output.color = color;
|
||||
|
||||
return output;
|
||||
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn f_main( input : VertexOutput ) -> @location(0) vec4<f32> {
|
||||
|
||||
return vec4<f32>( input.color, 1.0 );
|
||||
|
||||
}
|
||||
94
shaders/fft_col.wgsl
Normal file
94
shaders/fft_col.wgsl
Normal file
@@ -0,0 +1,94 @@
|
||||
const PI2 : f32 = 6.28318530718;
|
||||
|
||||
@group(0) @binding(0) var<storage, read> inputReal : array<f32>;
|
||||
@group(0) @binding(1) var<storage, read> inputImag : array<f32>;
|
||||
@group(0) @binding(2) var<storage, read_write> outputReal : array<f32>;
|
||||
@group(0) @binding(3) var<storage, read_write> outputImag : array<f32>;
|
||||
@group(0) @binding(4) var<storage, read_write> heightField : array<f32>;
|
||||
@group(0) @binding(5) var<storage, read> params : array<f32>;
|
||||
|
||||
var<workgroup> wr : array<f32, 64>;
|
||||
var<workgroup> wi : array<f32, 64>;
|
||||
|
||||
fn bitReverse( x : u32, bits : u32 ) -> u32 {
|
||||
var n : u32 = x;
|
||||
var r : u32 = 0u;
|
||||
for ( var i : u32 = 0u; i < bits; i = i + 1u ) {
|
||||
r = ( r << 1u ) | ( n & 1u );
|
||||
n = n >> 1u;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@compute @workgroup_size( 64 )
|
||||
fn main( @builtin(local_invocation_id) lid : vec3<u32>,
|
||||
@builtin(workgroup_id) gid : vec3<u32> ) {
|
||||
|
||||
let N : u32 = u32( params[ 1u ] );
|
||||
|
||||
if ( N != 64u ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let col : u32 = gid.x;
|
||||
let row : u32 = lid.x;
|
||||
let idx : u32 = row * N + col;
|
||||
|
||||
let bits : u32 = 6u;
|
||||
let rev : u32 = bitReverse( row, bits );
|
||||
let srcIdx : u32 = rev * N + col;
|
||||
|
||||
wr[ row ] = inputReal[ srcIdx ];
|
||||
wi[ row ] = inputImag[ srcIdx ];
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
var step : u32 = 1u;
|
||||
while ( step < N ) {
|
||||
|
||||
let jump : u32 = step << 1u;
|
||||
let twiddleAngle : f32 = -PI2 / f32( jump );
|
||||
|
||||
let pairIndex : u32 = ( row / jump ) * jump + ( row % step );
|
||||
let matchIndex : u32 = pairIndex + step;
|
||||
|
||||
let k : u32 = row % step;
|
||||
let angle : f32 = twiddleAngle * f32( k );
|
||||
|
||||
let c : f32 = cos( angle );
|
||||
let s : f32 = sin( angle );
|
||||
|
||||
let er : f32 = wr[ pairIndex ];
|
||||
let ei : f32 = wi[ pairIndex ];
|
||||
|
||||
let or : f32 = wr[ matchIndex ];
|
||||
let oi : f32 = wi[ matchIndex ];
|
||||
|
||||
let tr : f32 = c * or - s * oi;
|
||||
let ti : f32 = s * or + c * oi;
|
||||
|
||||
if ( row % jump < step ) {
|
||||
wr[ pairIndex ] = er + tr;
|
||||
wi[ pairIndex ] = ei + ti;
|
||||
} else {
|
||||
wr[ matchIndex ] = er - tr;
|
||||
wi[ matchIndex ] = ei - ti;
|
||||
}
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
step = jump;
|
||||
}
|
||||
|
||||
let invScale : f32 = 1.0 / f32( N );
|
||||
|
||||
let realVal : f32 = wr[ row ] * invScale;
|
||||
let imagVal : f32 = wi[ row ] * invScale;
|
||||
|
||||
outputReal[ idx ] = realVal;
|
||||
outputImag[ idx ] = imagVal;
|
||||
|
||||
// final height is real part
|
||||
heightField[ idx ] = realVal;
|
||||
|
||||
}
|
||||
86
shaders/fft_row.wgsl
Normal file
86
shaders/fft_row.wgsl
Normal file
@@ -0,0 +1,86 @@
|
||||
const PI2 : f32 = 6.28318530718;
|
||||
|
||||
@group(0) @binding(0) var<storage, read> inputReal : array<f32>;
|
||||
@group(0) @binding(1) var<storage, read> inputImag : array<f32>;
|
||||
@group(0) @binding(2) var<storage, read_write> outputReal : array<f32>;
|
||||
@group(0) @binding(3) var<storage, read_write> outputImag : array<f32>;
|
||||
@group(0) @binding(4) var<storage, read> params : array<f32>;
|
||||
|
||||
var<workgroup> wr : array<f32, 64>;
|
||||
var<workgroup> wi : array<f32, 64>;
|
||||
|
||||
fn bitReverse( x : u32, bits : u32 ) -> u32 {
|
||||
var n : u32 = x;
|
||||
var r : u32 = 0u;
|
||||
for ( var i : u32 = 0u; i < bits; i = i + 1u ) {
|
||||
r = ( r << 1u ) | ( n & 1u );
|
||||
n = n >> 1u;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@compute @workgroup_size( 64 )
|
||||
fn main( @builtin(local_invocation_id) lid : vec3<u32>,
|
||||
@builtin(workgroup_id) gid : vec3<u32> ) {
|
||||
|
||||
let N : u32 = u32( params[ 1u ] );
|
||||
|
||||
if ( N != 64u ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let row : u32 = gid.x;
|
||||
let col : u32 = lid.x;
|
||||
let idx : u32 = row * N + col;
|
||||
|
||||
let bits : u32 = 6u;
|
||||
let rev : u32 = bitReverse( col, bits );
|
||||
let srcIdx : u32 = row * N + rev;
|
||||
|
||||
wr[ col ] = inputReal[ srcIdx ];
|
||||
wi[ col ] = inputImag[ srcIdx ];
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
var step : u32 = 1u;
|
||||
while ( step < N ) {
|
||||
|
||||
let jump : u32 = step << 1u;
|
||||
let twiddleAngle : f32 = -PI2 / f32( jump );
|
||||
|
||||
let pairIndex : u32 = ( col / jump ) * jump + ( col % step );
|
||||
let matchIndex : u32 = pairIndex + step;
|
||||
|
||||
let k : u32 = col % step;
|
||||
let angle : f32 = twiddleAngle * f32( k );
|
||||
|
||||
let c : f32 = cos( angle );
|
||||
let s : f32 = sin( angle );
|
||||
|
||||
let er : f32 = wr[ pairIndex ];
|
||||
let ei : f32 = wi[ pairIndex ];
|
||||
|
||||
let or : f32 = wr[ matchIndex ];
|
||||
let oi : f32 = wi[ matchIndex ];
|
||||
|
||||
let tr : f32 = c * or - s * oi;
|
||||
let ti : f32 = s * or + c * oi;
|
||||
|
||||
if ( col % jump < step ) {
|
||||
wr[ pairIndex ] = er + tr;
|
||||
wi[ pairIndex ] = ei + ti;
|
||||
} else {
|
||||
wr[ matchIndex ] = er - tr;
|
||||
wi[ matchIndex ] = ei - ti;
|
||||
}
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
step = jump;
|
||||
}
|
||||
|
||||
let invScale : f32 = 1.0 / f32( N );
|
||||
outputReal[ idx ] = wr[ col ] * invScale;
|
||||
outputImag[ idx ] = wi[ col ] * invScale;
|
||||
|
||||
}
|
||||
304
shaders/ocean_render.wgsl
Normal file
304
shaders/ocean_render.wgsl
Normal file
@@ -0,0 +1,304 @@
|
||||
@group(0) @binding(0) var<storage, read> heightField : array<f32>;
|
||||
@group(0) @binding(1) var<storage, read> renderParams : array<f32>;
|
||||
@group(0) @binding(2) var<storage, read> viewProjection : array<f32>;
|
||||
@group(0) @binding(3) var<storage, read> cameraPosition : array<f32>;
|
||||
@group(0) @binding(4) var bottomColorTex : texture_2d<f32>;
|
||||
@group(0) @binding(5) var bottomHeightTex : texture_2d<f32>;
|
||||
@group(0) @binding(6) var bottomSampler : sampler;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position : vec4<f32>,
|
||||
@location(0) worldPosition : vec3<f32>,
|
||||
@location(1) normal : vec3<f32>,
|
||||
@location(2) heightValue : f32,
|
||||
};
|
||||
|
||||
fn indexFromCoord( x : u32, y : u32, size : u32 ) -> u32 {
|
||||
|
||||
return y * size + x;
|
||||
|
||||
}
|
||||
|
||||
fn loadViewProjection( ) -> mat4x4<f32> {
|
||||
|
||||
return mat4x4<f32>(
|
||||
vec4<f32>( viewProjection[ 0 ], viewProjection[ 1 ], viewProjection[ 2 ], viewProjection[ 3 ] ),
|
||||
vec4<f32>( viewProjection[ 4 ], viewProjection[ 5 ], viewProjection[ 6 ], viewProjection[ 7 ] ),
|
||||
vec4<f32>( viewProjection[ 8 ], viewProjection[ 9 ], viewProjection[ 10 ], viewProjection[ 11 ] ),
|
||||
vec4<f32>( viewProjection[ 12 ], viewProjection[ 13 ], viewProjection[ 14 ], viewProjection[ 15 ] )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
fn sampleHeight( fx : f32, fy : f32, simSize : u32, heightAmp : f32 ) -> f32 {
|
||||
|
||||
let sx : f32 = clamp( fx, 0.0, f32( simSize - 1u ) );
|
||||
let sy : f32 = clamp( fy, 0.0, f32( simSize - 1u ) );
|
||||
|
||||
let ix0 : u32 = u32( sx );
|
||||
let iy0 : u32 = u32( sy );
|
||||
|
||||
let ix1 : u32 = min( simSize - 1u, ix0 + 1u );
|
||||
let iy1 : u32 = min( simSize - 1u, iy0 + 1u );
|
||||
|
||||
let tx : f32 = sx - f32( ix0 );
|
||||
let ty : f32 = sy - f32( iy0 );
|
||||
|
||||
let idx00 : u32 = indexFromCoord( ix0, iy0, simSize );
|
||||
let idx10 : u32 = indexFromCoord( ix1, iy0, simSize );
|
||||
let idx01 : u32 = indexFromCoord( ix0, iy1, simSize );
|
||||
let idx11 : u32 = indexFromCoord( ix1, iy1, simSize );
|
||||
|
||||
let parity00 : u32 = ( ix0 + iy0 ) & 1u;
|
||||
let parity10 : u32 = ( ix1 + iy0 ) & 1u;
|
||||
let parity01 : u32 = ( ix0 + iy1 ) & 1u;
|
||||
let parity11 : u32 = ( ix1 + iy1 ) & 1u;
|
||||
|
||||
var sign00 : f32 = 1.0;
|
||||
var sign10 : f32 = 1.0;
|
||||
var sign01 : f32 = 1.0;
|
||||
var sign11 : f32 = 1.0;
|
||||
|
||||
if ( parity00 == 1u ) { sign00 = -1.0; }
|
||||
if ( parity10 == 1u ) { sign10 = -1.0; }
|
||||
if ( parity01 == 1u ) { sign01 = -1.0; }
|
||||
if ( parity11 == 1u ) { sign11 = -1.0; }
|
||||
|
||||
let h00 : f32 = heightField[ idx00 ] * heightAmp * sign00;
|
||||
let h10 : f32 = heightField[ idx10 ] * heightAmp * sign10;
|
||||
let h01 : f32 = heightField[ idx01 ] * heightAmp * sign01;
|
||||
let h11 : f32 = heightField[ idx11 ] * heightAmp * sign11;
|
||||
|
||||
let hx0 : f32 = mix( h00, h10, tx );
|
||||
let hx1 : f32 = mix( h01, h11, tx );
|
||||
|
||||
return mix( hx0, hx1, ty );
|
||||
|
||||
}
|
||||
|
||||
|
||||
@vertex
|
||||
fn v_main(
|
||||
@location(0) position : vec3<f32>,
|
||||
@location(1) uv : vec2<f32>,
|
||||
@builtin(instance_index) instanceIndex : u32
|
||||
) -> VertexOutput {
|
||||
|
||||
let simSize : u32 = u32( renderParams[ 0 ] );
|
||||
let meshSize : f32 = renderParams[ 1 ];
|
||||
let heightAmp : f32 = renderParams[ 2 ];
|
||||
let meshRes : u32 = u32( renderParams[ 4 ] );
|
||||
|
||||
// Tiling amount (slider 1..N). We clamp to at least 1.
|
||||
let tilingF : f32 = max( 1.0, renderParams[ 5 ] );
|
||||
let tiling : u32 = max( 1u, u32( tilingF + 0.5 ) );
|
||||
|
||||
let tileRange : u32 = tiling - 1u;
|
||||
let tilesPerRow : u32 = tileRange * 2u + 1u;
|
||||
|
||||
let tileIndex : u32 = instanceIndex;
|
||||
|
||||
let tileXIndex : u32 = tileIndex % tilesPerRow;
|
||||
let tileZIndex : u32 = tileIndex / tilesPerRow;
|
||||
|
||||
let offsetGridX : i32 = i32( tileXIndex ) - i32( tileRange );
|
||||
let offsetGridZ : i32 = i32( tileZIndex ) - i32( tileRange );
|
||||
|
||||
let offsetX : f32 = f32( offsetGridX ) * meshSize;
|
||||
let offsetZ : f32 = f32( offsetGridZ ) * meshSize;
|
||||
|
||||
let denom : f32 = max( 1.0, f32( meshRes - 1u ) );
|
||||
let fxNorm : f32 = clamp( uv.x / denom, 0.0, 1.0 );
|
||||
let fyNorm : f32 = clamp( uv.y / denom, 0.0, 1.0 );
|
||||
|
||||
let fx : f32 = fxNorm * f32( simSize - 1u );
|
||||
let fy : f32 = fyNorm * f32( simSize - 1u );
|
||||
|
||||
let hCenter : f32 = sampleHeight( fx, fy, simSize, heightAmp );
|
||||
|
||||
let worldX : f32 = ( fxNorm - 0.5 ) * meshSize + offsetX;
|
||||
let worldZ : f32 = ( fyNorm - 0.5 ) * meshSize + offsetZ;
|
||||
|
||||
// Derivatives via central differences on the sampled height field
|
||||
let fxStep : f32 = 1.0;
|
||||
let fyStep : f32 = 1.0;
|
||||
|
||||
let hXp : f32 = sampleHeight( fx + fxStep, fy, simSize, heightAmp );
|
||||
let hXm : f32 = sampleHeight( fx - fxStep, fy, simSize, heightAmp );
|
||||
let hYp : f32 = sampleHeight( fx, fy + fyStep, simSize, heightAmp );
|
||||
let hYm : f32 = sampleHeight( fx, fy - fyStep, simSize, heightAmp );
|
||||
|
||||
let h : f32 = hCenter;
|
||||
|
||||
let dx : f32 = hXp - hXm;
|
||||
let dz : f32 = hYp - hYm;
|
||||
|
||||
let normal : vec3<f32> = normalize( vec3<f32>( -dx, 2.0, -dz ) );
|
||||
|
||||
let vp : mat4x4<f32> = loadViewProjection();
|
||||
|
||||
var output : VertexOutput;
|
||||
output.position = vp * vec4<f32>( worldX, h, worldZ, 1.0 );
|
||||
output.worldPosition = vec3<f32>( worldX, h, worldZ );
|
||||
output.normal = normal;
|
||||
output.heightValue = h;
|
||||
|
||||
return output;
|
||||
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn f_main( input : VertexOutput ) -> @location(0) vec4<f32> {
|
||||
|
||||
let mode : u32 = u32( renderParams[ 3 ] );
|
||||
|
||||
let N : vec3<f32> = normalize( input.normal );
|
||||
|
||||
// Debug: visualize normals
|
||||
if ( mode == 1u ) {
|
||||
let normalColor : vec3<f32> = 0.5 * ( N + vec3<f32>( 1.0, 1.0, 1.0 ) );
|
||||
return vec4<f32>( normalColor, 1.0 );
|
||||
}
|
||||
|
||||
// Debug: true solid color (no height variation)
|
||||
if ( mode == 2u ) {
|
||||
let flatColor : vec3<f32> = vec3<f32>( 0.05, 0.4, 0.8 );
|
||||
return vec4<f32>( flatColor, 1.0 );
|
||||
}
|
||||
|
||||
// Debug: visualize height field as grayscale
|
||||
if ( mode == 3u ) {
|
||||
// Scale and bias height into a visible range
|
||||
let h : f32 = input.heightValue;
|
||||
let hNorm : f32 = clamp( h * 0.05 + 0.5, 0.0, 1.0 );
|
||||
let c : vec3<f32> = vec3<f32>( hNorm, hNorm, hNorm );
|
||||
return vec4<f32>( c, 1.0 );
|
||||
}
|
||||
|
||||
// Sun / light setup
|
||||
let lightDir : vec3<f32> = normalize( vec3<f32>( 0.2, 0.85, 0.35 ) );
|
||||
let sunColor : vec3<f32> = vec3<f32>( 1.0, 0.97, 0.9 );
|
||||
|
||||
// Camera + view
|
||||
let camPos : vec3<f32> = vec3<f32>( cameraPosition[ 0 ], cameraPosition[ 1 ], cameraPosition[ 2 ] );
|
||||
let viewDir : vec3<f32> = normalize( camPos - input.worldPosition );
|
||||
|
||||
// Normal and basic terms
|
||||
let NdotL : f32 = max( dot( N, lightDir ), 0.0 );
|
||||
let NdotV : f32 = max( dot( N, viewDir ), 0.0 );
|
||||
|
||||
// Water body base color
|
||||
let waterBase : vec3<f32> = vec3<f32>( 0.01, 0.18, 0.55 );
|
||||
|
||||
// Realistic mode: reuse simple lighting but with sand/depth-based underwater color
|
||||
if ( mode == 4u ) {
|
||||
|
||||
let meshSize : f32 = renderParams[ 1 ];
|
||||
let uvBase : vec2<f32> = input.worldPosition.xz / meshSize + vec2<f32>( 0.5, 0.5 );
|
||||
let noise : vec2<f32> = vec2<f32>(
|
||||
sin( input.worldPosition.x * 0.05 + input.worldPosition.z * 0.12 ),
|
||||
cos( input.worldPosition.x * 0.04 - input.worldPosition.z * 0.09 )
|
||||
) * 0.03;
|
||||
let uvMain : vec2<f32> = uvBase + noise;
|
||||
let uvAlt : vec2<f32> = uvBase + vec2<f32>( 0.25, 0.43 ) + noise * 0.8;
|
||||
let colorSample : vec4<f32> = textureSample( bottomColorTex, bottomSampler, uvMain );
|
||||
let colorAlt : vec4<f32> = textureSample( bottomColorTex, bottomSampler, uvAlt );
|
||||
let finalColor : vec3<f32> = mix( colorSample.rgb, colorAlt.rgb, 0.38 );
|
||||
let heightSample: vec4<f32> = textureSample( bottomHeightTex, bottomSampler, uvMain );
|
||||
|
||||
let heightAlt : vec4<f32> = textureSample( bottomHeightTex, bottomSampler, uvAlt );
|
||||
|
||||
let heightVal : f32 = mix( heightSample.r, heightAlt.r, 0.4 );
|
||||
|
||||
let baseColor : vec3<f32> = finalColor;
|
||||
|
||||
let poolY : f32 = -6.0;
|
||||
let depthGeom : f32 = clamp( poolY - input.worldPosition.y, 0.0, 20.0 );
|
||||
|
||||
let shallowT : f32 = clamp( ( heightVal - 0.4 ) / 0.6, 0.0, 1.0 );
|
||||
|
||||
let depthT : f32 = clamp( depthGeom / 8.0, 0.0, 1.0 );
|
||||
|
||||
let sandWeight : f32 = shallowT * ( 1.0 - depthT );
|
||||
let waterWeight : f32 = 1.0 - sandWeight;
|
||||
|
||||
let sandColor : vec3<f32> = baseColor;
|
||||
let wetColor : vec3<f32> = mix( baseColor, waterBase, 0.6 );
|
||||
|
||||
let bottomColor : vec3<f32> = sandColor * sandWeight + wetColor * waterWeight;
|
||||
|
||||
let absorbCoeff : vec3<f32> = vec3<f32>( 0.04, 0.09, 0.16 );
|
||||
let absorb : vec3<f32> = exp( -absorbCoeff * depthGeom );
|
||||
|
||||
let bodyColor : vec3<f32> = bottomColor * absorb;
|
||||
|
||||
let up : vec3<f32> = vec3<f32>( 0.0, 1.0, 0.0 );
|
||||
let reflDirEnv : vec3<f32> = normalize( reflect( -viewDir, N ) );
|
||||
let horizonT : f32 = clamp( pow( 1.0 - max( dot( reflDirEnv, up ), 0.0 ), 1.5 ), 0.0, 1.0 );
|
||||
let skyZenith : vec3<f32> = vec3<f32>( 0.02, 0.12, 0.28 );
|
||||
let skyHorizon : vec3<f32> = vec3<f32>( 0.30, 0.45, 0.70 );
|
||||
let skyAnalytic : vec3<f32> = mix( skyZenith, skyHorizon, horizonT );
|
||||
let skyMask : vec3<f32> = vec3<f32>( 0.08, 0.18, 0.32 );
|
||||
let skyColor : vec3<f32> = mix( skyAnalytic, skyMask, 0.4 );
|
||||
|
||||
let halfDir : vec3<f32> = normalize( lightDir + viewDir );
|
||||
let NdotH : f32 = max( dot( N, halfDir ), 0.0 );
|
||||
let spec : f32 = pow( NdotH, 140.0 ) * NdotL;
|
||||
let specTerm : vec3<f32> = spec * vec3<f32>( 1.0, 1.0, 1.0 ) * 1.3;
|
||||
|
||||
let F0 : vec3<f32> = vec3<f32>( 0.02, 0.025, 0.03 );
|
||||
let oneMinus : f32 = 1.0 - NdotV;
|
||||
let fresnel : vec3<f32> = F0 + ( vec3<f32>( 1.0, 1.0, 1.0 ) - F0 ) * pow( oneMinus, 5.0 );
|
||||
|
||||
let diffuse : vec3<f32> = bodyColor * ( 0.15 + 0.85 * NdotL ) * sunColor;
|
||||
|
||||
let envBlend : vec3<f32> = mix( skyColor, vec3<f32>( 0.08, 0.25, 0.55 ), 0.35 );
|
||||
let reflection : vec3<f32> = envBlend + specTerm;
|
||||
|
||||
let color : vec3<f32> = diffuse * ( vec3<f32>( 1.0, 1.0, 1.0 ) - fresnel ) + reflection * fresnel;
|
||||
|
||||
let colorGamma : vec3<f32> = pow( clamp( color, vec3<f32>( 0.0, 0.0, 0.0 ), vec3<f32>( 1.0, 1.0, 1.0 ) ), vec3<f32>( 1.0 / 2.2, 1.0 / 2.2, 1.0 / 2.2 ) );
|
||||
|
||||
return vec4<f32>( colorGamma, 1.0 );
|
||||
|
||||
}
|
||||
|
||||
// Simple sky / environment tint (towards horizon)
|
||||
let skyZenith : vec3<f32> = vec3<f32>( 0.02, 0.12, 0.28 );
|
||||
let skyHorizon : vec3<f32> = vec3<f32>( 0.30, 0.45, 0.70 );
|
||||
|
||||
// Reflection direction for environment lookup
|
||||
let reflDirEnv : vec3<f32> = normalize( reflect( -viewDir, N ) );
|
||||
let up : vec3<f32> = vec3<f32>( 0.0, 1.0, 0.0 );
|
||||
let horizonT : f32 = clamp( pow( 1.0 - max( dot( reflDirEnv, up ), 0.0 ), 1.5 ), 0.0, 1.0 );
|
||||
let skyAnalytic : vec3<f32> = mix( skyZenith, skyHorizon, horizonT );
|
||||
let skyMask : vec3<f32> = vec3<f32>( 0.08, 0.18, 0.32 );
|
||||
let skyColor : vec3<f32> = mix( skyAnalytic, skyMask, 0.4 );
|
||||
|
||||
// Specular highlight using Blinn-Phong
|
||||
let halfDir : vec3<f32> = normalize( lightDir + viewDir );
|
||||
let NdotH : f32 = max( dot( N, halfDir ), 0.0 );
|
||||
let spec : f32 = pow( NdotH, 140.0 ) * NdotL;
|
||||
let specTerm : vec3<f32> = spec * vec3<f32>( 1.0, 1.0, 1.0 ) * 1.3;
|
||||
|
||||
// Fresnel using Schlick's approximation (F0 ~ 0.02 for water)
|
||||
let F0 : vec3<f32> = vec3<f32>( 0.02, 0.025, 0.03 );
|
||||
let oneMinus : f32 = 1.0 - NdotV;
|
||||
let fresnel : vec3<f32> = F0 + ( vec3<f32>( 1.0, 1.0, 1.0 ) - F0 ) * pow( oneMinus, 5.0 );
|
||||
|
||||
// Diffuse-ish underwater term (absorbed light)
|
||||
let diffuse : vec3<f32> = waterBase * ( 0.15 + 0.85 * NdotL ) * sunColor;
|
||||
|
||||
// Reflected environment (sky + sun highlight)
|
||||
let envBlend : vec3<f32> = mix( skyColor, vec3<f32>( 0.08, 0.25, 0.55 ), 0.35 );
|
||||
let reflection : vec3<f32> = envBlend + specTerm;
|
||||
|
||||
// Mix refraction (underwater) and reflection via Fresnel
|
||||
let color : vec3<f32> = diffuse * ( vec3<f32>( 1.0, 1.0, 1.0 ) - fresnel ) + reflection * fresnel;
|
||||
|
||||
// Mild contrast to keep it punchy
|
||||
let colorGamma : vec3<f32> = pow( clamp( color, vec3<f32>( 0.0, 0.0, 0.0 ), vec3<f32>( 1.0, 1.0, 1.0 ) ), vec3<f32>( 1.0 / 2.2, 1.0 / 2.2, 1.0 / 2.2 ) );
|
||||
|
||||
return vec4<f32>( colorGamma, 1.0 );
|
||||
|
||||
}
|
||||
93
shaders/ocean_spectrum.wgsl
Normal file
93
shaders/ocean_spectrum.wgsl
Normal file
@@ -0,0 +1,93 @@
|
||||
const TWO_PI : f32 = 6.28318530718;
|
||||
const GRAVITY : f32 = 9.81;
|
||||
|
||||
@group(0) @binding(0) var<storage, read> h0Real : array<f32>;
|
||||
@group(0) @binding(1) var<storage, read> h0Imag : array<f32>;
|
||||
@group(0) @binding(2) var<storage, read_write> spectrumReal : array<f32>;
|
||||
@group(0) @binding(3) var<storage, read_write> spectrumImag : array<f32>;
|
||||
@group(0) @binding(4) var<storage, read> params : array<f32>;
|
||||
|
||||
fn indexFromCoord( x : u32, y : u32, size : u32 ) -> u32 {
|
||||
|
||||
return y * size + x;
|
||||
|
||||
}
|
||||
|
||||
fn mirrorIndex( x : u32, y : u32, size : u32 ) -> u32 {
|
||||
|
||||
let mx : u32 = ( size - x ) % size;
|
||||
let my : u32 = ( size - y ) % size;
|
||||
|
||||
return indexFromCoord( mx, my, size );
|
||||
|
||||
}
|
||||
|
||||
@compute @workgroup_size( 8, 8 )
|
||||
fn main( @builtin(global_invocation_id) gid : vec3<u32> ) {
|
||||
|
||||
let size : u32 = u32( params[ 1u ] );
|
||||
|
||||
if ( gid.x >= size || gid.y >= size ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nFloat : f32 = f32( size );
|
||||
let xPos : f32 = f32( gid.x );
|
||||
let yPos : f32 = f32( gid.y );
|
||||
|
||||
var realValue : f32 = 0.0;
|
||||
var imagValue : f32 = 0.0;
|
||||
|
||||
for ( var ky : u32 = 0u; ky < size; ky = ky + 1u ) {
|
||||
|
||||
let kyShift : f32 = f32( i32( ky ) - i32( size ) / 2 );
|
||||
|
||||
for ( var kx : u32 = 0u; kx < size; kx = kx + 1u ) {
|
||||
|
||||
let kxShift : f32 = f32( i32( kx ) - i32( size ) / 2 );
|
||||
|
||||
let idx : u32 = indexFromCoord( kx, ky, size );
|
||||
|
||||
let mirrorIdx : u32 = mirrorIndex( kx, ky, size );
|
||||
|
||||
let kLength : f32 = sqrt( kxShift * kxShift + kyShift * kyShift );
|
||||
|
||||
if ( kLength == 0.0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let h0R : f32 = h0Real[ idx ];
|
||||
let h0I : f32 = h0Imag[ idx ];
|
||||
|
||||
let h0mR : f32 = h0Real[ mirrorIdx ];
|
||||
let h0mI : f32 = -h0Imag[ mirrorIdx ]; // conjugate
|
||||
|
||||
let omega : f32 = sqrt( GRAVITY * kLength );
|
||||
|
||||
let phase : f32 =
|
||||
omega * params[ 0u ] +
|
||||
( kxShift * TWO_PI * xPos / nFloat ) +
|
||||
( kyShift * TWO_PI * yPos / nFloat );
|
||||
|
||||
let sinP : f32 = sin( phase );
|
||||
let cosP : f32 = cos( phase );
|
||||
|
||||
let hPos : f32 = h0R * cosP - h0I * sinP;
|
||||
|
||||
let hNeg : f32 = h0mR * cosP + h0mI * sinP;
|
||||
|
||||
let hPosI : f32 = h0R * sinP + h0I * cosP;
|
||||
let hNegI : f32 = h0mR * sinP - h0mI * cosP;
|
||||
|
||||
realValue = realValue + hPos + hNeg;
|
||||
imagValue = imagValue + hPosI + hNegI;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let idxOut : u32 = indexFromCoord( gid.x, gid.y, size );
|
||||
|
||||
spectrumReal[ idxOut ] = realValue * params[ 2u ];
|
||||
spectrumImag[ idxOut ] = imagValue * params[ 2u ];
|
||||
|
||||
}
|
||||
80
shaders/ocean_spectrum_new.wgsl
Normal file
80
shaders/ocean_spectrum_new.wgsl
Normal file
@@ -0,0 +1,80 @@
|
||||
const GRAVITY : f32 = 9.81;
|
||||
|
||||
@group(0) @binding(0) var<storage, read> h0Real : array<f32>;
|
||||
@group(0) @binding(1) var<storage, read> h0Imag : array<f32>;
|
||||
@group(0) @binding(2) var<storage, read_write> spectrumReal : array<f32>;
|
||||
@group(0) @binding(3) var<storage, read_write> spectrumImag : array<f32>;
|
||||
@group(0) @binding(4) var<storage, read> params : array<f32>;
|
||||
|
||||
fn indexFromCoord( x : u32, y : u32, size : u32 ) -> u32 {
|
||||
|
||||
return y * size + x;
|
||||
|
||||
}
|
||||
|
||||
fn mirrorIndex( x : u32, y : u32, size : u32 ) -> u32 {
|
||||
|
||||
let mx : u32 = ( size - x ) % size;
|
||||
let my : u32 = ( size - y ) % size;
|
||||
|
||||
return indexFromCoord( mx, my, size );
|
||||
|
||||
}
|
||||
|
||||
@compute @workgroup_size( 8, 8 )
|
||||
fn main( @builtin(global_invocation_id) gid : vec3<u32> ) {
|
||||
|
||||
let size : u32 = u32( params[ 1u ] );
|
||||
|
||||
if ( gid.x >= size || gid.y >= size ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let kx : u32 = gid.x;
|
||||
let ky : u32 = gid.y;
|
||||
|
||||
let kxShift : f32 = f32( i32( kx ) - i32( size ) / 2 );
|
||||
let kyShift : f32 = f32( i32( ky ) - i32( size ) / 2 );
|
||||
|
||||
let kLength : f32 = sqrt( kxShift * kxShift + kyShift * kyShift );
|
||||
|
||||
let idx : u32 = indexFromCoord( kx, ky, size );
|
||||
|
||||
if ( kLength == 0.0 ) {
|
||||
spectrumReal[ idx ] = 0.0;
|
||||
spectrumImag[ idx ] = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
let mirrorIdx : u32 = mirrorIndex( kx, ky, size );
|
||||
|
||||
let h0R : f32 = h0Real[ idx ];
|
||||
let h0I : f32 = h0Imag[ idx ];
|
||||
|
||||
let h0mR : f32 = h0Real[ mirrorIdx ];
|
||||
let h0mI : f32 = h0Imag[ mirrorIdx ];
|
||||
|
||||
let omega : f32 = sqrt( GRAVITY * kLength );
|
||||
let time : f32 = params[ 0u ];
|
||||
let heightA : f32 = params[ 2u ];
|
||||
|
||||
let theta : f32 = omega * time;
|
||||
|
||||
let cosT : f32 = cos( theta );
|
||||
let sinT : f32 = sin( theta );
|
||||
|
||||
// h0(k) * e^{ i * omega * t }
|
||||
let hPosR : f32 = h0R * cosT - h0I * sinT;
|
||||
let hPosI : f32 = h0R * sinT + h0I * cosT;
|
||||
|
||||
// h0(-k) * e^{ -i * omega * t }
|
||||
let hNegR : f32 = h0mR * cosT + h0mI * sinT;
|
||||
let hNegI : f32 = h0mI * cosT - h0mR * sinT;
|
||||
|
||||
let outR : f32 = ( hPosR + hNegR ) * heightA;
|
||||
let outI : f32 = ( hPosI + hNegI ) * heightA;
|
||||
|
||||
spectrumReal[ idx ] = outR;
|
||||
spectrumImag[ idx ] = outI;
|
||||
|
||||
}
|
||||
74
shaders/sky_sphere.wgsl
Normal file
74
shaders/sky_sphere.wgsl
Normal file
@@ -0,0 +1,74 @@
|
||||
@group(0) @binding(0) var<uniform> viewProjectionMatrix : mat4x4<f32>;
|
||||
@group(0) @binding(1) var<uniform> cameraPosition : vec3<f32>;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position : vec4<f32>,
|
||||
@location(0) worldPosition : vec3<f32>,
|
||||
@location(1) worldNormal : vec3<f32>,
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn v_main(
|
||||
@location(0) position : vec3<f32>,
|
||||
@location(1) normal : vec3<f32>
|
||||
) -> VertexOutput {
|
||||
|
||||
var output : VertexOutput;
|
||||
|
||||
output.position = viewProjectionMatrix * vec4<f32>( position, 1.0 );
|
||||
output.worldPosition = position;
|
||||
output.worldNormal = normalize( normal );
|
||||
|
||||
return output;
|
||||
|
||||
}
|
||||
|
||||
struct FragmentOutput {
|
||||
@location(0) color : vec4<f32>,
|
||||
@builtin(frag_depth) depth : f32,
|
||||
};
|
||||
|
||||
@fragment
|
||||
fn f_main( input : VertexOutput ) -> FragmentOutput {
|
||||
|
||||
let surfaceDir : vec3<f32> = normalize( input.worldPosition );
|
||||
|
||||
let up : vec3<f32> = vec3<f32>( 0.0, 1.0, 0.0 );
|
||||
|
||||
let cosAngle : f32 = clamp( dot( surfaceDir, up ), 0.0, 1.0 );
|
||||
|
||||
let skyZenith : vec3<f32> = vec3<f32>( 0.45, 0.78, 0.98 );
|
||||
|
||||
let skyHorizon : vec3<f32> = vec3<f32>( 0.12, 0.28, 0.52 );
|
||||
|
||||
let sunDir : vec3<f32> = normalize( vec3<f32>( -0.1, 0.97, 0.2 ) );
|
||||
|
||||
let sunAngle : f32 = clamp( dot( surfaceDir, sunDir ), 0.0, 1.0 );
|
||||
|
||||
let sunExposure : f32 = smoothstep( 0.94, 0.999, sunAngle );
|
||||
|
||||
let skyGradient : vec3<f32> = mix( skyHorizon, skyZenith, pow( cosAngle, 1.1 ) );
|
||||
|
||||
let airglow : vec3<f32> = vec3<f32>( 0.09, 0.16, 0.28 ) * pow( 1.0 - cosAngle, 3.0 );
|
||||
|
||||
let scattering : vec3<f32> = skyGradient + airglow;
|
||||
|
||||
let sunDisk : vec3<f32> = vec3<f32>( 1.0, 0.94, 0.76 ) * sunExposure * sunExposure * 1.6;
|
||||
|
||||
let skyColor : vec3<f32> = mix( scattering, scattering + sunDisk, sunExposure );
|
||||
|
||||
let horizonBlend : f32 = smoothstep( 0.0, 0.6, 1.0 - cosAngle );
|
||||
|
||||
let baseColor : vec3<f32> = mix( skyHorizon, skyZenith, max( cosAngle, 0.1 ) );
|
||||
|
||||
let color : vec3<f32> = mix( baseColor * 1.1 + vec3<f32>( 0.02, 0.03, 0.04 ), skyColor * 0.9, 1.0 - horizonBlend );
|
||||
|
||||
return FragmentOutput(
|
||||
|
||||
vec4<f32>( color, 1.0 ),
|
||||
|
||||
0.999
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user