Weitere Bugfixes und kleine Feature Erweiterungen

This commit is contained in:
2026-08-10 19:41:32 +02:00
parent 753af2dc13
commit 145db8317f
20 changed files with 652 additions and 211 deletions

View File

@@ -0,0 +1,307 @@
// Blight-Override von JME3's Shadows.glsllib
// Änderung: SHADOW_BIAS wird einmal in getDirectionalLightShadows() von
// projCoord.z abgezogen (Empfänger-seitiger Depth-Bias).
// Das behebt Self-Shadowing-Acne bei hohem Sonnenstand, ohne den
// Pre-Shadow-PolyOffset zu erhöhen.
// Wert anpassen: bei verbleibendem Acne erhöhen, bei "Peter Panning" verkleinern.
#define SHADOW_BIAS 0.0002
#if __VERSION__ >= 130
#ifdef GL_ES
#extension GL_OES_gpu_shader5 : enable
#else
#extension GL_ARB_gpu_shader5 : enable
#endif
#define IVEC2 ivec2
#ifdef HARDWARE_SHADOWS
#define SHADOWMAP sampler2DShadow
#define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset)
#define SHADOWCOMPARE(tex,coord) textureProj(tex, coord)
#if defined GL_ES && __VERSION__ <= 300
#define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
#else
#define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
#endif
#else
#define SHADOWMAP sampler2D
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r)
#define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r)
#define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
#endif
#if FILTER_MODE == 10
#define GETSHADOW Shadow_Nearest
#define KERNEL 1.0
#elif FILTER_MODE == 1
#ifdef HARDWARE_SHADOWS
#define GETSHADOW Shadow_Nearest
#else
#define GETSHADOW Shadow_DoBilinear_2x2
#endif
#define KERNEL 1.0
#endif
#else
#define IVEC2 vec2
#if defined GL_ES
#define SHADOWMAP sampler2D
#define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
#elif defined HARDWARE_SHADOWS
#define SHADOWMAP sampler2DShadow
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
#else
#define SHADOWMAP sampler2D
#define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
#endif
#if FILTER_MODE == 10
#define GETSHADOW Shadow_DoShadowCompare
#define KERNEL 1.0
#elif FILTER_MODE == 1
#ifdef HARDWARE_SHADOWS
#define GETSHADOW Shadow_DoShadowCompare
#else
#define GETSHADOW Shadow_DoBilinear_2x2
#endif
#define KERNEL 1.0
#endif
#endif
#if (FILTER_MODE == 2)
#define GETSHADOW Shadow_DoDither_2x2
#define KERNEL 1.0
#elif FILTER_MODE == 3
#define GETSHADOW Shadow_DoPCF
#define KERNEL 4.0
#elif FILTER_MODE == 4
#define GETSHADOW Shadow_DoPCFPoisson
#define KERNEL 4.0
#elif FILTER_MODE == 5
#define GETSHADOW Shadow_DoPCF
#define KERNEL 8.0
#endif
uniform SHADOWMAP m_ShadowMap0;
uniform SHADOWMAP m_ShadowMap1;
uniform SHADOWMAP m_ShadowMap2;
uniform SHADOWMAP m_ShadowMap3;
#ifdef POINTLIGHT
uniform SHADOWMAP m_ShadowMap4;
uniform SHADOWMAP m_ShadowMap5;
#endif
#ifdef PSSM
uniform vec4 m_Splits;
#endif
uniform float m_ShadowIntensity;
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
float shadowBorderScale = 1.0;
float Shadow_DoShadowCompare(in SHADOWMAP tex,in vec4 projCoord){
return SHADOWCOMPARE(tex, projCoord);
}
float Shadow_BorderCheck(in vec2 coord){
#ifdef GL_ES
return 0.0;
#else
vec4 t = vec4(coord.xy, 0.0, 1.0);
t = step(t.wwxy, t.xyzz);
return dot(t,t);
#endif
}
float Shadow_Nearest(in SHADOWMAP tex,in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0){
return 1.0;
}
return SHADOWCOMPARE(tex, projCoord);
}
float Shadow_DoShadowCompareOffset(in SHADOWMAP tex,in vec4 projCoord,in vec2 offset){
vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2 * shadowBorderScale, projCoord.zw);
return SHADOWCOMPARE(tex, coord);
}
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
float shadow = 0.0;
vec2 o = vec2(IVEC2(mod(floor(gl_FragCoord.xy), 2.0)));
shadow += Shadow_DoShadowCompareOffset(tex, projCoord, (vec2(-1.5, 1.5)+o));
shadow += Shadow_DoShadowCompareOffset(tex, projCoord, (vec2( 0.5, 1.5)+o));
shadow += Shadow_DoShadowCompareOffset(tex, projCoord, (vec2(-1.5, -0.5)+o));
shadow += Shadow_DoShadowCompareOffset(tex, projCoord, (vec2( 0.5, -0.5)+o));
shadow *= 0.25;
return shadow;
}
float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0){
return 1.0;
}
vec4 gather = vec4(0.0);
#if __VERSION__ >= 130
#if defined GL_ARB_gpu_shader5 || defined GL_OES_gpu_shader5
vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0);
gather = SHADOWGATHER(tex, coord);
#else
gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0));
#endif
#else
gather.x = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 0.0));
gather.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0));
gather.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0));
gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 1.0));
#endif
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
vec2 mx = mix( gather.wx, gather.zy, f.x );
return mix( mx.x, mx.y, f.y );
}
float Shadow_DoPCF(in SHADOWMAP tex,in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
const float bound = (KERNEL * 0.5 - 0.5 ) * PCFEDGE;
for (float y = -bound; y <= bound; y += PCFEDGE){
for (float x = -bound; x <= bound; x += PCFEDGE){
#if __VERSION__ < 130
shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) + border, 0.0, 1.0);
#else
shadow += Shadow_DoShadowCompareOffset(tex, projCoord, vec2(x,y));
#endif
}
}
shadow = shadow / (KERNEL * KERNEL);
return shadow;
}
//12 tap poisson disk
const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016);
const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201);
const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521);
const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819);
const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957);
const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841);
const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516);
const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126);
const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693);
const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707);
const vec2 poissonDisk10 = vec2(0.5653144, 0.60262);
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0){
return 1.0;
}
vec2 texelSize = pixSize2 * 4.0 * PCFEDGE * shadowBorderScale;
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk0 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw));
//this is divided by 12
return shadow * 0.08333333333;
}
#ifdef POINTLIGHT
float getPointLightShadows(in vec4 worldPos,in vec3 lightPos,
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,in SHADOWMAP shadowMap4,in SHADOWMAP shadowMap5,
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3,in vec4 projCoord4,in vec4 projCoord5){
float shadow = 1.0;
vec3 vect = worldPos.xyz - lightPos;
vec3 absv= abs(vect);
float maxComp = max(absv.x,max(absv.y,absv.z));
if(maxComp == absv.y){
if(vect.y < 0.0){
shadow = GETSHADOW(shadowMap0, projCoord0 / projCoord0.w);
}else{
shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w);
}
}else if(maxComp == absv.z){
if(vect.z < 0.0){
shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w);
}else{
shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w);
}
}else if(maxComp == absv.x){
if(vect.x < 0.0){
shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w);
}else{
shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w);
}
}
return shadow;
}
#else
#ifdef PSSM
float getDirectionalLightShadows(in vec4 splits,in float shadowPosition,
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3){
// Receiver-seitiger Depth-Bias: verhindert Self-Shadowing-Acne bei
// flachen Oberflächen und hohem Sonnenstand (PSSM nutzt orthografische
// Projektion → w=1, daher direkt von z abziehen).
vec4 pc0 = vec4(projCoord0.xy, projCoord0.z - SHADOW_BIAS, projCoord0.w);
vec4 pc1 = vec4(projCoord1.xy, projCoord1.z - SHADOW_BIAS, projCoord1.w);
vec4 pc2 = vec4(projCoord2.xy, projCoord2.z - SHADOW_BIAS, projCoord2.w);
vec4 pc3 = vec4(projCoord3.xy, projCoord3.z - SHADOW_BIAS, projCoord3.w);
float shadow = 1.0;
if(shadowPosition < splits.x){
shadow = GETSHADOW(shadowMap0, pc0);
}else if( shadowPosition < splits.y){
shadowBorderScale = 0.5;
shadow = GETSHADOW(shadowMap1, pc1);
}else if( shadowPosition < splits.z){
shadowBorderScale = 0.25;
shadow = GETSHADOW(shadowMap2, pc2);
}else if( shadowPosition < splits.w){
shadowBorderScale = 0.125;
shadow = GETSHADOW(shadowMap3, pc3);
}
return shadow;
}
#else
float getSpotLightShadows(in SHADOWMAP shadowMap,in vec4 projCoord){
float shadow = 1.0;
projCoord /= projCoord.w;
shadow = GETSHADOW(shadowMap,projCoord);
projCoord = projCoord * 2.0 - 1.0;
float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1;
return mix(shadow,1.0,clamp(fallOff,0.0,1.0));
}
#endif
#endif

