mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Stabilize shadows and soften foliage shadow casting
This commit is contained in:
parent
f17b15395d
commit
979c0b5592
8 changed files with 136 additions and 13 deletions
|
|
@ -41,6 +41,7 @@ uniform float uFogEnd;
|
|||
uniform sampler2DShadow uShadowMap;
|
||||
uniform mat4 uLightSpaceMatrix;
|
||||
uniform bool uShadowEnabled;
|
||||
uniform float uShadowStrength;
|
||||
|
||||
float calcShadow() {
|
||||
vec4 lsPos = uLightSpaceMatrix * vec4(FragPos, 1.0);
|
||||
|
|
@ -99,6 +100,7 @@ void main() {
|
|||
|
||||
// Shadow
|
||||
float shadow = uShadowEnabled ? calcShadow() : 1.0;
|
||||
shadow = mix(1.0, shadow, clamp(uShadowStrength, 0.0, 1.0));
|
||||
|
||||
// Combine lighting (terrain is purely diffuse — no specular on ground)
|
||||
vec3 result = ambient + shadow * diffuse;
|
||||
|
|
|
|||
|
|
@ -158,6 +158,11 @@ public:
|
|||
*/
|
||||
void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
|
||||
/**
|
||||
* Render depth-only pass for shadow casting
|
||||
*/
|
||||
void renderShadow(GLuint shadowShaderProgram);
|
||||
|
||||
/**
|
||||
* Render smoke particles (call after render())
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -180,6 +180,8 @@ private:
|
|||
uint32_t shadowDepthTex = 0;
|
||||
uint32_t shadowShaderProgram = 0;
|
||||
glm::mat4 lightSpaceMatrix = glm::mat4(1.0f);
|
||||
glm::vec3 shadowCenter = glm::vec3(0.0f);
|
||||
bool shadowCenterInitialized = false;
|
||||
|
||||
void initShadowMap();
|
||||
void renderShadowPass();
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ bool CharacterRenderer::initialize() {
|
|||
uniform sampler2DShadow uShadowMap;
|
||||
uniform mat4 uLightSpaceMatrix;
|
||||
uniform int uShadowEnabled;
|
||||
uniform float uShadowStrength;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
|
|
@ -134,6 +135,7 @@ bool CharacterRenderer::initialize() {
|
|||
shadow /= 9.0;
|
||||
}
|
||||
}
|
||||
shadow = mix(1.0, shadow, clamp(uShadowStrength, 0.0, 1.0));
|
||||
|
||||
// Ambient
|
||||
vec3 ambient = vec3(0.3);
|
||||
|
|
@ -1029,6 +1031,7 @@ void CharacterRenderer::render(const Camera& camera, const glm::mat4& view, cons
|
|||
|
||||
// Shadows
|
||||
characterShader->setUniform("uShadowEnabled", shadowEnabled ? 1 : 0);
|
||||
characterShader->setUniform("uShadowStrength", 0.65f);
|
||||
if (shadowEnabled) {
|
||||
characterShader->setUniform("uLightSpaceMatrix", lightSpaceMatrix);
|
||||
glActiveTexture(GL_TEXTURE7);
|
||||
|
|
|
|||
|
|
@ -269,6 +269,7 @@ bool M2Renderer::initialize(pipeline::AssetManager* assets) {
|
|||
uniform sampler2DShadow uShadowMap;
|
||||
uniform mat4 uLightSpaceMatrix;
|
||||
uniform bool uShadowEnabled;
|
||||
uniform float uShadowStrength;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
|
|
@ -320,6 +321,7 @@ bool M2Renderer::initialize(pipeline::AssetManager* assets) {
|
|||
shadow /= 9.0;
|
||||
}
|
||||
}
|
||||
shadow = mix(1.0, shadow, clamp(uShadowStrength, 0.0, 1.0));
|
||||
|
||||
vec3 ambient = uAmbientColor * texColor.rgb;
|
||||
vec3 diffuse = diff * texColor.rgb;
|
||||
|
|
@ -1127,6 +1129,7 @@ void M2Renderer::render(const Camera& camera, const glm::mat4& view, const glm::
|
|||
shader->setUniform("uFogStart", fogStart);
|
||||
shader->setUniform("uFogEnd", fogEnd);
|
||||
shader->setUniform("uShadowEnabled", shadowEnabled ? 1 : 0);
|
||||
shader->setUniform("uShadowStrength", 0.65f);
|
||||
if (shadowEnabled) {
|
||||
shader->setUniform("uLightSpaceMatrix", lightSpaceMatrix);
|
||||
glActiveTexture(GL_TEXTURE7);
|
||||
|
|
@ -1231,6 +1234,58 @@ void M2Renderer::render(const Camera& camera, const glm::mat4& view, const glm::
|
|||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
void M2Renderer::renderShadow(GLuint shadowShaderProgram) {
|
||||
if (instances.empty() || shadowShaderProgram == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLint modelLoc = glGetUniformLocation(shadowShaderProgram, "uModel");
|
||||
GLint useTexLoc = glGetUniformLocation(shadowShaderProgram, "uUseTexture");
|
||||
GLint texLoc = glGetUniformLocation(shadowShaderProgram, "uTexture");
|
||||
GLint alphaTestLoc = glGetUniformLocation(shadowShaderProgram, "uAlphaTest");
|
||||
GLint opacityLoc = glGetUniformLocation(shadowShaderProgram, "uShadowOpacity");
|
||||
if (modelLoc < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (useTexLoc >= 0) glUniform1i(useTexLoc, 0);
|
||||
if (alphaTestLoc >= 0) glUniform1i(alphaTestLoc, 0);
|
||||
if (opacityLoc >= 0) glUniform1f(opacityLoc, 1.0f);
|
||||
if (texLoc >= 0) glUniform1i(texLoc, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
for (const auto& instance : instances) {
|
||||
auto it = models.find(instance.modelId);
|
||||
if (it == models.end()) continue;
|
||||
|
||||
const M2ModelGPU& model = it->second;
|
||||
if (!model.isValid() || model.isSmoke) continue;
|
||||
|
||||
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, &instance.modelMatrix[0][0]);
|
||||
glBindVertexArray(model.vao);
|
||||
|
||||
for (const auto& batch : model.batches) {
|
||||
if (batch.indexCount == 0) continue;
|
||||
bool useTexture = (batch.texture != 0);
|
||||
bool alphaCutout = batch.hasAlpha;
|
||||
|
||||
// Foliage/leaf cutout batches cast softer shadows than opaque trunk geometry.
|
||||
float shadowOpacity = alphaCutout ? 0.55f : 1.0f;
|
||||
|
||||
if (useTexLoc >= 0) glUniform1i(useTexLoc, useTexture ? 1 : 0);
|
||||
if (alphaTestLoc >= 0) glUniform1i(alphaTestLoc, alphaCutout ? 1 : 0);
|
||||
if (opacityLoc >= 0) glUniform1f(opacityLoc, shadowOpacity);
|
||||
if (useTexture) {
|
||||
glBindTexture(GL_TEXTURE_2D, batch.texture);
|
||||
}
|
||||
glDrawElements(GL_TRIANGLES, batch.indexCount, GL_UNSIGNED_SHORT,
|
||||
(void*)(batch.indexStart * sizeof(uint16_t)));
|
||||
}
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void M2Renderer::renderSmokeParticles(const Camera& /*camera*/, const glm::mat4& view, const glm::mat4& projection) {
|
||||
if (smokeParticles.empty() || !smokeShader || smokeVAO == 0) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -1524,13 +1524,42 @@ uint32_t Renderer::compileShadowShader() {
|
|||
uniform mat4 uLightSpaceMatrix;
|
||||
uniform mat4 uModel;
|
||||
layout(location = 0) in vec3 aPos;
|
||||
layout(location = 4) in vec2 aTexCoord;
|
||||
out vec2 vTexCoord;
|
||||
void main() {
|
||||
vTexCoord = aTexCoord;
|
||||
gl_Position = uLightSpaceMatrix * uModel * vec4(aPos, 1.0);
|
||||
}
|
||||
)";
|
||||
const char* fragSrc = R"(
|
||||
#version 330 core
|
||||
void main() { }
|
||||
in vec2 vTexCoord;
|
||||
uniform bool uUseTexture;
|
||||
uniform sampler2D uTexture;
|
||||
uniform bool uAlphaTest;
|
||||
uniform float uShadowOpacity;
|
||||
|
||||
float hash12(vec2 p) {
|
||||
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
|
||||
p3 += dot(p3, p3.yzx + 33.33);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float opacity = clamp(uShadowOpacity, 0.0, 1.0);
|
||||
if (uUseTexture) {
|
||||
vec4 tex = texture(uTexture, vTexCoord);
|
||||
if (uAlphaTest && tex.a < 0.5) discard;
|
||||
opacity *= tex.a;
|
||||
}
|
||||
|
||||
// Stochastic alpha for soft/translucent shadow casters (foliage).
|
||||
// Use UV-space hash so pattern stays stable with camera movement.
|
||||
if (opacity < 0.999) {
|
||||
float d = hash12(floor(vTexCoord * 4096.0));
|
||||
if (d > opacity) discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
|
||||
|
|
@ -1581,8 +1610,24 @@ glm::mat4 Renderer::computeLightSpaceMatrix() {
|
|||
// Sun direction matching WMO light dir
|
||||
glm::vec3 sunDir = glm::normalize(glm::vec3(-0.3f, -0.7f, -0.6f));
|
||||
|
||||
// Center on character position
|
||||
glm::vec3 center = characterPosition;
|
||||
// Keep a stable shadow focus center and only recentre occasionally.
|
||||
glm::vec3 desiredCenter = characterPosition;
|
||||
if (!shadowCenterInitialized) {
|
||||
shadowCenter = desiredCenter;
|
||||
shadowCenterInitialized = true;
|
||||
} else {
|
||||
constexpr float recenterThreshold = 30.0f; // world units
|
||||
if (std::abs(desiredCenter.x - shadowCenter.x) > recenterThreshold ||
|
||||
std::abs(desiredCenter.y - shadowCenter.y) > recenterThreshold) {
|
||||
shadowCenter.x = desiredCenter.x;
|
||||
shadowCenter.y = desiredCenter.y;
|
||||
}
|
||||
// Avoid vertical jitter from tiny terrain/camera height changes.
|
||||
if (std::abs(desiredCenter.z - shadowCenter.z) > 4.0f) {
|
||||
shadowCenter.z = desiredCenter.z;
|
||||
}
|
||||
}
|
||||
glm::vec3 center = shadowCenter;
|
||||
|
||||
// Texel snapping: round center to shadow texel boundaries to prevent shimmer
|
||||
float halfExtent = 120.0f;
|
||||
|
|
@ -1598,10 +1643,11 @@ glm::mat4 Renderer::computeLightSpaceMatrix() {
|
|||
|
||||
// Snap center in light space to texel grid
|
||||
glm::vec4 centerLS = lightView * glm::vec4(center, 1.0f);
|
||||
centerLS.x = std::floor(centerLS.x / texelWorld) * texelWorld;
|
||||
centerLS.y = std::floor(centerLS.y / texelWorld) * texelWorld;
|
||||
centerLS.x = std::round(centerLS.x / texelWorld) * texelWorld;
|
||||
centerLS.y = std::round(centerLS.y / texelWorld) * texelWorld;
|
||||
glm::vec4 snappedCenter = glm::inverse(lightView) * centerLS;
|
||||
center = glm::vec3(snappedCenter);
|
||||
shadowCenter = center;
|
||||
|
||||
// Rebuild with snapped center
|
||||
lightView = glm::lookAt(center - sunDir * 200.0f, center, up);
|
||||
|
|
@ -1629,6 +1675,14 @@ void Renderer::renderShadowPass() {
|
|||
glUseProgram(shadowShaderProgram);
|
||||
GLint lsmLoc = glGetUniformLocation(shadowShaderProgram, "uLightSpaceMatrix");
|
||||
glUniformMatrix4fv(lsmLoc, 1, GL_FALSE, &lightSpaceMatrix[0][0]);
|
||||
GLint useTexLoc = glGetUniformLocation(shadowShaderProgram, "uUseTexture");
|
||||
GLint texLoc = glGetUniformLocation(shadowShaderProgram, "uTexture");
|
||||
GLint alphaTestLoc = glGetUniformLocation(shadowShaderProgram, "uAlphaTest");
|
||||
GLint opacityLoc = glGetUniformLocation(shadowShaderProgram, "uShadowOpacity");
|
||||
if (useTexLoc >= 0) glUniform1i(useTexLoc, 0);
|
||||
if (alphaTestLoc >= 0) glUniform1i(alphaTestLoc, 0);
|
||||
if (opacityLoc >= 0) glUniform1f(opacityLoc, 1.0f);
|
||||
if (texLoc >= 0) glUniform1i(texLoc, 0);
|
||||
|
||||
// Render terrain into shadow map
|
||||
if (terrainRenderer) {
|
||||
|
|
@ -1643,18 +1697,11 @@ void Renderer::renderShadowPass() {
|
|||
// directly by calling renderShadow with the light view/proj split.
|
||||
// For simplicity, compute the split:
|
||||
glm::vec3 sunDir = glm::normalize(glm::vec3(-0.3f, -0.7f, -0.6f));
|
||||
glm::vec3 center = characterPosition;
|
||||
glm::vec3 center = shadowCenterInitialized ? shadowCenter : characterPosition;
|
||||
float halfExtent = 120.0f;
|
||||
float texelWorld = (2.0f * halfExtent) / static_cast<float>(SHADOW_MAP_SIZE);
|
||||
glm::vec3 up(0.0f, 0.0f, 1.0f);
|
||||
if (std::abs(glm::dot(sunDir, up)) > 0.99f) up = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
glm::mat4 lightView = glm::lookAt(center - sunDir * 200.0f, center, up);
|
||||
glm::vec4 centerLS = lightView * glm::vec4(center, 1.0f);
|
||||
centerLS.x = std::floor(centerLS.x / texelWorld) * texelWorld;
|
||||
centerLS.y = std::floor(centerLS.y / texelWorld) * texelWorld;
|
||||
glm::vec4 snappedCenter = glm::inverse(lightView) * centerLS;
|
||||
center = glm::vec3(snappedCenter);
|
||||
lightView = glm::lookAt(center - sunDir * 200.0f, center, up);
|
||||
glm::mat4 lightProj = glm::ortho(-halfExtent, halfExtent, -halfExtent, halfExtent, 1.0f, 400.0f);
|
||||
|
||||
// WMO renderShadow needs a Shader reference — but it only uses setUniform("uModel", ...)
|
||||
|
|
@ -1668,6 +1715,11 @@ void Renderer::renderShadowPass() {
|
|||
shadowShaderWrapper.releaseProgram(); // Don't let wrapper delete our program
|
||||
}
|
||||
|
||||
// Render M2 doodads into shadow map
|
||||
if (m2Renderer) {
|
||||
m2Renderer->renderShadow(shadowShaderProgram);
|
||||
}
|
||||
|
||||
// Restore state
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glCullFace(GL_BACK);
|
||||
|
|
|
|||
|
|
@ -357,6 +357,7 @@ void TerrainRenderer::render(const Camera& camera) {
|
|||
|
||||
// Shadow map
|
||||
shader->setUniform("uShadowEnabled", shadowEnabled ? 1 : 0);
|
||||
shader->setUniform("uShadowStrength", 0.65f);
|
||||
if (shadowEnabled) {
|
||||
shader->setUniform("uLightSpaceMatrix", lightSpaceMatrix);
|
||||
glActiveTexture(GL_TEXTURE7);
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ bool WMORenderer::initialize(pipeline::AssetManager* assets) {
|
|||
uniform sampler2DShadow uShadowMap;
|
||||
uniform mat4 uLightSpaceMatrix;
|
||||
uniform bool uShadowEnabled;
|
||||
uniform float uShadowStrength;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
|
|
@ -137,6 +138,7 @@ bool WMORenderer::initialize(pipeline::AssetManager* assets) {
|
|||
shadow /= 9.0;
|
||||
}
|
||||
}
|
||||
shadow = mix(1.0, shadow, clamp(uShadowStrength, 0.0, 1.0));
|
||||
|
||||
// Combine lighting with texture
|
||||
vec3 result = (ambient + (diffuse + specular) * shadow) * texColor.rgb;
|
||||
|
|
@ -499,6 +501,7 @@ void WMORenderer::render(const Camera& camera, const glm::mat4& view, const glm:
|
|||
shader->setUniform("uFogStart", fogStart);
|
||||
shader->setUniform("uFogEnd", fogEnd);
|
||||
shader->setUniform("uShadowEnabled", shadowEnabled ? 1 : 0);
|
||||
shader->setUniform("uShadowStrength", 0.65f);
|
||||
if (shadowEnabled) {
|
||||
shader->setUniform("uLightSpaceMatrix", lightSpaceMatrix);
|
||||
glActiveTexture(GL_TEXTURE7);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue