Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions public/shaders/std-gerstner.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
fn rotateDir(a: vec2<f32>, rad: f32) -> vec2<f32> {
let sinC = sin(rad);
let cosC = cos(rad);
return vec2(
a.x * cosC - a.y * sinC,
a.x * sinC + a.y * cosC
);
}

fn gerstner(uv: vec2<f32>, t: f32) -> mat2x3<f32> {
var displacement = vec3<f32>(0.0, 0.0, 0.0);
var normal = vec3<f32>(0.0, 0.0, 0.0);
for (var i = 0u; i < scene.numGerstnerWaves; i++) {
let wave = gerstnerWaves.ms[i];
// let D = rotateDir(wave.D, 0.7);
let D = wave.D;
let dot_w_d_uv_phi_t = wave.w * dot(D, uv) + wave.phi * t;
let _cos = cos(dot_w_d_uv_phi_t);
let _sin = sin(dot_w_d_uv_phi_t);
displacement.x += wave.Q * wave.A * D.x * _cos;
displacement.z += wave.Q * wave.A * D.y * _cos;
displacement.y += wave.A * _sin;
normal.x += -1.0 * D.x * wave.w * wave.A * _cos;
normal.z += -1.0 * D.y * wave.w * wave.A * _cos;
normal.y += wave.Q * wave.w * wave.A * _sin;
}
normal.y = 1.0 - normal.y;
normalize(normal);
return mat2x3(displacement, normal);
}
82 changes: 36 additions & 46 deletions public/shaders/std-ocean.wgsl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
struct VertexOutput {
// TODO(@darzu): change
@location(0) normal : vec3<f32>,
@location(1) color : vec3<f32>,
// @location(0) @interpolate(flat) normal : vec3<f32>,
@location(1) @interpolate(flat) color : vec3<f32>,
@location(2) worldPos: vec4<f32>,
@location(3) uv: vec2<f32>,
@location(4) @interpolate(flat) surface: u32,
Expand Down Expand Up @@ -56,25 +57,6 @@ fn getShadowVis(shadowPos: vec3<f32>, normal: vec3<f32>, lightDir: vec3<f32>, in
return visibility;
}

fn gerstner(uv: vec2<f32>, t: f32) -> mat2x3<f32> {
var displacement = vec3<f32>(0.0, 0.0, 0.0);
var normal = vec3<f32>(0.0, 0.0, 0.0);
for (var i = 0u; i < scene.numGerstnerWaves; i++) {
let wave = gerstnerWaves.ms[i];
displacement = displacement +
vec3<f32>(wave.Q * wave.A + wave.D.x * cos(dot(wave.w * wave.D, uv) + wave.phi * t),
wave.A * sin(dot(wave.w * wave.D, uv) + wave.phi * t),
wave.Q * wave.A + wave.D.y * cos(dot(wave.w * wave.D, uv) + wave.phi * t));
normal = normal +
vec3<f32>(-1.0 * wave.D.x * wave.w * wave.A * cos(wave.w * dot(wave.D, uv) + wave.phi * t),
wave.Q * wave.w * wave.A * sin(wave.w * dot(wave.D, uv) + wave.phi * t),
-1.0 * wave.D.y * wave.w * wave.A * cos(wave.w * dot(wave.D, uv) + wave.phi * t));
}
normal.y = 1.0 - normal.y;
normalize(normal);
return mat2x3(displacement, normal);
}

@vertex
fn vert_main(input: VertexInput) -> VertexOutput {
let position = input.position;
Expand All @@ -88,10 +70,13 @@ fn vert_main(input: VertexInput) -> VertexOutput {
// TODO(@darzu): we're not totally sure about x,y,z vs normal,tangent,perp
let surfBasis = mat3x3<f32>(perp, normal, tangent);
let gerst = gerstner(input.uv * 1000, scene.time * .001);
let displacedPos = position + surfBasis * gerst[0];
//let displacedPos = flattenedPos + gerst[0];
let gerstNormal = surfBasis * gerst[1];
//let gerstNormal = gerst[1];

// let displacedPos = position + surfBasis * gerst[0];
// let gerstNormal = surfBasis * gerst[1];
// FLATTENED:
let displacedPos = flattenedPos + gerst[0];
let gerstNormal = gerst[1];

// let displacedPos = flattenedPos + wave1;
// let displacedPos = position + wave0;
// let displacedPos = position + wave1;
Expand Down Expand Up @@ -120,42 +105,47 @@ fn vert_main(input: VertexInput) -> VertexOutput {

struct FragOut {
@location(0) color: vec4<f32>,
@location(1) surface: vec2<u32>,
@location(1) normal: vec4<f32>,
@location(2) surface: vec2<u32>,
}

@fragment
fn frag_main(input: VertexOutput) -> FragOut {
let normal = normalize(input.normal);

var lightingColor: vec3<f32> = vec3<f32>(0.0, 0.0, 0.0);
let unlit = 0u;
for (var i: u32 = 0u; i < scene.numPointLights; i++) {
let light = pointLights.ms[i];
let toLight = light.position - input.worldPos.xyz;
let distance = length(toLight);
let attenuation = 1.0 / (light.constant + light.linear * distance +
light.quadratic * distance * distance);
let angle = clamp(dot(normalize(toLight), input.normal), 0.0, 1.0);
// XY is in (-1, 1) space, Z is in (0, 1) space
let posFromLight = (pointLights.ms[i].viewProj * input.worldPos).xyz;
// var lightingColor: vec3<f32> = vec3<f32>(0.0, 0.0, 0.0);
// let unlit = 1u;
// for (var i: u32 = 0u; i < scene.numPointLights; i++) {
// let light = pointLights.ms[i];
// let toLight = light.position - input.worldPos.xyz;
// let distance = length(toLight);
// let attenuation = 1.0 / (light.constant + light.linear * distance +
// light.quadratic * distance * distance);
// let angle = clamp(dot(normalize(toLight), input.normal), 0.0, 1.0);
// // XY is in (-1, 1) space, Z is in (0, 1) space
// let posFromLight = (pointLights.ms[i].viewProj * input.worldPos).xyz;

// Convert XY to (0, 1), Y is flipped because texture coords are Y-down.
let shadowPos = vec3<f32>(posFromLight.xy * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5, 0.5),
posFromLight.z
);
let shadowVis = getShadowVis(shadowPos, input.normal, normalize(toLight), i);
//lightingColor = lightingColor + clamp(abs((light.ambient * attenuation) + (light.diffuse * angle * attenuation * shadowVis)), vec3(0.0), vec3(1.0));
//lightingColor += light.ambient;
lightingColor = lightingColor + f32(1u - unlit) * ((light.ambient * attenuation) + (light.diffuse * angle * attenuation * shadowVis));
}
let litColor = input.color * (lightingColor + vec3(f32(unlit)));
// // Convert XY to (0, 1), Y is flipped because texture coords are Y-down.
// let shadowPos = vec3<f32>(posFromLight.xy * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5, 0.5),
// posFromLight.z
// );
// let shadowVis = getShadowVis(shadowPos, input.normal, normalize(toLight), i);
// //lightingColor = lightingColor + clamp(abs((light.ambient * attenuation) + (light.diffuse * angle * attenuation * shadowVis)), vec3(0.0), vec3(1.0));
// //lightingColor += light.ambient;
// lightingColor = lightingColor + f32(1u - unlit) * ((light.ambient * attenuation) + (light.diffuse * angle * attenuation * shadowVis));
// }
// let litColor = input.color * (lightingColor + vec3(f32(unlit)));

// let litColor = mix(vec3(0.1, 0.3, 0.8), vec3(0.015), input.uv.x);
let litColor = mix(vec3(0.1, 0.3, 0.8), vec3(0.1, 0.05, 0.1), input.uv.x);


var out: FragOut;
out.color = vec4<f32>(litColor, 1.0);

out.surface.r = input.surface;
out.surface.g = input.id;
out.normal = vec4(normal, 0.0);

return out;
}
2 changes: 1 addition & 1 deletion public/shaders/std-outline.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ fn frag_main(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {
color *= 1.0 + edgeLum;
// TODO(@darzu): DEBUG WIREFRAME
// color *= 2.0;
// color = vec3(0.8);
color = vec3(0.8);
}
// DEBUG WIREFRAME
// else {
Expand Down
180 changes: 140 additions & 40 deletions src/game/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ const MeshTransforms: Partial<{
mat4.fromYRotation(mat4.create(), -Math.PI * 0.5),
vec3.fromValues(-5, 0, 0)
),
// ocean: mat4.fromScaling(mat4.create(), [0.1, 0.1, 0.1]),
ocean: mat4.fromScaling(mat4.create(), [2, 2, 2]),
};

Expand Down Expand Up @@ -168,8 +169,28 @@ const MeshModify: Partial<{
// console.log("getMeshAsGrid failed!");
// console.error(e);
// }

const xLen = grid.length;
const yLen = grid[0].length;

// redo quad indices based on the grid (optional?)
for (let xi = 0; xi < xLen - 1; xi++) {
for (let yi = 0; yi < yLen - 1; yi++) {
const qi = gridXYtoQuad(xi, yi);
vec4.copy(m.quad[qi], [
grid[xi][yi],
grid[xi + 1][yi],
grid[xi + 1][yi + 1],
grid[xi][yi + 1],
]);
}
}
function gridXYtoQuad(xi: number, yi: number): number {
const qi = yi + xi * (yLen - 1);
assert(qi < m.quad.length, "quads and grid mismatch!");
return qi;
}

// console.log(`xLen:${xLen},yLen:${yLen}`);
const uvs = m.pos.map((_, vi) => vec2.create());
m.uvs = uvs;
Expand All @@ -182,45 +203,132 @@ const MeshModify: Partial<{
// instead of per quad, for vertex displacement (e.g. waves)
// purposes?

//set tangents
// set tangents and normals
m.tangents = m.pos.map(() => vec3.create());
m.normals = m.pos.map(() => vec3.create());
for (let xIndex = 0; xIndex < grid.length; xIndex++) {
for (let yIndex = 0; yIndex < grid[0].length; yIndex++) {
for (let xi = 0; xi < grid.length; xi++) {
for (let yi = 0; yi < grid[0].length; yi++) {
let normal: vec3;
let tangent: vec3;
if (xIndex + 1 < grid.length && yIndex + 1 < grid[0].length) {
const pos = m.pos[grid[xIndex][yIndex]];
const posNX = m.pos[grid[xIndex + 1][yIndex]];
const posNY = m.pos[grid[xIndex][yIndex + 1]];
if (xi + 1 < grid.length && yi + 1 < grid[0].length) {
const pos = m.pos[grid[xi][yi]];
const posNX = m.pos[grid[xi + 1][yi]];
const posNY = m.pos[grid[xi][yi + 1]];

normal = computeTriangleNormal(pos, posNX, posNY);

tangent = vec3.sub(m.tangents[grid[xIndex][yIndex]], posNX, pos);
tangent = vec3.sub(m.tangents[grid[xi][yi]], posNX, pos);
vec3.normalize(tangent, tangent);
} else if (xIndex + 1 >= grid.length) {
normal = m.normals[grid[xIndex - 1][yIndex]];
tangent = m.tangents[grid[xIndex - 1][yIndex]];
} else if (yIndex + 1 >= grid[0].length) {
normal = m.normals[grid[xIndex][yIndex - 1]];
tangent = m.tangents[grid[xIndex][yIndex - 1]];
} else if (xi + 1 >= grid.length) {
normal = m.normals[grid[xi - 1][yi]];
tangent = m.tangents[grid[xi - 1][yi]];
} else if (yi + 1 >= grid[0].length) {
normal = m.normals[grid[xi][yi - 1]];
tangent = m.tangents[grid[xi][yi - 1]];
} else {
assert(false);
}
vec3.copy(m.normals[grid[xIndex][yIndex]], normal);
vec3.copy(m.tangents[grid[xIndex][yIndex]], tangent);
vec3.copy(m.normals[grid[xi][yi]], normal);
vec3.copy(m.tangents[grid[xi][yi]], tangent);
}
}

// TODO(@darzu): testing subdivide
// for (let i = 0; i < 100; i++) {
// subdivideQuad(i);
// }
let startXi = Math.floor(grid.length * 0.85);
let endXi = Math.floor(grid.length * 1.0);
let startYi = Math.floor(grid[0].length * 0.0);
let endYi = Math.floor(grid[0].length * 0.2);
for (let xi = startXi; xi < endXi; xi++) {
for (let yi = startYi; yi < endYi; yi++) {
subdivideQuad(gridXYtoQuad(xi, yi), 2);
}
}

// console.dir(uvs);
// console.log(`
// X:
// ${max(uvs.map((uv) => uv[0]))}
// ${min(uvs.map((uv) => uv[0]))}
// Y:
// ${max(uvs.map((uv) => uv[1]))}
// ${min(uvs.map((uv) => uv[1]))}
// `);
// console.dir(m);

// console.dir({
// uvMin: [min(m.uvs.map((a) => a[0])), min(m.uvs.map((a) => a[1]))],
// uvMax: [max(m.uvs.map((a) => a[0])), max(m.uvs.map((a) => a[1]))],
// });

// console.dir(m.uvs);
// console.dir({ minX, maxX, minZ, maxZ });
return m;

function subdivideQuad(quadIdx: number, recurse = 0) {
// maintain: colors, normals, pos, quad, surfaceIds, tangents, uvs
let [tl, tr, br, bl] = m.quad[quadIdx];
let tm = m.pos.push(midPos(tl, tr)) - 1;
let rm = m.pos.push(midPos(tr, br)) - 1;
let bm = m.pos.push(midPos(br, bl)) - 1;
let lm = m.pos.push(midPos(bl, tl)) - 1;
let mm = m.pos.push(midPos(tm, bm)) - 1;

// all quads are: top-left, top-right, bottom-right, bottom-left
let q_tl = quadIdx; // re-use
m.quad[q_tl] = [tl, tm, mm, lm];
let q_tr = m.quad.push([tm, tr, rm, mm]) - 1;
let q_br = m.quad.push([mm, rm, br, bm]) - 1;
let q_bl = m.quad.push([lm, mm, bm, bl]) - 1;

// init per-vertex (set later)
for (let p of [tm, rm, bm, lm, mm]) {
m.uvs![p] = vec2.create();
m.tangents![p] = vec3.create();
m.normals![p] = vec3.create();
}

// update quad properties
for (let q of [q_tl, q_tr, q_br, q_bl]) {
// per quad:
m.colors[q] = vec3.clone(m.colors[quadIdx]);
m.surfaceIds![q] = q;
// per provoking vert:
let vs = m.quad[q];
m.normals![vs[0]] = computeTriangleNormal(
m.pos[vs[0]],
m.pos[vs[1]],
m.pos[vs[2]]
);
m.tangents![vs[0]] = vec3.sub(
vec3.create(),
m.pos[vs[1]],
m.pos[vs[0]]
);
vec3.normalize(m.tangents![vs[0]], m.tangents![vs[0]]);
}
// per vertex:
m.uvs![tm] = midUV(tl, tr);
m.uvs![rm] = midUV(tr, br);
m.uvs![bm] = midUV(br, bl);
m.uvs![lm] = midUV(bl, tl);
m.uvs![mm] = midUV(tm, bm);

// recurse?
if (recurse > 0) {
subdivideQuad(q_tl, recurse - 1);
subdivideQuad(q_tr, recurse - 1);
subdivideQuad(q_br, recurse - 1);
subdivideQuad(q_bl, recurse - 1);
}

function midPos(p0: number, p1: number): vec3 {
return vec3.fromValues(
(m.pos[p0][0] + m.pos[p1][0]) * 0.5,
(m.pos[p0][1] + m.pos[p1][1]) * 0.5,
(m.pos[p0][2] + m.pos[p1][2]) * 0.5
);
}
function midUV(p0: number, p1: number): vec2 {
return vec2.fromValues(
(m.uvs![p0][0] + m.uvs![p1][0]) * 0.5,
(m.uvs![p0][1] + m.uvs![p1][1]) * 0.5
);
}
}

function setUV(
x: number,
Expand Down Expand Up @@ -252,14 +360,6 @@ const MeshModify: Partial<{
];
setUV(nX, nY, dir, newDist, branch);
}
// console.dir({
// uvMin: [min(m.uvs.map((a) => a[0])), min(m.uvs.map((a) => a[1]))],
// uvMax: [max(m.uvs.map((a) => a[0])), max(m.uvs.map((a) => a[1]))],
// });

// console.dir(m.uvs);
// console.dir({ minX, maxX, minZ, maxZ });
return m;
},
};

Expand Down Expand Up @@ -714,13 +814,13 @@ onInit(async (em) => {

const assetsPromise = loadAssets(renderer.renderer);
assetLoader.promise = assetsPromise;
try {
const result = await assetsPromise;
em.addSingletonComponent(AssetsDef, result);
} catch (failureReason) {
// TODO(@darzu): fail more gracefully
throw `Failed to load assets: ${failureReason}`;
}
// try {
const result = await assetsPromise;
em.addSingletonComponent(AssetsDef, result);
// } catch (failureReason) {
// // TODO(@darzu): fail more gracefully
// throw `Failed to load assets: ${failureReason}`;
// }
});

async function loadTxtInternal(relPath: string): Promise<string> {
Expand Down
Loading