mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-09 10:33:51 +00:00
Two changes that work together: 1. terrain_mesh.cpp: bump texture-coord scale from 1× to 4× per chunk so the texture's own pattern repeats every ~8 yards instead of every ~33 yards. At 1×, the texture's repeat frequency syncs with the chunk grid and any per-chunk alpha difference reads as a hard 33-yard square. At 4× the pattern noise breaks up the boundary line and the eye stops locking onto the grid. 2. terrain.frag.glsl: widen the alpha-edge feather from 3 to 8 texels and use 9 taps instead of 5 so per-chunk alpha values bleed across the chunk boundary instead of stepping. Hard alpha steps were the second contributor to visible chunk tiles in painted regions. Reported by user via screenshot showing obvious chunk-grid artifacts in painted areas of the texture-paint editor.
144 lines
5.4 KiB
GLSL
144 lines
5.4 KiB
GLSL
#version 450
|
|
|
|
layout(set = 0, binding = 0) uniform PerFrame {
|
|
mat4 view;
|
|
mat4 projection;
|
|
mat4 lightSpaceMatrix;
|
|
vec4 lightDir;
|
|
vec4 lightColor;
|
|
vec4 ambientColor;
|
|
vec4 viewPos;
|
|
vec4 fogColor;
|
|
vec4 fogParams;
|
|
vec4 shadowParams;
|
|
};
|
|
|
|
layout(set = 1, binding = 0) uniform sampler2D uBaseTexture;
|
|
layout(set = 1, binding = 1) uniform sampler2D uLayer1Texture;
|
|
layout(set = 1, binding = 2) uniform sampler2D uLayer2Texture;
|
|
layout(set = 1, binding = 3) uniform sampler2D uLayer3Texture;
|
|
layout(set = 1, binding = 4) uniform sampler2D uLayer1Alpha;
|
|
layout(set = 1, binding = 5) uniform sampler2D uLayer2Alpha;
|
|
layout(set = 1, binding = 6) uniform sampler2D uLayer3Alpha;
|
|
|
|
layout(set = 1, binding = 7) uniform TerrainParams {
|
|
int layerCount;
|
|
int hasLayer1;
|
|
int hasLayer2;
|
|
int hasLayer3;
|
|
};
|
|
|
|
layout(set = 0, binding = 1) uniform sampler2DShadow uShadowMap;
|
|
|
|
layout(location = 0) in vec3 FragPos;
|
|
layout(location = 1) in vec3 Normal;
|
|
layout(location = 2) in vec2 TexCoord;
|
|
layout(location = 3) in vec2 LayerUV;
|
|
|
|
layout(location = 0) out vec4 outColor;
|
|
|
|
const float SHADOW_TEXEL = 1.0 / 4096.0;
|
|
|
|
float sampleShadowPCF(sampler2DShadow smap, vec3 coords) {
|
|
float shadow = 0.0;
|
|
for (int x = -1; x <= 1; ++x) {
|
|
for (int y = -1; y <= 1; ++y) {
|
|
shadow += texture(smap, vec3(coords.xy + vec2(x, y) * SHADOW_TEXEL, coords.z));
|
|
}
|
|
}
|
|
return shadow / 9.0;
|
|
}
|
|
|
|
float sampleAlpha(sampler2D tex, vec2 uv) {
|
|
// Smooth 9-tap box near chunk edges to hide alpha-map seams;
|
|
// blends gradually to avoid a visible ring at the transition.
|
|
// Wider feather (8 texels) makes per-chunk alpha differences
|
|
// bleed across the boundary so the chunk grid stops reading
|
|
// as a hard step.
|
|
vec2 edge = min(uv, 1.0 - uv);
|
|
float border = min(edge.x, edge.y);
|
|
float blurWeight = 1.0 - smoothstep(1.0 / 64.0, 8.0 / 64.0, border);
|
|
float center = texture(tex, uv).r;
|
|
if (blurWeight < 0.001) return center;
|
|
vec2 texel = vec2(1.0 / 64.0);
|
|
float avg = center;
|
|
avg += texture(tex, uv + vec2(-texel.x, 0.0)).r;
|
|
avg += texture(tex, uv + vec2( texel.x, 0.0)).r;
|
|
avg += texture(tex, uv + vec2(0.0, -texel.y)).r;
|
|
avg += texture(tex, uv + vec2(0.0, texel.y)).r;
|
|
avg += texture(tex, uv + vec2(-texel.x, -texel.y)).r;
|
|
avg += texture(tex, uv + vec2( texel.x, -texel.y)).r;
|
|
avg += texture(tex, uv + vec2(-texel.x, texel.y)).r;
|
|
avg += texture(tex, uv + vec2( texel.x, texel.y)).r;
|
|
avg *= 1.0 / 9.0;
|
|
return mix(center, avg, blurWeight);
|
|
}
|
|
|
|
void main() {
|
|
vec4 baseColor = texture(uBaseTexture, TexCoord);
|
|
|
|
// WoW terrain: layers are blended sequentially, each on top of the previous result.
|
|
// Alpha=1 means the layer fully covers everything below; alpha=0 means invisible.
|
|
vec4 finalColor = baseColor;
|
|
if (hasLayer1 != 0) {
|
|
float a1 = sampleAlpha(uLayer1Alpha, LayerUV);
|
|
finalColor = mix(finalColor, texture(uLayer1Texture, TexCoord), a1);
|
|
}
|
|
if (hasLayer2 != 0) {
|
|
float a2 = sampleAlpha(uLayer2Alpha, LayerUV);
|
|
finalColor = mix(finalColor, texture(uLayer2Texture, TexCoord), a2);
|
|
}
|
|
if (hasLayer3 != 0) {
|
|
float a3 = sampleAlpha(uLayer3Alpha, LayerUV);
|
|
finalColor = mix(finalColor, texture(uLayer3Texture, TexCoord), a3);
|
|
}
|
|
|
|
vec3 norm = normalize(Normal);
|
|
|
|
// Derivative-based normal mapping: perturb vertex normal using texture detail.
|
|
// Fade out with distance and near chunk edges (dFdx/dFdy are invalid across
|
|
// chunk draw-call boundaries, producing visible seams if not faded).
|
|
float fragDist = length(viewPos.xyz - FragPos);
|
|
float bumpFade = 1.0 - smoothstep(50.0, 125.0, fragDist);
|
|
float edgeDist = min(min(LayerUV.x, 1.0 - LayerUV.x), min(LayerUV.y, 1.0 - LayerUV.y));
|
|
bumpFade *= smoothstep(0.0, 0.06, edgeDist);
|
|
if (bumpFade > 0.001) {
|
|
float lum = dot(finalColor.rgb, vec3(0.299, 0.587, 0.114));
|
|
float dLdx = dFdx(lum);
|
|
float dLdy = dFdy(lum);
|
|
vec3 dpdx = dFdx(FragPos);
|
|
vec3 dpdy = dFdy(FragPos);
|
|
float bumpStrength = 9.0 * bumpFade;
|
|
vec3 perturbation = (dLdx * cross(norm, dpdy) + dLdy * cross(dpdx, norm)) * bumpStrength;
|
|
vec3 candidate = norm - perturbation;
|
|
float len2 = dot(candidate, candidate);
|
|
norm = (len2 > 1e-8) ? candidate * inversesqrt(len2) : norm;
|
|
}
|
|
|
|
vec3 lightDir2 = normalize(-lightDir.xyz);
|
|
vec3 ambient = ambientColor.rgb * finalColor.rgb;
|
|
float diff = max(abs(dot(norm, lightDir2)), 0.2);
|
|
vec3 diffuse = diff * lightColor.rgb * finalColor.rgb;
|
|
|
|
float shadow = 1.0;
|
|
if (shadowParams.x > 0.5) {
|
|
vec3 ldir = normalize(-lightDir.xyz);
|
|
float normalOffset = SHADOW_TEXEL * 2.0 * (1.0 - abs(dot(norm, ldir)));
|
|
vec3 biasedPos = FragPos + norm * normalOffset;
|
|
vec4 lsPos = lightSpaceMatrix * vec4(biasedPos, 1.0);
|
|
vec3 proj = lsPos.xyz / lsPos.w;
|
|
proj.xy = proj.xy * 0.5 + 0.5;
|
|
if (proj.x >= 0.0 && proj.x <= 1.0 && proj.y >= 0.0 && proj.y <= 1.0 && proj.z >= 0.0 && proj.z <= 1.0) {
|
|
float bias = max(0.0005 * (1.0 - abs(dot(norm, ldir))), 0.00005);
|
|
shadow = sampleShadowPCF(uShadowMap, vec3(proj.xy, proj.z - bias));
|
|
shadow = mix(1.0, shadow, shadowParams.y);
|
|
}
|
|
}
|
|
|
|
vec3 result = ambient + shadow * diffuse;
|
|
|
|
float fogFactor = clamp((fogParams.y - fragDist) / (fogParams.y - fogParams.x), 0.0, 1.0);
|
|
result = mix(fogColor.rgb, result, fogFactor);
|
|
|
|
outColor = vec4(result, 1.0);
|
|
}
|