From 8b619bfc2891d86e7cc7b9018d291137713d2097 Mon Sep 17 00:00:00 2001 From: math-pixel <59537610+math-pixel@users.noreply.github.com> Date: Tue, 19 May 2026 22:54:29 +0200 Subject: [PATCH] feat: add NetShader and UnicolorShader with a debug component for visual testing in the world scene --- src/components/three/debug/NetTest.tsx | 25 ++++++++++ src/shaders/NetShader.ts | 66 ++++++++++++++++++++++++++ src/shaders/UnicolorShader.ts | 34 +++++++++++++ src/world/World.tsx | 2 + 4 files changed, 127 insertions(+) create mode 100644 src/components/three/debug/NetTest.tsx create mode 100644 src/shaders/NetShader.ts create mode 100644 src/shaders/UnicolorShader.ts diff --git a/src/components/three/debug/NetTest.tsx b/src/components/three/debug/NetTest.tsx new file mode 100644 index 0000000..44dd1e0 --- /dev/null +++ b/src/components/three/debug/NetTest.tsx @@ -0,0 +1,25 @@ +import { useRef } from "react"; +import { useFrame } from "@react-three/fiber"; +import * as THREE from "three"; +import { createNetShader } from "@/shaders/NetShader"; + +export function NetTest(): React.JSX.Element { + const materialRef = useRef(null); + + useFrame((_, delta) => { + if (materialRef.current) { + materialRef.current.uniforms.uTime.value += delta; + } + }); + + return ( + + + + + ); +} diff --git a/src/shaders/NetShader.ts b/src/shaders/NetShader.ts new file mode 100644 index 0000000..7d3f205 --- /dev/null +++ b/src/shaders/NetShader.ts @@ -0,0 +1,66 @@ +import { ShaderMaterial, Color, PlaneGeometry, Mesh, Vector2 } from "three"; + +export const createNetShader = (): ShaderMaterial => { + return new ShaderMaterial({ + uniforms: { + uTime: { value: 0 }, + uGridScale: { value: 15.0 }, + uPincushionStrength: { value: 0.4 }, + uBloomIntensity: { value: 0.3 }, + uGridThickness: { value: 0.02 }, + }, + vertexShader: ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, + fragmentShader: ` + uniform float uTime; + uniform float uGridScale; + uniform float uPincushionStrength; + uniform float uBloomIntensity; + uniform float uGridThickness; + + varying vec2 vUv; + + vec2 applyPincushion(vec2 uv, float strength) { + vec2 center = uv - 0.5; + float dist = length(center); + float distortion = 1.0 + dist * dist * strength; + return center * distortion + 0.5; + } + + float grid(vec2 uv, float scale, float thickness) { + vec2 gridUV = fract(uv * scale); + float lineX = smoothstep(thickness, thickness + 0.01, gridUV.x) + * smoothstep(1.0 - thickness, 1.0 - thickness - 0.01, gridUV.x); + float lineY = smoothstep(thickness, thickness + 0.01, gridUV.y) + * smoothstep(1.0 - thickness, 1.0 - thickness - 0.01, gridUV.y); + return lineX + lineY; + } + + void main() { + vec2 uv = applyPincushion(vUv, uPincushionStrength); + + float gridPattern = grid(uv, uGridScale, uGridThickness); + + vec3 gridColor = vec3(1.0, 0.4, 0.7); + vec3 bgColor = vec3(0.05, 0.02, 0.05); + + float bloom = gridPattern * uBloomIntensity; + vec3 col = mix(bgColor, gridColor + bloom, gridPattern); + + gl_FragColor = vec4(col, 1.0); + } + `, + }); +}; + +export const createNetMesh = (): Mesh => { + const geometry = new PlaneGeometry(2, 2); + const material = createNetShader(); + return new Mesh(geometry, material); +}; diff --git a/src/shaders/UnicolorShader.ts b/src/shaders/UnicolorShader.ts new file mode 100644 index 0000000..f9ef615 --- /dev/null +++ b/src/shaders/UnicolorShader.ts @@ -0,0 +1,34 @@ +import { ShaderMaterial, Color } from "three"; + +export const createUnicolorShader = ( + color: Color | string | number, +): ShaderMaterial => { + return new ShaderMaterial({ + uniforms: { + uColor: { value: color instanceof Color ? color : new Color(color) }, + }, + vertexShader: ` + varying vec3 vNormal; + varying vec3 vPosition; + + void main() { + vNormal = normalize(normalMatrix * normal); + vPosition = (modelViewMatrix * vec4(position, 1.0)).xyz; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, + fragmentShader: ` + uniform vec3 uColor; + varying vec3 vNormal; + varying vec3 vPosition; + + void main() { + vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0)); + float diffuse = max(dot(vNormal, lightDir), 0.0); + float ambient = 0.3; + vec3 finalColor = uColor * (ambient + diffuse * 0.7); + gl_FragColor = vec4(finalColor, 1.0); + } + `, + }); +}; diff --git a/src/world/World.tsx b/src/world/World.tsx index bad4fc2..0f77b35 100644 --- a/src/world/World.tsx +++ b/src/world/World.tsx @@ -28,6 +28,7 @@ import { GameMap } from "@/world/GameMap"; import { GameStageContent } from "@/world/GameStageContent"; import { Player } from "@/world/player/Player"; import { TestMap } from "@/world/debug/TestMap"; +import { NetTest } from "@/components/three/debug/NetTest"; import type { SceneLoadingChangeHandler } from "@/types/world/sceneLoading"; interface WorldProps { @@ -99,6 +100,7 @@ export function World({ onLoadingStateChange }: WorldProps): React.JSX.Element { ) : ( + )} {sceneMode !== "game" && spawnPlayer ? ( -- 2.52.0