Compare commits
10 Commits
6d7651dec9
...
d506071ce2
| Author | SHA1 | Date | |
|---|---|---|---|
| d506071ce2 | |||
| cad47f07bd | |||
| 10543bba89 | |||
| aeee2158ba | |||
| 569a4f29fb | |||
| 9a67a800fa | |||
| 719a75d393 | |||
| f583cfdc57 | |||
| 5665804b8f | |||
| 67bf6325fa |
+15
-9
@@ -68,10 +68,10 @@ function Scene() {
|
|||||||
grassDryColor: '#495a17',
|
grassDryColor: '#495a17',
|
||||||
grassLushColor: '#255825',
|
grassLushColor: '#255825',
|
||||||
grassCount: { value: 8000, min: 1000, max: 30000, step: 500 },
|
grassCount: { value: 8000, min: 1000, max: 30000, step: 500 },
|
||||||
grassSize: { value: 0.65, min: 0.1, max: 2.0, step: 0.05 },
|
grassSize: { value: 0.85, min: 0.1, max: 2.0, step: 0.05 },
|
||||||
grassLOD: { value: 60, min: 10, max: 200, step: 5 },
|
grassLOD: { value: 60, min: 10, max: 200, step: 5 },
|
||||||
grassBlades: { value: 3, min: 1, max: 4, step: 1 },
|
grassBlades: { value: 3, min: 1, max: 5, step: 1 },
|
||||||
grassSegments: { value: 3, min: 1, max: 5, step: 1 },
|
grassSegments: { value: 4, min: 1, max: 5, step: 1 },
|
||||||
grassLODStart: { value: 0.15, min: 0.0, max: 0.9, step: 0.05 },
|
grassLODStart: { value: 0.15, min: 0.0, max: 0.9, step: 0.05 },
|
||||||
grassLODExponent: { value: 1.8, min: 0.5, max: 3.0, step: 0.1 },
|
grassLODExponent: { value: 1.8, min: 0.5, max: 3.0, step: 0.1 },
|
||||||
})
|
})
|
||||||
@@ -80,11 +80,17 @@ function Scene() {
|
|||||||
return (<>
|
return (<>
|
||||||
<Environment
|
<Environment
|
||||||
files={'niko/hdr/sky.hdr'}
|
files={'niko/hdr/sky.hdr'}
|
||||||
environmentIntensity={1}
|
environmentIntensity={0.85}
|
||||||
background
|
background
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<fogExp2 attach='fog' args={[0x9a9a9a, 0.01]} />
|
<fogExp2 attach='fog' args={[0xa3a5ba, 0.0125]} />
|
||||||
|
|
||||||
|
<ambientLight intensity={0.5} />
|
||||||
|
<directionalLight
|
||||||
|
position={[15, 25, 15]}
|
||||||
|
intensity={1}
|
||||||
|
/>
|
||||||
|
|
||||||
<Terrain
|
<Terrain
|
||||||
chunks={chunks}
|
chunks={chunks}
|
||||||
@@ -123,9 +129,9 @@ function PostProcessing() {
|
|||||||
<Vignette />
|
<Vignette />
|
||||||
<Noise opacity={0.05} />
|
<Noise opacity={0.05} />
|
||||||
<Bloom
|
<Bloom
|
||||||
intensity={2}
|
intensity={0.8}
|
||||||
luminanceThreshold={0.5}
|
luminanceThreshold={0.4}
|
||||||
luminanceSmoothing={0.1}
|
luminanceSmoothing={0.5}
|
||||||
/>
|
/>
|
||||||
<SMAA />
|
<SMAA />
|
||||||
<HueSaturation saturation={0.3} />
|
<HueSaturation saturation={0.3} />
|
||||||
@@ -145,7 +151,7 @@ export default function Seal() {
|
|||||||
<Canvas
|
<Canvas
|
||||||
shadows
|
shadows
|
||||||
camera={{ position: [0, 5, 15], fov: 50, far: 100 }}
|
camera={{ position: [0, 5, 15], fov: 50, far: 100 }}
|
||||||
gl={{ antialias: true }}
|
gl={{ antialias: false, powerPreference: "high-performance" }}
|
||||||
className='canvas'
|
className='canvas'
|
||||||
>
|
>
|
||||||
<AmbientSound url="niko/snd/wind.mp3" volume={0.4} />
|
<AmbientSound url="niko/snd/wind.mp3" volume={0.4} />
|
||||||
|
|||||||
@@ -173,24 +173,27 @@ export default function Grass({
|
|||||||
dummy.rotation.x = (Math.random() - 0.5) * 0.2;
|
dummy.rotation.x = (Math.random() - 0.5) * 0.2;
|
||||||
dummy.rotation.z = (Math.random() - 0.5) * 0.2;
|
dummy.rotation.z = (Math.random() - 0.5) * 0.2;
|
||||||
|
|
||||||
const baseScale = grassSize + Math.random() * grassSize * 0.5;
|
const noiseVal = noise2D(globalX * 0.02, globalZ * 0.02);
|
||||||
const heightMult = 0.5 + Math.random() * 1.0;
|
const t = (noiseVal + 1) / 2;
|
||||||
dummy.scale.set(baseScale, baseScale * heightMult, baseScale);
|
const randomInternal = (Math.random() - 0.5) * 0.2;
|
||||||
|
const finalT = Math.max(0, Math.min(1, t + randomInternal));
|
||||||
|
color.lerpColors(dryColorObj, lushColorObj, finalT);
|
||||||
|
meshRef.current.setColorAt(instanceIndex, color);
|
||||||
|
|
||||||
|
const heightNoise = noise2D(globalX * 0.08, globalZ * 0.08);
|
||||||
|
const macroHeight = (heightNoise + 1.0) * 0.5; // 0..1
|
||||||
|
const microNoise = noise2D(globalX * 0.3, globalZ * 0.3);
|
||||||
|
const microHeight = (microNoise + 1.0) * 0.25; // 0..0.5
|
||||||
|
const perBladeRandom = Math.random() * 0.4;
|
||||||
|
|
||||||
|
const grassWidth = grassSize * (0.7 + Math.random() * 0.5);
|
||||||
|
const grassHeight = grassSize * (0.4 + macroHeight * 0.8 + microHeight + perBladeRandom);
|
||||||
|
|
||||||
|
dummy.scale.set(grassWidth, grassHeight, grassWidth);
|
||||||
|
|
||||||
dummy.updateMatrix();
|
dummy.updateMatrix();
|
||||||
meshRef.current.setMatrixAt(instanceIndex, dummy.matrix);
|
meshRef.current.setMatrixAt(instanceIndex, dummy.matrix);
|
||||||
|
|
||||||
const noiseVal = noise2D(globalX * 0.02, globalZ * 0.02);
|
|
||||||
|
|
||||||
const t = (noiseVal + 1) / 2;
|
|
||||||
|
|
||||||
const randomInternal = (Math.random() - 0.5) * 0.2;
|
|
||||||
const finalT = Math.max(0, Math.min(1, t + randomInternal));
|
|
||||||
|
|
||||||
color.lerpColors(dryColorObj, lushColorObj, finalT);
|
|
||||||
|
|
||||||
meshRef.current.setColorAt(instanceIndex, color);
|
|
||||||
|
|
||||||
instanceIndex++;
|
instanceIndex++;
|
||||||
}
|
}
|
||||||
meshRef.current.count = instanceIndex;
|
meshRef.current.count = instanceIndex;
|
||||||
@@ -224,6 +227,7 @@ export default function Grass({
|
|||||||
shader.vertexShader = `
|
shader.vertexShader = `
|
||||||
uniform float uTime;
|
uniform float uTime;
|
||||||
varying vec2 vGrassUv;
|
varying vec2 vGrassUv;
|
||||||
|
varying vec3 vWorldPos;
|
||||||
|
|
||||||
float hash(vec2 p) {
|
float hash(vec2 p) {
|
||||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
||||||
@@ -233,12 +237,10 @@ export default function Grass({
|
|||||||
vec2 i = floor(p);
|
vec2 i = floor(p);
|
||||||
vec2 f = fract(p);
|
vec2 f = fract(p);
|
||||||
f = f * f * (3.0 - 2.0 * f);
|
f = f * f * (3.0 - 2.0 * f);
|
||||||
|
|
||||||
float a = hash(i);
|
float a = hash(i);
|
||||||
float b = hash(i + vec2(1.0, 0.0));
|
float b = hash(i + vec2(1.0, 0.0));
|
||||||
float c = hash(i + vec2(0.0, 1.0));
|
float c = hash(i + vec2(0.0, 1.0));
|
||||||
float d = hash(i + vec2(1.0, 1.0));
|
float d = hash(i + vec2(1.0, 1.0));
|
||||||
|
|
||||||
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,23 +248,21 @@ export default function Grass({
|
|||||||
float value = 0.0;
|
float value = 0.0;
|
||||||
float amplitude = 0.5;
|
float amplitude = 0.5;
|
||||||
float frequency = 1.0;
|
float frequency = 1.0;
|
||||||
|
|
||||||
for(int i = 0; i < 4; i++) {
|
for(int i = 0; i < 4; i++) {
|
||||||
value += amplitude * noise(p * frequency);
|
value += amplitude * noise(p * frequency);
|
||||||
frequency *= 2.0;
|
frequency *= 2.0;
|
||||||
amplitude *= 0.5;
|
amplitude *= 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
${shader.vertexShader}
|
${shader.vertexShader}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
shader.vertexShader = shader.vertexShader.replace(
|
shader.vertexShader = shader.vertexShader.replace(
|
||||||
'#include <begin_vertex>',
|
'#include <begin_vertex>',
|
||||||
`
|
`
|
||||||
#include <begin_vertex>
|
#include <begin_vertex>
|
||||||
vGrassUv = uv;
|
|
||||||
${grassVert}
|
${grassVert}
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
@@ -270,8 +270,10 @@ export default function Grass({
|
|||||||
shader.fragmentShader = `
|
shader.fragmentShader = `
|
||||||
uniform float uTime;
|
uniform float uTime;
|
||||||
varying vec2 vGrassUv;
|
varying vec2 vGrassUv;
|
||||||
|
varying vec3 vWorldPos;
|
||||||
${shader.fragmentShader}
|
${shader.fragmentShader}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
shader.fragmentShader = shader.fragmentShader.replace(
|
shader.fragmentShader = shader.fragmentShader.replace(
|
||||||
'#include <color_fragment>',
|
'#include <color_fragment>',
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -12,13 +12,19 @@ const SealCube = forwardRef<Mesh>((props, ref) => {
|
|||||||
if (meshRef.current) {
|
if (meshRef.current) {
|
||||||
meshRef.current.rotation.x += delta * 0.5;
|
meshRef.current.rotation.x += delta * 0.5;
|
||||||
meshRef.current.rotation.y += delta * 0.5;
|
meshRef.current.rotation.y += delta * 0.5;
|
||||||
|
meshRef.current.position.y = 3 + Math.sin(state.clock.getElapsedTime() * 1) * 0.15;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<mesh ref={meshRef} position={[0, 3, 0]} castShadow receiveShadow>
|
<mesh ref={meshRef} position={[0, 3, 0]} castShadow receiveShadow>
|
||||||
<boxGeometry args={[0.85, 0.85, 0.85]} />
|
<boxGeometry args={[0.85, 0.85, 0.85]} />
|
||||||
<meshBasicMaterial map={texture} depthWrite={true} />
|
<meshStandardMaterial
|
||||||
|
map={texture}
|
||||||
|
roughness={0.4}
|
||||||
|
metalness={0.1}
|
||||||
|
envMapIntensity={1.2}
|
||||||
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,31 @@
|
|||||||
float ao = smoothstep(0.0, 0.7, vGrassUv.y);
|
float ao = smoothstep(0.0, 0.7, vGrassUv.y);
|
||||||
ao = mix(0.05, 1.0, ao);
|
ao = mix(0.05, 1.0, pow(ao, 1.6));
|
||||||
|
|
||||||
vec3 rootColor = diffuseColor.rgb * 0.4;
|
vec3 rootColor = diffuseColor.rgb * 0.15;
|
||||||
vec3 tipColor = diffuseColor.rgb * 1.5;
|
vec3 midColor = diffuseColor.rgb;
|
||||||
|
vec3 tipColor = diffuseColor.rgb * 1.3 + vec3(0.06, 0.08, 0.0);
|
||||||
|
|
||||||
vec3 grassColor = mix(rootColor, tipColor, vGrassUv.y);
|
float heightParam = vGrassUv.y;
|
||||||
grassColor *= ao;
|
vec3 grassColor;
|
||||||
|
if (heightParam < 0.4) {
|
||||||
|
float t = smoothstep(0.0, 0.4, heightParam);
|
||||||
|
grassColor = mix(rootColor, midColor, t);
|
||||||
|
} else {
|
||||||
|
float t = smoothstep(0.4, 1.0, heightParam);
|
||||||
|
grassColor = mix(midColor, tipColor, t);
|
||||||
|
}
|
||||||
|
|
||||||
float translucency = pow(vGrassUv.y, 2.0) * 0.5;
|
vec3 viewDir = normalize(cameraPosition - vWorldPos);
|
||||||
grassColor += diffuseColor.rgb * translucency;
|
vec3 lightDir = normalize(vec3(15.0, 25.0, 15.0));
|
||||||
|
|
||||||
diffuseColor.rgb = grassColor;
|
float VdotL = max(0.0, dot(viewDir, -lightDir));
|
||||||
|
float sss = pow(VdotL, 3.0) * smoothstep(0.2, 0.9, vGrassUv.y);
|
||||||
|
|
||||||
|
vec3 sssColor = diffuseColor.rgb * vec3(0.6, 1.0, 0.15) * 1.8;
|
||||||
|
grassColor += sssColor * sss * 2.0;
|
||||||
|
|
||||||
|
float NdotV = 1.0 - max(0.0, dot(normalize(vNormal), viewDir));
|
||||||
|
float rim = pow(NdotV, 3.0) * smoothstep(0.3, 1.0, vGrassUv.y) * 0.15;
|
||||||
|
grassColor += vec3(0.3, 0.5, 0.1) * rim;
|
||||||
|
|
||||||
|
diffuseColor.rgb = grassColor * ao;
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
vGrassUv = uv;
|
||||||
|
|
||||||
vec4 worldPos = modelMatrix * instanceMatrix * vec4(0.0, 0.0, 0.0, 1.0);
|
vec4 worldPos = modelMatrix * instanceMatrix * vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
float gx = worldPos.x;
|
float gx = worldPos.x;
|
||||||
float gz = worldPos.z;
|
float gz = worldPos.z;
|
||||||
@@ -8,7 +10,6 @@ float windSpeed = 1.5;
|
|||||||
float windTime = uTime * windSpeed;
|
float windTime = uTime * windSpeed;
|
||||||
vec2 windSamplePos = (worldPos.xz * 0.05) - (mainWindDir * windTime * 0.2);
|
vec2 windSamplePos = (worldPos.xz * 0.05) - (mainWindDir * windTime * 0.2);
|
||||||
|
|
||||||
|
|
||||||
float windBase = fbm(windSamplePos * 0.8) * 0.4 + 0.2;
|
float windBase = fbm(windSamplePos * 0.8) * 0.4 + 0.2;
|
||||||
float gustNoise = fbm(windSamplePos * 0.4);
|
float gustNoise = fbm(windSamplePos * 0.4);
|
||||||
float gust = pow(gustNoise, 3.0) * 1.8;
|
float gust = pow(gustNoise, 3.0) * 1.8;
|
||||||
@@ -20,19 +21,32 @@ float spring = sin(uTime * 2.0 + phase) * 0.06 + sin(uTime * 4.5 + phase * 1.5)
|
|||||||
float angleNoise = fbm(windSamplePos * 2.0 + uTime * 0.1) - 0.5;
|
float angleNoise = fbm(windSamplePos * 2.0 + uTime * 0.1) - 0.5;
|
||||||
vec2 windDir = normalize(mainWindDir + vec2(-mainWindDir.y, mainWindDir.x) * angleNoise * 0.4);
|
vec2 windDir = normalize(mainWindDir + vec2(-mainWindDir.y, mainWindDir.x) * angleNoise * 0.4);
|
||||||
|
|
||||||
float taperFactor = pow(uv.y, 4.0);
|
// taper (fade)
|
||||||
float taper = 1.0 - taperFactor * 0.6;
|
float taperFactor = uv.y * uv.y * uv.y;
|
||||||
|
float taper = 1.0 - taperFactor * 0.85;
|
||||||
transformed.x *= taper;
|
transformed.x *= taper;
|
||||||
transformed.z *= taper;
|
transformed.z *= taper;
|
||||||
|
|
||||||
|
// curve
|
||||||
float curveVal = fbm(vec2(gx, gz) * 0.5);
|
float curveVal = fbm(vec2(gx, gz) * 0.5);
|
||||||
float curveStrength = 2.0 + curveVal * 2.0;
|
float curveStrength = 1.5 + curveVal * 2.5;
|
||||||
float curveAmount = uv.y * uv.y * curveStrength;
|
float curveAmount = uv.y * uv.y * curveStrength;
|
||||||
vec2 curveDir = normalize(vec2(curveVal, fbm(vec2(gz, gx))) - 0.5);
|
vec2 curveDir = normalize(vec2(curveVal, fbm(vec2(gz, gx))) - 0.5);
|
||||||
transformed.x += curveAmount * curveDir.x * 0.5;
|
transformed.x += curveAmount * curveDir.x * 0.4;
|
||||||
transformed.z += curveAmount * curveDir.y * 0.5;
|
transformed.z += curveAmount * curveDir.y * 0.4;
|
||||||
|
|
||||||
|
// sway
|
||||||
float swayAmount = (totalWind + spring) * uv.y * uv.y;
|
float swayAmount = (totalWind + spring) * uv.y * uv.y;
|
||||||
transformed.x += swayAmount * windDir.x;
|
transformed.x += swayAmount * windDir.x;
|
||||||
transformed.z += swayAmount * windDir.y;
|
transformed.z += swayAmount * windDir.y;
|
||||||
transformed.y -= abs(swayAmount) * 0.2;
|
transformed.y -= abs(swayAmount) * 0.2;
|
||||||
|
|
||||||
|
// normal comp
|
||||||
|
vec2 totalBend = curveDir * curveAmount * 0.4 + windDir * swayAmount;
|
||||||
|
float bendMag = length(totalBend);
|
||||||
|
vec3 bentNormal = normalize(vec3(-totalBend.x * 0.5, 1.0, -totalBend.y * 0.5));
|
||||||
|
|
||||||
|
// normal mix
|
||||||
|
objectNormal = normalize(mix(vec3(0.0, 1.0, 0.0), bentNormal, uv.y));
|
||||||
|
|
||||||
|
vWorldPos = (modelMatrix * instanceMatrix * vec4(transformed, 1.0)).xyz;
|
||||||
Reference in New Issue
Block a user