Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 606dde5122 | |||
| 59e5a8c0c0 | |||
| a40ee3854b | |||
| 18246eab22 | |||
| a6a6cdd168 | |||
| d9869a469e | |||
| 0db7c08168 | |||
| 5e8ebf9491 | |||
| 4d28cf8bcb |
Binary file not shown.
Binary file not shown.
+20
-15
@@ -3,12 +3,14 @@
|
|||||||
import './page.css';
|
import './page.css';
|
||||||
|
|
||||||
import { Environment, OrbitControls, useProgress } from "@react-three/drei";
|
import { Environment, OrbitControls, useProgress } from "@react-three/drei";
|
||||||
import { Canvas } from '@react-three/fiber';
|
import { Canvas, useLoader } from '@react-three/fiber';
|
||||||
import { Bloom, BrightnessContrast, DepthOfField, EffectComposer, HueSaturation, Noise, SMAA, SSAO, Vignette } from '@react-three/postprocessing';
|
import { Bloom, BrightnessContrast, DepthOfField, EffectComposer, HueSaturation, LUT, Noise, SMAA, SSAO, Vignette } from '@react-three/postprocessing';
|
||||||
import { useLayoutEffect, useState } from "react";
|
import { useLayoutEffect, useState } from "react";
|
||||||
import { folder, useControls, Leva } from 'leva';
|
import { folder, useControls, Leva } from 'leva';
|
||||||
import SealCube from './scene-components/sealcube';
|
import SealCube from './scene-components/sealcube';
|
||||||
import Terrain from './scene-components/terrain';
|
import Terrain from './scene-components/terrain';
|
||||||
|
import { LUTCubeLoader } from 'three/examples/jsm/Addons.js';
|
||||||
|
import { AmbientSound } from './scene-components/ambient-sound';
|
||||||
|
|
||||||
function Loader() {
|
function Loader() {
|
||||||
const { progress, active } = useProgress();
|
const { progress, active } = useProgress();
|
||||||
@@ -52,15 +54,15 @@ function Scene() {
|
|||||||
grassLODExponent
|
grassLODExponent
|
||||||
} = useControls('Environment', {
|
} = useControls('Environment', {
|
||||||
Terrain: folder({
|
Terrain: folder({
|
||||||
terrainDryColor: '#20270a',
|
terrainDryColor: '#232a0c',
|
||||||
terrainLushColor: '#0f240f',
|
terrainLushColor: '#142a14',
|
||||||
chunks: { value: 16, min: 4, max: 24, step: 2 },
|
chunks: { value: 16, min: 4, max: 24, step: 2 },
|
||||||
chunkSize: { value: 10.0, min: 5.0, max: 40.0, step: 1.0 },
|
chunkSize: { value: 10.0, min: 5.0, max: 40.0, step: 1.0 },
|
||||||
resolution: { value: 8.0, min: 4.0, max: 30.0, step: 1.0 },
|
resolution: { value: 8.0, min: 4.0, max: 30.0, step: 1.0 },
|
||||||
hillScale: { value: 0.1, min: 0.01, max: 0.5, step: 0.01 },
|
hillScale: { value: 0.15, min: 0.01, max: 0.5, step: 0.01 },
|
||||||
hillHeight: { value: 6.0, min: 0.0, max: 20.0, step: 0.5 },
|
hillHeight: { value: 4.0, min: 0.0, max: 20.0, step: 0.5 },
|
||||||
detailScale: { value: 1.0, min: 0.1, max: 5.0, step: 0.1 },
|
detailScale: { value: 1.0, min: 0.1, max: 5.0, step: 0.1 },
|
||||||
detailHeight: { value: 0.2, min: 0.0, max: 2.0, step: 0.05 },
|
detailHeight: { value: 0.3, min: 0.0, max: 2.0, step: 0.05 },
|
||||||
}),
|
}),
|
||||||
Grass: folder({
|
Grass: folder({
|
||||||
grassDryColor: '#495a17',
|
grassDryColor: '#495a17',
|
||||||
@@ -110,6 +112,11 @@ function Scene() {
|
|||||||
</>)
|
</>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function LutEffect() {
|
||||||
|
const lutTexture = useLoader(LUTCubeLoader, 'niko/lut/Landscape6.cube');
|
||||||
|
return <LUT lut={lutTexture.texture3D} />;
|
||||||
|
}
|
||||||
|
|
||||||
function PostProcessing() {
|
function PostProcessing() {
|
||||||
return (<EffectComposer>
|
return (<EffectComposer>
|
||||||
<DepthOfField target={[0, 3, 0]} focalLength={10} bokehScale={5} />
|
<DepthOfField target={[0, 3, 0]} focalLength={10} bokehScale={5} />
|
||||||
@@ -121,8 +128,9 @@ function PostProcessing() {
|
|||||||
luminanceSmoothing={0.1}
|
luminanceSmoothing={0.1}
|
||||||
/>
|
/>
|
||||||
<SMAA />
|
<SMAA />
|
||||||
<HueSaturation saturation={0.1} />
|
<HueSaturation saturation={0.3} />
|
||||||
<BrightnessContrast brightness={0.05} contrast={-0.1} />
|
<BrightnessContrast brightness={0.05} contrast={-0.1} />
|
||||||
|
<LutEffect />
|
||||||
</EffectComposer>)
|
</EffectComposer>)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,21 +148,18 @@ export default function Seal() {
|
|||||||
gl={{ antialias: true }}
|
gl={{ antialias: true }}
|
||||||
className='canvas'
|
className='canvas'
|
||||||
>
|
>
|
||||||
|
<AmbientSound url="niko/snd/wind.mp3" volume={0.4}/>
|
||||||
|
<AmbientSound url="niko/snd/birds.mp3" volume={0.1}/>
|
||||||
|
|
||||||
<Scene />
|
<Scene />
|
||||||
<PostProcessing />
|
<PostProcessing />
|
||||||
|
|
||||||
<Environment
|
|
||||||
files={'niko/hdr/sky.hdr'}
|
|
||||||
environmentIntensity={1}
|
|
||||||
background
|
|
||||||
/>
|
|
||||||
|
|
||||||
<OrbitControls
|
<OrbitControls
|
||||||
target={[0, 3, 0]}
|
target={[0, 3, 0]}
|
||||||
enablePan={false}
|
enablePan={false}
|
||||||
makeDefault
|
makeDefault
|
||||||
minDistance={2}
|
minDistance={2}
|
||||||
maxDistance={10}
|
maxDistance={6}
|
||||||
/>
|
/>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
|
interface AmbientSoundProps {
|
||||||
|
url: string
|
||||||
|
volume?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AmbientSound({ url, volume = 0.5 }: AmbientSoundProps) {
|
||||||
|
const audioRef = useRef<HTMLAudioElement | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const audio = new Audio(url)
|
||||||
|
audio.loop = true
|
||||||
|
audio.volume = volume
|
||||||
|
audioRef.current = audio
|
||||||
|
|
||||||
|
const startAudio = () => {
|
||||||
|
audio.play().catch((err) => {
|
||||||
|
console.warn('Autoplay blocked. Waiting for user interaction.', err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
startAudio()
|
||||||
|
|
||||||
|
window.addEventListener('click', startAudio, { once: true })
|
||||||
|
window.addEventListener('keydown', startAudio, { once: true })
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('click', startAudio)
|
||||||
|
window.removeEventListener('keydown', startAudio)
|
||||||
|
audio.pause()
|
||||||
|
audioRef.current = null
|
||||||
|
}
|
||||||
|
}, [url])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (audioRef.current) {
|
||||||
|
audioRef.current.volume = volume
|
||||||
|
}
|
||||||
|
}, [volume])
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ interface GrassProps {
|
|||||||
grassLODExponent?: number;
|
grassLODExponent?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function({
|
export default function Grass({
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
size,
|
size,
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ interface TerrainProps {
|
|||||||
grassLODStart?: number;
|
grassLODStart?: number;
|
||||||
grassLODExponent?: number;
|
grassLODExponent?: number;
|
||||||
}
|
}
|
||||||
export default function({
|
export default function Terrain({
|
||||||
chunks = 5,
|
chunks = 5,
|
||||||
chunkSize = 10,
|
chunkSize = 10,
|
||||||
resolution = 8,
|
resolution = 8,
|
||||||
|
|||||||
Reference in New Issue
Block a user