Skip to content

Commit

Permalink
add shader hint
Browse files Browse the repository at this point in the history
  • Loading branch information
weilueluo committed Aug 12, 2024
1 parent dccb819 commit 8a86353
Show file tree
Hide file tree
Showing 19 changed files with 3,061 additions and 70 deletions.
2 changes: 1 addition & 1 deletion scripts/deploy/V3_NEXT_TAG.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
96
103
2 changes: 2 additions & 0 deletions src/app/[locale]/shader/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ShaderHeader from "@/components/shader/ShaderHeader";
import ShaderRenderer from "@/components/shader/ShaderRenderer";
import { fetchMessages } from "@/shared/i18n/translation";
import { BasePageProps } from "@/shared/types/comp";

Expand All @@ -10,6 +11,7 @@ export default async function Page({ params, children }: BasePageProps) {
return (
<>
<ShaderHeader messages={messages} locale={params.locale} />
<ShaderRenderer messages={messages} locale={params.locale} />
</>
);
}
92 changes: 49 additions & 43 deletions src/components/shader/ShaderHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,132 +2,138 @@
import { Messages } from "@/shared/i18n/type";
import { BaseCompProps } from "@/shared/types/comp";
import { tm } from "@/shared/utils";
import { BiTargetLock } from "react-icons/bi";
import { BsShadows } from "react-icons/bs";
import { GiFilmProjector, GiShieldReflect } from "react-icons/gi";
import { IoIosCut } from "react-icons/io";
import { IoIosColorPalette, IoIosCut } from "react-icons/io";
import { IoLayers } from "react-icons/io5";
import { MdBlurOn, MdDeblur, MdGroupWork, MdLightbulb, MdScatterPlot } from "react-icons/md";
import { MdBlurOn, MdGroupWork, MdLightbulb, MdScatterPlot } from "react-icons/md";
import { PiPath, PiWaveTriangleFill } from "react-icons/pi";
import { RiColorFilterFill } from "react-icons/ri";
import { TbBounceLeftFilled, TbFence, TbTriangleSquareCircleFilled } from "react-icons/tb";
import IconedText from "../ui/icon-text";
import { useCoursework } from "./coursework";
import { IoIosColorPalette } from "react-icons/io";
import { BiTargetLock } from "react-icons/bi";

export interface ShaderHeaderProps extends BaseCompProps<"div"> {
messages: Messages;
}

export default function ShaderHeader({ messages }: ShaderHeaderProps) {
const { active, setActive, activeOptions, selectOption } = useCoursework();
const { type, toggleType, options, toggleOption } = useCoursework();

if (!messages) return null;

const isRayTracer = active === "ray tracer";
const isRasterization = active === "rasterization";
const isPathTracer = active === "path tracer";
const isRayTracer = type === "ray tracer";
const isRasterization = type === "rasterization";
const isPathTracer = type === "path tracer";

return (
<div>
<div className="flex flex-row justify-center gap-4">
<IconedText active={isRayTracer} onClick={() => setActive("ray tracer")}>
<IconedText active={isRayTracer} onClick={() => toggleType("ray tracer")}>
<GiShieldReflect />
Ray Tracer
</IconedText>
<IconedText active={isRasterization} onClick={() => setActive("rasterization")}>
<IconedText active={isRasterization} onClick={() => toggleType("rasterization")}>
<TbFence />
Rasterization
</IconedText>
<IconedText active={isPathTracer} onClick={() => setActive("path tracer")}>
<IconedText active={isPathTracer} onClick={() => toggleType("path tracer")}>
<PiPath />
Path Tracer
</IconedText>
</div>
<div>
<div className={tm("mt-4 flex flex-row flex-wrap justify-around gap-4", !isRayTracer && "hidden")}>
<div
className={tm(
"mt-2 flex flex-row flex-wrap justify-around gap-2 md:gap-4",
!isRayTracer && "hidden"
)}>
<IconedText
active={activeOptions.includes("Cylinder And Plane")}
onClick={() => selectOption("Cylinder And Plane")}>
active={options.includes("Cylinder And Plane")}
onClick={() => toggleOption("Cylinder And Plane")}>
<TbTriangleSquareCircleFilled />
Cylinder And Plane
</IconedText>
<IconedText
active={activeOptions.includes("Reflect And Refract")}
onClick={() => selectOption("Reflect And Refract")}>
active={options.includes("Reflect And Refract")}
onClick={() => toggleOption("Reflect And Refract")}>
<PiWaveTriangleFill />
Reflect And Refract
</IconedText>
<IconedText active={activeOptions.includes("Fresnel")} onClick={() => selectOption("Fresnel")}>
<MdDeblur />
<IconedText active={options.includes("Fresnel")} onClick={() => toggleOption("Fresnel")}>
<BsShadows />
Fresnel
</IconedText>
</div>
<div className={tm("mt-4 flex flex-row flex-wrap justify-around gap-4", !isRasterization && "hidden")}>
<IconedText
active={activeOptions.includes("Projection")}
onClick={() => selectOption("Projection")}>
<div
className={tm(
"mt-4 flex flex-row flex-wrap justify-around gap-2 md:gap-4",
!isRasterization && "hidden"
)}>
<IconedText active={options.includes("Projection")} onClick={() => toggleOption("Projection")}>
<GiFilmProjector />
Projection
</IconedText>
<IconedText
active={activeOptions.includes("Rasterization")}
onClick={() => selectOption("Rasterization")}>
active={options.includes("Rasterization")}
onClick={() => toggleOption("Rasterization")}>
<TbFence />
Rasterization
</IconedText>
<IconedText active={activeOptions.includes("Clipping")} onClick={() => selectOption("Clipping")}>
<IconedText active={options.includes("Clipping")} onClick={() => toggleOption("Clipping")}>
<IoIosCut />
Clipping
</IconedText>
<IconedText
active={activeOptions.includes("Interpolation")}
onClick={() => selectOption("Interpolation")}>
active={options.includes("Interpolation")}
onClick={() => toggleOption("Interpolation")}>
<RiColorFilterFill />
Interpolation
</IconedText>
<IconedText
active={activeOptions.includes("Zbuffering")}
onClick={() => selectOption("Zbuffering")}>
<IconedText active={options.includes("Zbuffering")} onClick={() => toggleOption("Zbuffering")}>
<IoLayers />
Zbuffering
</IconedText>
<IconedText active={activeOptions.includes("AAlias")} onClick={() => selectOption("AAlias")}>
<IconedText active={options.includes("AAlias")} onClick={() => toggleOption("AAlias")}>
<MdBlurOn />
AAlias
</IconedText>
</div>
<div className={tm("mt-4 flex flex-row flex-wrap justify-around gap-4", !isPathTracer && "hidden")}>
<IconedText active={activeOptions.includes("Light")} onClick={() => selectOption("Light")}>
<div
className={tm(
"mt-4 flex flex-row flex-wrap justify-around gap-2 md:gap-4",
!isPathTracer && "hidden"
)}>
<IconedText active={options.includes("Light")} onClick={() => toggleOption("Light")}>
<MdLightbulb />
Light
</IconedText>
<IconedText active={activeOptions.includes("Bounce")} onClick={() => selectOption("Bounce")}>
<IconedText active={options.includes("Bounce")} onClick={() => toggleOption("Bounce")}>
<TbBounceLeftFilled />
Bounce
</IconedText>
<IconedText
active={activeOptions.includes("Throughput")}
onClick={() => selectOption("Throughput")}>
<IconedText active={options.includes("Throughput")} onClick={() => toggleOption("Throughput")}>
<IoIosColorPalette />
Throughput
</IconedText>
<IconedText active={activeOptions.includes("Halton")} onClick={() => selectOption("Halton")}>
<IconedText active={options.includes("Halton")} onClick={() => toggleOption("Halton")}>
<MdScatterPlot />
Halton
</IconedText>
<IconedText active={activeOptions.includes("AAlias")} onClick={() => selectOption("AAlias")}>
<IconedText active={options.includes("AAlias")} onClick={() => toggleOption("AAlias")}>
<MdBlurOn />
AAlias
</IconedText>
<IconedText
active={activeOptions.includes("Importance Sampling")}
onClick={() => selectOption("Importance Sampling")}>
active={options.includes("Importance Sampling")}
onClick={() => toggleOption("Importance Sampling")}>
<BiTargetLock />
Importance Sampling
</IconedText>
<IconedText
active={activeOptions.includes("Multi-light IS")}
onClick={() => selectOption("Multi-light IS")}>
active={options.includes("Multi-light IS")}
onClick={() => toggleOption("Multi-light IS")}>
<MdGroupWork />
Multi-light IS
</IconedText>
Expand Down
108 changes: 108 additions & 0 deletions src/components/shader/ShaderRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"use client";
import { BaseCompProps } from "@/shared/types/comp";
import { tm } from "@/shared/utils";
import Script from "next/script";
import { useEffect, useState } from "react";
import { MdSpeed } from "react-icons/md";
import IconedText from "../ui/icon-text";
import Loading from "../ui/loading/spinner";
import Separator from "../ui/separator";
import { TYPE_2_SOLUTION, useCoursework } from "./coursework";
import { ShaderFramework } from "./shaderFramework";

const COURSEWORK_TYPE_2_FRAG_SHADER = {
"ray tracer": "cwk1-fragment-shader",
rasterization: "cwk2-fragment-shader",
"path tracer": "cwk3-fragment-shader",
};

const COURSEWORK_TYPE_2_VERT_SHADER = {
"ray tracer": "cwk1-vertex-shader",
rasterization: "cwk2-vertex-shader",
"path tracer": "cwk3-vertex-shader",
};

export default function ShaderRenderer(params: BaseCompProps<"div">) {
const [framework, setFramework] = useState<ShaderFramework | undefined>(undefined);
const { type, options, processing, setProcessing } = useCoursework();
const [frames, setFrames] = useState(0);
const [startTime, setStartTime] = useState(0);

const canvasId = "courseworkCanvas";

useEffect(() => {
setFramework(prevFramework => {
setProcessing(true);
if (prevFramework) {
prevFramework.stop();
}
if (type === undefined) {
console.warn("No coursework type selected");
return undefined;
}
const isPathTracer = type === "path tracer";
const newFramework = new ShaderFramework(
canvasId,
COURSEWORK_TYPE_2_VERT_SHADER[type],
COURSEWORK_TYPE_2_FRAG_SHADER[type],
isPathTracer,
isPathTracer ? "cwk3-tonemap-shader" : undefined
);
options.map(opt => TYPE_2_SOLUTION[opt]).forEach(opt => newFramework.addSolution(opt));

const maxFrames = isPathTracer ? 1000 : 1;
setStartTime(Date.now());
setFrames(0);
newFramework.setFrameCallback(() => {
setFrames(newFramework.getCurrentFrame());
});
newFramework
.initialize()
.then(() => newFramework.start(maxFrames))
.then(() => setProcessing(false));
return newFramework;
});
}, [type, options, setProcessing]);

return (
<>
<Separator className="mb-2 h-2" />
<div className={tm("relative mt-4 flex flex-col items-center")}>
<canvas
id={canvasId}
className={tm("w-full border-4 border-black md:w-4/5", processing && "brightness-50")}></canvas>

{processing && <Loading className="absolute left-0 top-0 z-10 h-full w-full fill-black" />}
<IconedText hover={false}>
<MdSpeed />
<span className="secondary-text">
{frames}/{framework?.getMaxFrame()} frames | {getFps(startTime, frames)} fps
</span>
</IconedText>
<span className="secondary-text z-10">*This runs in real-time on your machine</span>
</div>
<Script
id="cwk1-fragment-shader"
type="x-shader/x-fragment"
src="/shaders/cwk1/fragmentShader.glsl"></Script>
<Script id="cwk1-vertex-shader" type="x-shader/x-vertex" src="/shaders/cwk1/vertexShader.glsl"></Script>
<Script
id="cwk2-fragment-shader"
type="x-shader/x-fragment"
src="/shaders/cwk2/fragmentShader.glsl"></Script>
<Script id="cwk2-vertex-shader" type="x-shader/x-vertex" src="/shaders/cwk2/vertexShader.glsl"></Script>

<Script
id="cwk3-fragment-shader"
type="x-shader/x-fragment"
src="/shaders/cwk3/fragmentShader.glsl"></Script>
<Script id="cwk3-vertex-shader" type="x-shader/x-vertex" src="/shaders/cwk3/vertexShader.glsl"></Script>
<Script id="cwk3-tonemap-shader" type="x-shader/x-fragment" src="/shaders/cwk3/tonemapShader.glsl"></Script>
</>
);
}

function getFps(startTime: number, frames: number) {
const time = Date.now() - startTime;
return Math.floor((frames / time) * 1000);
}
Loading

0 comments on commit 8a86353

Please sign in to comment.