Skip to content

Commit

Permalink
add shadow mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
YoanWithY committed Sep 24, 2024
1 parent 4b91cd7 commit f3936e5
Show file tree
Hide file tree
Showing 17 changed files with 323 additions and 42 deletions.
6 changes: 4 additions & 2 deletions frontend/src/ts/Material/AbstractPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export abstract class AbstractPipeline {
};

abstract readonly vertexEntryPoint: string;
abstract readonly fragmentEntryPoint: string;
abstract readonly fragmentEntryPoint?: string;

constructor(label: string) {
this.label = label;
Expand All @@ -68,7 +68,9 @@ export abstract class AbstractPipeline {
};
}

protected createFragmentState(): GPUFragmentState {
protected createFragmentState(): GPUFragmentState | undefined {
if (!this.fragmentEntryPoint)
return undefined;
return {
module: this.shaderModule,
entryPoint: this.fragmentEntryPoint,
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/ts/Viewport/Camera.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import mat4 from "../math/mat4";
import vec3 from "../math/vec3";
import Projection from "../projection/Projection";
import TransformationStack from "../transformation/TransformationStack";

/**
* A minimal interface that describes the functionality of a camera.
*/
export default interface Camera {
transformationStack: TransformationStack;
getProjectionMatrix(width: number, height: number): mat4;
getProjection(): Projection;
getViewMatrix(): mat4;
getWorldLocation(): vec3;
}
7 changes: 4 additions & 3 deletions frontend/src/ts/Viewport/ViewportCamera.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import mat4 from "../math/mat4";
import vec3 from "../math/vec3";
import PerspectiveProjection from "../projection/PerspectiveProjection";
import Projection from "../projection/Projection";
import TransformationStack from "../transformation/TransformationStack";
import Camera from "./Camera";

Expand All @@ -11,14 +12,14 @@ export default class ViewportCamera implements Camera {
projection = new PerspectiveProjection();
transformationStack = new TransformationStack();

getProjectionMatrix(width: number, height: number): mat4 {
return this.projection.getProjectionMatrix(width, height);
}
getViewMatrix(): mat4 {
return this.transformationStack.getInverseTransformationMatrix();
}
getWorldLocation(): vec3 {
return this.transformationStack.getTransformationMatrix().getTranslation();
}
getProjection(): Projection {
return this.projection;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import staticShaderCode from "./heightField.wgsl?raw";
import { resolveShader } from "../../rendering/Shader";
import Renderer from "../../rendering/Renderer";
import { Project } from "../../project/Project";
import { sunBindGroupLayout } from "../../lights/SunLight";

export const heightFieldDataLayout = gpuDevice.createBindGroupLayout(
{
Expand Down Expand Up @@ -108,7 +109,7 @@ export class HeightFieldPipeline extends AbstractPipeline {
const renderer = this.project.renderer;
return gpuDevice.createPipelineLayout({
label: "Height field pipeline layout",
bindGroupLayouts: [renderer.bindGroup0Layout, renderer.bindGroupR3Layout, heightFieldDataLayout],
bindGroupLayouts: [renderer.bindGroup0Layout, renderer.bindGroupR3Layout, heightFieldDataLayout, sunBindGroupLayout],
});
}

Expand Down
46 changes: 29 additions & 17 deletions frontend/src/ts/dynamicObject/heightField/HeightFieldR3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { HeightFieldSelectionPipeline } from "./HeightFieldSelectionPipeline";
import { gpuDevice } from "../../GPUX";
import { resolveShader } from "../../rendering/Shader";
import heightFieldComputeCode from "./heightFieldCompute.wgsl?raw";
import { HeightFieldShadowPipeline as HeightFieldDepthPipeline } from "./HeightFieldShadowPipeline";

export default class HeightFieldR3 extends R3Object {
pipeline: HeightFieldPipeline;
selectionPipeline: HeightFieldSelectionPipeline;
computePipeline: GPUComputePipeline;
private _xVerts: number;
private _yVerts: number;
Expand All @@ -29,7 +29,6 @@ export default class HeightFieldR3 extends R3Object {
usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING
});
this.pipeline = new HeightFieldPipeline(project, "Height Field Pipeline");
this.selectionPipeline = new HeightFieldSelectionPipeline(project, "Height Field Selection Pieline");
this.dataBuffer = gpuDevice.createBuffer({
label: "height field data buffer",
size: 10 * 4,
Expand Down Expand Up @@ -87,15 +86,17 @@ export default class HeightFieldR3 extends R3Object {
}
]
});
const computeModule = gpuDevice.createShaderModule({
label: "height field compute shader module",
code: resolveShader(heightFieldComputeCode, { heightCode: heightFunction }),
compilationHints: [{ entryPoint: "computeHeight" }],
})
const computePipelineLayout = gpuDevice.createPipelineLayout({
label: "height field compute pipeline layout",
bindGroupLayouts: [project.renderer.bindGroup0Layout, heightFieldComputeBindGroupLayout],
})
const computeModule = gpuDevice.createShaderModule(
{
label: "height field compute shader module",
code: resolveShader(heightFieldComputeCode, { heightCode: heightFunction }),
compilationHints: [{ entryPoint: "computeHeight" }],
})
const computePipelineLayout = gpuDevice.createPipelineLayout(
{
label: "height field compute pipeline layout",
bindGroupLayouts: [project.renderer.bindGroup0Layout, heightFieldComputeBindGroupLayout],
})
this.computePipeline = gpuDevice.createComputePipeline(
{
label: "Height field compute pipeline",
Expand All @@ -106,20 +107,31 @@ export default class HeightFieldR3 extends R3Object {
});
}

static selectionPipeline: HeightFieldSelectionPipeline;
static depthPipeline: HeightFieldDepthPipeline;
static init(project: Project) {
this.selectionPipeline = new HeightFieldSelectionPipeline(project, "height field selection pipeline");
this.depthPipeline = new HeightFieldDepthPipeline(project, "height field depth pipeline");
}

getVerts(): number {
return this._xVerts * this._yVerts + this._xVerts * (this._yVerts - 2) + 2 * this._yVerts - 2;
}

render(renderPassEncoder: GPURenderPassEncoder): void {
renderPassEncoder.setPipeline(this.pipeline.gpuPipeline);
this.updateUniforms();
renderPassEncoder.setBindGroup(1, this.defaultBindGroup);
renderPassEncoder.setBindGroup(2, this.dataBindGroup);
renderPassEncoder.draw(this.getVerts());
this.renderWithPipeline(renderPassEncoder, this.pipeline.gpuPipeline);
}

renderSelection(renderPassEncoder: GPURenderPassEncoder): void {
renderPassEncoder.setPipeline(this.selectionPipeline.gpuPipeline);
this.renderWithPipeline(renderPassEncoder, HeightFieldR3.selectionPipeline.gpuPipeline);
}

renderDepth(renderPassEncoder: GPURenderPassEncoder): void {
this.renderWithPipeline(renderPassEncoder, HeightFieldR3.depthPipeline.gpuPipeline);
}

private renderWithPipeline(renderPassEncoder: GPURenderPassEncoder, pipeline: GPURenderPipeline) {
renderPassEncoder.setPipeline(pipeline);
this.updateUniforms();
renderPassEncoder.setBindGroup(1, this.defaultBindGroup);
renderPassEncoder.setBindGroup(2, this.dataBindGroup);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class HeightFieldSelectionPipeline extends AbstractPipeline {
createPipelineLayout(): GPUPipelineLayout | "auto" {
const renderer = this.project.renderer;
return gpuDevice.createPipelineLayout({
label: "Height field geometry pipeline layout",
label: "Height field selection pipeline layout",
bindGroupLayouts: [renderer.bindGroup0Layout, renderer.bindGroupR3Layout, heightFieldDataLayout],
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { gpuDevice } from "../../GPUX";
import { AbstractPipeline, vertexGeometryEntryPoint } from "../../Material/AbstractPipeline";
import staticShaderCode from "./heightField.wgsl?raw";
import { resolveShader } from "../../rendering/Shader";
import { Project } from "../../project/Project";
import { heightFieldDataLayout } from "./HeightFieldPipeline";

export class HeightFieldShadowPipeline extends AbstractPipeline {
vertexEntryPoint = vertexGeometryEntryPoint;
fragmentEntryPoint = undefined;
gpuPipeline: GPURenderPipeline;
readonly isDisplayOutputPipeline = false;
readonly shaderCode: string;
readonly preProzessedShaderCoder;
readonly shaderModule: GPUShaderModule;
vertexConstants: Record<string, number>;
vertexBufferLayout: GPUVertexBufferLayout[];
fragmentConstants: Record<string, number>;
fragmentTargets: GPUColorTargetState[];
topology: GPUPrimitiveTopology;
cullMode: GPUCullMode;
stripIndexFormat?: GPUIndexFormat;
depthStencilFormat: GPUTextureFormat;
depthCompare: GPUCompareFunction;
depthWriteEnabled: boolean;
project: Project;

constructor(project: Project, label: string) {
super(label);
this.project = project;
this.shaderCode = staticShaderCode;
this.preProzessedShaderCoder = resolveShader(this.shaderCode);
this.vertexConstants = {};
this.vertexBufferLayout = [];
this.fragmentConstants = {};
this.topology = "triangle-strip";
this.cullMode = "none";
this.depthCompare = "less";
this.depthWriteEnabled = true;
this.depthStencilFormat = "depth24plus";
this.fragmentTargets = [];

this.shaderModule = gpuDevice.createShaderModule(
{
label: `${label} shader module`,
code: this.preProzessedShaderCoder,
compilationHints: [
{ entryPoint: vertexGeometryEntryPoint },
]
});
this.gpuPipeline = this.buildPipeline();
}

createPipelineLayout(): GPUPipelineLayout | "auto" {
const renderer = this.project.renderer;
return gpuDevice.createPipelineLayout({
label: "Height field shadow pipeline layout",
bindGroupLayouts: [renderer.bindGroup0Layout, renderer.bindGroupR3Layout, heightFieldDataLayout],
});
}
}
35 changes: 32 additions & 3 deletions frontend/src/ts/dynamicObject/heightField/heightField.wgsl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
struct VertexOut {
@builtin(position) position: vec4f,
@location(0) ls_pos: vec3f,
@location(1) normal: vec3f
@location(1) normal: vec3f,
@location(2) ws_pos: vec3f,
}

#include <utility/frame>
Expand Down Expand Up @@ -69,6 +70,7 @@ fn vertex_main(@builtin(vertex_index) index: u32) -> VertexOut {
vertexUniform.transformation[2].xyz
);
output.normal = normMat * data.xyz;
output.ws_pos = ws_pos;
return output;
}

Expand All @@ -89,11 +91,38 @@ fn steps(v: vec3f, stepSize: f32) -> vec3f {
return floor(v / stepSize) * stepSize;
}

struct Sun {
matrix: mat4x4f,
light: vec4f
}
@group(3) @binding(0) var<uniform> sun: Sun;
@group(3) @binding(1) var shadowMap: texture_depth_2d;
@group(3) @binding(2) var shadowSampler: sampler_comparison;
fn getShadow(ws_pos: vec3f) -> f32 {
var shadowNDC = (sun.matrix * vec4f(ws_pos, 1.0));
var shadowUV = shadowNDC.xy * vec2f(0.5, -0.5) + 0.5;
var shadow = 0.0;
for(var y = -1; y<=1; y++) {
for(var x = -1; x<=1; x++) {
shadow += textureSampleCompare(shadowMap, shadowSampler, shadowUV + vec2f(f32(x), f32(y)) / 4096, shadowNDC.z - 0.001);
}
}
if(any(shadowUV < vec2f(0)) || any(shadowUV > vec2f(1))) {
return 1;
}
return shadow / 9;
}


@fragment
fn fragment_main(@builtin(front_facing) front_facing: bool, vertexData: VertexOut) -> R3FragmentOutput {
let n = normalize(vertexData.normal) * select(-1.0, 1.0, front_facing);
let color = vec3(clamp(dot(n, normalize(vec3(1,1,1))), 0.0, 1.0));
let outColor = vec4f(createOutputFragment(color), 1);
let l = sun.light.xyz;
let strength = sun.light.w;
var light = vec3(clamp(dot(n, l), 0.0, 1.0)) * strength;
light *= getShadow(vertexData.ws_pos);
let color = vec3(cos(vertexData.ws_pos) *0.5 + 0.5);
let outColor = vec4f(createOutputFragment(light * color), 1);
return R3FragmentOutput(outColor, fragmentUniform.id);
}

Expand Down
Loading

0 comments on commit f3936e5

Please sign in to comment.