feat: rewrite grass LOD system and visuals
This commit is contained in:
+43
-25
@@ -84,6 +84,7 @@ interface GrassProps {
|
||||
detailScale: number;
|
||||
detailHeight: number;
|
||||
noise2D: (x: number, y: number) => number;
|
||||
grassLOD: number;
|
||||
enableShadows?: boolean;
|
||||
}
|
||||
|
||||
@@ -98,7 +99,8 @@ function Grass({
|
||||
hillHeight,
|
||||
detailScale,
|
||||
detailHeight,
|
||||
noise2D
|
||||
noise2D,
|
||||
grassLOD
|
||||
}: GrassProps) {
|
||||
const meshRef = useRef<InstancedMesh>(null);
|
||||
const dummyRef = useRef<Object3D>(new Object3D());
|
||||
@@ -120,6 +122,8 @@ function Grass({
|
||||
) {
|
||||
(materialRef.current.userData.shader as Shader).uniforms.uTime.value =
|
||||
state.clock.getElapsedTime();
|
||||
(materialRef.current.userData.shader as Shader).uniforms.uLOD.value =
|
||||
grassLOD;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -239,15 +243,18 @@ function Grass({
|
||||
hillHeight,
|
||||
detailScale,
|
||||
detailHeight,
|
||||
noise2D
|
||||
noise2D,
|
||||
grassLOD
|
||||
]);
|
||||
|
||||
const onBeforeCompile = useMemo(
|
||||
() => (shader: Shader) => {
|
||||
shader.uniforms.uTime = { value: 0 };
|
||||
shader.uniforms.uLOD = { value: grassLOD };
|
||||
|
||||
shader.vertexShader = `
|
||||
uniform float uTime;
|
||||
uniform float uLOD;
|
||||
varying vec2 vGrassUv;
|
||||
|
||||
float hash(vec2 p) {
|
||||
@@ -309,7 +316,7 @@ function Grass({
|
||||
materialRef.current.userData.shader = shader;
|
||||
}
|
||||
},
|
||||
[]
|
||||
[grassLOD]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -366,19 +373,28 @@ function TerrainChunk({
|
||||
grassSize,
|
||||
grassLOD
|
||||
}: TerrainChunkProps) {
|
||||
const distance = Math.sqrt((x * size) ** 2 + (y * size) ** 2);
|
||||
|
||||
let adjustedGrassCount = grassCount;
|
||||
if (distance > grassLOD) {
|
||||
adjustedGrassCount = 0;
|
||||
} else if (distance > grassLOD * 0.6) {
|
||||
const fadeStart = grassLOD * 0.6;
|
||||
const fadeRange = grassLOD * 0.4;
|
||||
const fadeFactor = 1.0 - (distance - fadeStart) / fadeRange;
|
||||
adjustedGrassCount = Math.floor(grassCount * fadeFactor * fadeFactor);
|
||||
}
|
||||
const [showGrass, setShowGrass] = useState(false);
|
||||
const meshRef = useRef<Mesh>(null);
|
||||
|
||||
useFrame((state) => {
|
||||
const camPos = state.camera.position;
|
||||
const chunkPosX = x * size;
|
||||
const chunkPosZ = y * size;
|
||||
|
||||
const dx = camPos.x - chunkPosX;
|
||||
const dz = camPos.z - chunkPosZ;
|
||||
const distSq = dx * dx + dz * dz;
|
||||
|
||||
// Use grassLOD as the distance threshold
|
||||
const threshold = grassLOD * grassLOD;
|
||||
|
||||
if (distSq < threshold) {
|
||||
if (!showGrass) setShowGrass(true);
|
||||
} else {
|
||||
if (showGrass) setShowGrass(false);
|
||||
}
|
||||
});
|
||||
|
||||
const geometry = useMemo(() => {
|
||||
const geo = new BufferGeometry();
|
||||
|
||||
@@ -444,8 +460,8 @@ function TerrainChunk({
|
||||
const colorNoise = noise2D(globalX * 0.02, globalZ * 0.02);
|
||||
const t = (colorNoise + 1) / 2;
|
||||
|
||||
const dryGreen = { r: 0.05, g: 0.1, b: 0.0 };
|
||||
const lushGreen = { r: 0.0, g: 0.2, b: 0.0 };
|
||||
const dryGreen = { r: 0.01, g: 0.01, b: 0.0 };
|
||||
const lushGreen = { r: 0.0, g: 0.05, b: 0.0 };
|
||||
|
||||
const r = dryGreen.r + (lushGreen.r - dryGreen.r) * t;
|
||||
const g = dryGreen.g + (lushGreen.g - dryGreen.g) * t;
|
||||
@@ -489,12 +505,12 @@ function TerrainChunk({
|
||||
wireframe={wireframe}
|
||||
/>
|
||||
</mesh>
|
||||
{!wireframe && adjustedGrassCount > 0 && (
|
||||
{!wireframe && showGrass && (
|
||||
<Grass
|
||||
x={x}
|
||||
y={y}
|
||||
size={size}
|
||||
count={adjustedGrassCount}
|
||||
count={grassCount}
|
||||
grassSize={grassSize}
|
||||
scale={scale}
|
||||
hillScale={hillScale}
|
||||
@@ -502,6 +518,7 @@ function TerrainChunk({
|
||||
detailScale={detailScale}
|
||||
detailHeight={detailHeight}
|
||||
noise2D={noise2D}
|
||||
grassLOD={grassLOD}
|
||||
/>
|
||||
)}
|
||||
</group>
|
||||
@@ -622,16 +639,17 @@ export default function Home() {
|
||||
className='canvas'
|
||||
>
|
||||
<EffectComposer>
|
||||
<DepthOfField target={[0, 5, 0]} focalLength={20} bokehScale={5} />
|
||||
{/* <DepthOfField target={[0, 5, 0]} focalLength={10} bokehScale={5} /> */}
|
||||
{/* <Pixelation granularity={6} /> */}
|
||||
<Vignette />
|
||||
<Noise opacity={0.05} />
|
||||
<Bloom
|
||||
intensity={2.2}
|
||||
intensity={2}
|
||||
luminanceThreshold={0.5}
|
||||
luminanceSmoothing={0.1}
|
||||
/>
|
||||
{sealMesh ? (
|
||||
|
||||
{/* {sealMesh ? (
|
||||
<GodRays
|
||||
sun={sealMesh}
|
||||
samples={16}
|
||||
@@ -644,7 +662,7 @@ export default function Home() {
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
)} */}
|
||||
</EffectComposer>
|
||||
|
||||
<Environment files={'hdr/sky.hdr'} environmentIntensity={1} background />
|
||||
@@ -661,13 +679,13 @@ export default function Home() {
|
||||
wireframe={false}
|
||||
grassCount={8000}
|
||||
grassSize={0.6}
|
||||
grassLOD={60}
|
||||
grassLOD={80}
|
||||
/>
|
||||
<SealCube
|
||||
{/* <SealCube
|
||||
ref={(mesh) => {
|
||||
if (mesh && !sealMesh) setSealMesh(mesh);
|
||||
}}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<OrbitControls
|
||||
target={[0, 5, 0]}
|
||||
|
||||
Reference in New Issue
Block a user