faster lighting

This commit is contained in:
tresabhi committed 2026-08-10 16:42:35 -05:00
1 parent 9b7aa13d61
commit 7739dbd0a3
4 files changed
+19 -23

No files matched your search

@@ -1,3 +1,4 @@
import { ContactShadows } from "@react-three/drei";
import { useFrame } from "@react-three/fiber"; import { useFrame } from "@react-three/fiber";
import { clamp, times } from "lodash-es"; import { clamp, times } from "lodash-es";
import { Quicklime } from "quicklime"; import { Quicklime } from "quicklime";
@@ -12,25 +13,24 @@ import { HemisphereLight, SpotLight, type Group } from "three";
import { degToRad, lerp } from "three/src/math/MathUtils.js"; import { degToRad, lerp } from "three/src/math/MathUtils.js";
import { Tankopedia } from "../../../../../../stores/tankopedia"; import { Tankopedia } from "../../../../../../stores/tankopedia";
import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent"; import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent";
import { HelpingSpotLight } from "../../../../../HelpingSpotLight";
const ANGLE = degToRad(12); const ANGLE = degToRad(12);
const REVEAL_ANIMATION_TIME = 3; const REVEAL_ANIMATION_TIME = 3;
const TRANSITION_ANIMATION_TIME = 0.5; const TRANSITION_ANIMATION_TIME = 0.5;
const LIGHTS_COUNT = 4; const LIGHTS_COUNT = 3;
const THETA_OFFSET = degToRad(180 - 45); const THETA_OFFSET = degToRad(180 - 45);
const LIGHT_DISTANCE = 20; const LIGHT_DISTANCE = 20;
const LIGHT_HEIGHT_0 = 4; const LIGHT_HEIGHT_0 = 4;
const LIGHT_HEIGHT_1 = 6; const LIGHT_HEIGHT_1 = 6;
const INTENSITY_0 = 6 * 9; const INTENSITY_0 = 2 ** 6.5;
const INTENSITY_1 = 3 * 9; const INTENSITY_1 = 2 ** 3.5;
const HEMISPHERE_INTENSITY = 2; const HEMISPHERE_INTENSITY = 2 ** 1.2;
export const transitionEvent = new Quicklime<number>(0); export const transitionEvent = new Quicklime<number>(0);
export function Lighting() { export function Lighting() {
const wrapper = useRef<Group>(null!); const wrapper = useRef<Group>(null);
const highGraphics = TankopediaPersistent.use((state) => state.highGraphics); const highGraphics = TankopediaPersistent.use((state) => state.highGraphics);
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay); const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
@@ -61,6 +61,8 @@ export function Lighting() {
return ( return (
<> <>
<ContactShadows blur={2 ** 1} opacity={2 ** 1} />
{animate && ( {animate && (
<Animator <Animator
t0={t0} t0={t0}
@@ -73,7 +75,7 @@ export function Lighting() {
)} )}
<group ref={wrapper}> <group ref={wrapper}>
<hemisphereLight intensity={0} color="#ffffff" groundColor="#afafaf" /> <hemisphereLight intensity={0} color="#d2e9ff" groundColor="#85490c" />
{times(LIGHTS_COUNT, (index) => { {times(LIGHTS_COUNT, (index) => {
const x = index / (LIGHTS_COUNT - 1); const x = index / (LIGHTS_COUNT - 1);
@@ -86,8 +88,7 @@ export function Lighting() {
const intensity = lerp(INTENSITY_0, INTENSITY_1, x); const intensity = lerp(INTENSITY_0, INTENSITY_1, x);
return ( return (
<HelpingSpotLight <spotLight
userData={{ index }}
key={index} key={index}
position={position} position={position}
intensity={intensity} intensity={intensity}
@@ -109,13 +110,15 @@ interface AnimatorProps {
t0: RefObject<number>; t0: RefObject<number>;
animationTime: RefObject<number>; animationTime: RefObject<number>;
wrapper: RefObject<Group>; wrapper: RefObject<Group | null>;
} }
function Animator({ stop, t0, animationTime, wrapper }: AnimatorProps) { function Animator({ stop, t0, animationTime, wrapper }: AnimatorProps) {
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay); const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
const apply = useCallback((t: number) => { const apply = useCallback((t: number) => {
if (wrapper.current === null) return;
for (const child of wrapper.current.children) { for (const child of wrapper.current.children) {
if (child instanceof SpotLight) { if (child instanceof SpotLight) {
child.angle = ANGLE * t; child.angle = ANGLE * t;
@@ -3,7 +3,7 @@ import { Quicklime, type QuicklimeEvent } from "quicklime";
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { Mesh, MeshStandardMaterial } from "three"; import { Mesh, MeshStandardMaterial } from "three";
const SIZE = 2 ** 4; const SIZE = 2 ** 5;
export const screenshotReadyEvent = new Quicklime(false); export const screenshotReadyEvent = new Quicklime(false);
@@ -26,6 +26,7 @@ export function SceneProps() {
return ( return (
<mesh <mesh
position={[0, -(2 ** -8), 0]}
rotation={[-Math.PI / 2, 0, 0]} rotation={[-Math.PI / 2, 0, 0]}
receiveShadow receiveShadow
visible={!screenshotReadyEvent.last!} visible={!screenshotReadyEvent.last!}
@@ -36,7 +37,7 @@ export function SceneProps() {
ref={material} ref={material}
color="black" color="black"
roughness={1} roughness={1}
metalness={0} metalness={1}
/> />
</mesh> </mesh>
); );
@@ -5,7 +5,6 @@ import {
useImperativeHandle, useImperativeHandle,
useRef, useRef,
} from "react"; } from "react";
import { Fog } from "three";
import { applyPitchYawLimits } from "../../../../../core/blitz/applyPitchYawLimits"; import { applyPitchYawLimits } from "../../../../../core/blitz/applyPitchYawLimits";
import { modelTransformEvent } from "../../../../../core/blitzkit/modelTransform"; import { modelTransformEvent } from "../../../../../core/blitzkit/modelTransform";
import { Pose, poseEvent } from "../../../../../core/blitzkit/pose"; import { Pose, poseEvent } from "../../../../../core/blitzkit/pose";
@@ -150,7 +149,6 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
<SmartCanvas <SmartCanvas
resize={{ debounce: 0 }} resize={{ debounce: 0 }}
ref={canvas} ref={canvas}
scene={{ fog: new Fog("black", 30, 50) }}
gl={{ gl={{
clippingPlanes: [], clippingPlanes: [],
localClippingEnabled: true, localClippingEnabled: true,
@@ -380,7 +380,7 @@ async function extractModel(vfs: AbstractVFS, path: string) {
async function readBaseColor(path: string) { async function readBaseColor(path: string) {
const raw = await readTexture(path); const raw = await readTexture(path);
const image = await sharp(raw.data, { raw: raw }).webp().toBuffer(); const image = await sharp(raw.data, { raw }).webp().toBuffer();
return image; return image;
} }
@@ -388,13 +388,7 @@ async function readBaseColor(path: string) {
async function readOcclusion(path: string) { async function readOcclusion(path: string) {
const raw = await readTexture(path); const raw = await readTexture(path);
const image = await sharp(raw.data, { const image = await sharp(raw.data, { raw })
raw: {
width: raw.width,
height: raw.height,
channels: raw.channels,
},
})
.extractChannel(3) .extractChannel(3)
.webp() .webp()
.toBuffer(); .toBuffer();
@@ -404,7 +398,7 @@ async function readOcclusion(path: string) {
async function readRoughnessMetallic(path: string) { async function readRoughnessMetallic(path: string) {
const raw = await readTexture(path); const raw = await readTexture(path);
const blitz = sharp(raw.data, { raw: raw }); const blitz = sharp(raw.data, { raw });
const metallicness = await blitz?.extractChannel(1).raw().toBuffer(); const metallicness = await blitz?.extractChannel(1).raw().toBuffer();
const roughness = await blitz?.extractChannel(3).raw().toBuffer(); const roughness = await blitz?.extractChannel(3).raw().toBuffer();