View File

@@ -12,6 +12,7 @@ MaterialDef BakedTerrain {
Vector3 SunColor : 0.68 0.65 0.60
Vector3 AmbientColor : 0.08 0.10 0.16
Boolean DebugNoLight
Float Wetness
}
Technique {

View File

@@ -13,10 +13,11 @@ uniform sampler2D m_AlphaMap_2;
uniform int m_SteepIndex;
#endif
uniform vec3 m_LightDir;
uniform vec3 m_SunColor;
uniform vec3 m_AmbientColor;
uniform vec3 g_CameraPosition;
uniform vec3 m_LightDir;
uniform vec3 m_SunColor;
uniform vec3 m_AmbientColor;
uniform vec3 g_CameraPosition;
uniform float m_Wetness;
in vec3 vWorldPos;
in vec3 vNormal;
@@ -77,15 +78,22 @@ void main() {
col = col * flatBlend + steepCol * steepBlend;
#endif
vec3 N = normalize(vNormal);
vec3 N = normalize(vNormal);
vec3 lightDir = normalize(m_LightDir);
float diff = max(dot(N, lightDir), 0.0);
vec3 light = m_AmbientColor + m_SunColor * diff;
// Nass-Effekt: Oberfläche abdunkeln + Blinn-Phong Specular Schimmer
float wet = clamp(m_Wetness, 0.0, 1.0);
col.rgb *= (1.0 - 0.20 * wet);
vec3 viewDir = normalize(g_CameraPosition - vWorldPos);
vec3 H = normalize(lightDir + viewDir);
float spec = pow(max(dot(N, H), 0.0), 80.0) * wet;
const float BRIGHTNESS = 0.80;
#ifdef DEBUG_NO_LIGHT
outColor = vec4(col.rgb * BRIGHTNESS, col.a);
#else
outColor = vec4(col.rgb * light * BRIGHTNESS, col.a);
outColor = vec4(col.rgb * light * BRIGHTNESS + vec3(spec * 0.35), col.a);
#endif
}

View File

@@ -29,13 +29,15 @@ void main() {
vec2 windN = (dot(m_WindDir, m_WindDir) > 0.001) ? normalize(m_WindDir) : vec2(0.0, 1.0);
float wavePhase = dot(worldXZ, windN);
// Pro-Halm-Zufallsphase: floor() macht sie für alle Vertices desselben Halms gleich
float randPhase = fract(sin(dot(floor(worldXZ), vec2(127.1, 311.7))) * 43758.5453) * 6.2832;
// Zwei überlagerte Sinuswellen für organische Bewegung
float sway = sin(t * 2.1 + wavePhase * 0.08) * 0.6
+ sin(t * 1.4 + wavePhase * 0.06) * 0.4;
// Zwei überlagerte Sinuswellen + Zufallsphase → kein synchroner Gleichtakt
float sway = sin(t * 2.1 + wavePhase * 0.08 + randPhase) * 0.6
+ sin(t * 1.4 + wavePhase * 0.06 + randPhase * 0.73) * 0.4;
// Mindest-Stärke damit bei wenig Wind noch Bewegung sichtbar ist
float effectiveStrength = max(m_WindStrength, 0.05);
// Halbierte Windstärke für Textur-Gras; Mindest-Stärke für minimale Bewegung
float effectiveStrength = max(m_WindStrength, 0.05) * 0.5;
// Quadratische Gewichtung: Spitze bewegt sich mehr als Basis
float bend = sway * effectiveStrength * inTexCoord.y * inTexCoord.y;

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB