Added shadow maps
This commit is contained in:
parent
a47306533e
commit
d8795ec076
@ -48,6 +48,8 @@ add_executable(space-game001
|
||||
# ../src/planet/StoneObject.h
|
||||
../src/render/FrameBuffer.cpp
|
||||
../src/render/FrameBuffer.h
|
||||
../src/render/ShadowMap.cpp
|
||||
../src/render/ShadowMap.h
|
||||
../src/UiManager.cpp
|
||||
../src/UiManager.h
|
||||
../src/Projectile.h
|
||||
@ -109,6 +111,7 @@ target_compile_definitions(space-game001 PRIVATE
|
||||
WIN32_LEAN_AND_MEAN
|
||||
PNG_ENABLED
|
||||
SDL_MAIN_HANDLED
|
||||
# DEBUG_LIGHT
|
||||
# NETWORK
|
||||
# SIMPLIFIED
|
||||
)
|
||||
|
||||
19
resources/shaders/default_shadow.vertex
Normal file
19
resources/shaders/default_shadow.vertex
Normal file
@ -0,0 +1,19 @@
|
||||
attribute vec3 vPosition;
|
||||
attribute vec2 vTexCoord;
|
||||
attribute vec3 vNormal;
|
||||
|
||||
varying vec2 texCoord;
|
||||
varying vec4 fragPosLightSpace;
|
||||
varying vec3 fragNormal;
|
||||
|
||||
uniform mat4 ProjectionModelViewMatrix;
|
||||
uniform mat4 ModelViewMatrix;
|
||||
uniform mat4 uLightFromCamera;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
|
||||
texCoord = vTexCoord;
|
||||
fragPosLightSpace = uLightFromCamera * ModelViewMatrix * vec4(vPosition, 1.0);
|
||||
fragNormal = mat3(ModelViewMatrix) * vNormal;
|
||||
}
|
||||
70
resources/shaders/default_shadow_desktop.fragment
Normal file
70
resources/shaders/default_shadow_desktop.fragment
Normal file
@ -0,0 +1,70 @@
|
||||
uniform sampler2D Texture;
|
||||
uniform sampler2D uShadowMap;
|
||||
uniform vec3 uLightDir;
|
||||
|
||||
varying vec2 texCoord;
|
||||
varying vec4 fragPosLightSpace;
|
||||
varying vec3 fragNormal;
|
||||
|
||||
float computeShadow(vec4 lightSpacePos, vec3 normal)
|
||||
{
|
||||
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
|
||||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
|
||||
projCoords.z > 1.0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
// Slope-dependent bias: large for grazing angles, tiny for surfaces facing the light
|
||||
float bias = 0.0004;
|
||||
if (dot(normal, normal) > 0.001)
|
||||
{
|
||||
float cosTheta = dot(normalize(normal), -normalize(uLightDir));
|
||||
bias = max(0.002 * (1.0 - cosTheta), 0.0002);
|
||||
}
|
||||
|
||||
// 3x3 PCF (percentage-closer filtering) for softer shadows
|
||||
float shadow = 0.0;
|
||||
float texelSize = 1.0 / 2048.0;
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
float pcfDepth = texture2D(uShadowMap, projCoords.xy + vec2(float(x), float(y)) * texelSize).r;
|
||||
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||
}
|
||||
}
|
||||
shadow /= 9.0;
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color = texture2D(Texture, texCoord);
|
||||
|
||||
float ambient = 0.4;
|
||||
float diffuseStrength = 0.6;
|
||||
|
||||
// Compute diffuse term; if normals are missing (zero-length) treat as fully lit
|
||||
float diffuse = 1.0;
|
||||
vec3 n = fragNormal;
|
||||
if (dot(n, n) > 0.001)
|
||||
{
|
||||
n = normalize(n);
|
||||
diffuse = max(dot(n, -normalize(uLightDir)), 0.0);
|
||||
}
|
||||
|
||||
float shadow = computeShadow(fragPosLightSpace, fragNormal);
|
||||
|
||||
// Shadow dims only the diffuse contribution; ambient is always present
|
||||
float lighting = ambient + diffuseStrength * diffuse * (1.0 - shadow);
|
||||
color.rgb *= lighting;
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
68
resources/shaders/default_shadow_web.fragment
Normal file
68
resources/shaders/default_shadow_web.fragment
Normal file
@ -0,0 +1,68 @@
|
||||
precision mediump float;
|
||||
|
||||
uniform sampler2D Texture;
|
||||
uniform sampler2D uShadowMap;
|
||||
uniform vec3 uLightDir;
|
||||
|
||||
varying vec2 texCoord;
|
||||
varying vec4 fragPosLightSpace;
|
||||
varying vec3 fragNormal;
|
||||
|
||||
float computeShadow(vec4 lightSpacePos, vec3 normal)
|
||||
{
|
||||
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
|
||||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
|
||||
projCoords.z > 1.0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
float bias = 0.0004;
|
||||
if (dot(normal, normal) > 0.001)
|
||||
{
|
||||
float cosTheta = dot(normalize(normal), -normalize(uLightDir));
|
||||
bias = max(0.002 * (1.0 - cosTheta), 0.0002);
|
||||
}
|
||||
|
||||
float shadow = 0.0;
|
||||
float texelSize = 1.0 / 2048.0;
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
float pcfDepth = texture2D(uShadowMap, projCoords.xy + vec2(float(x), float(y)) * texelSize).r;
|
||||
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||
}
|
||||
}
|
||||
shadow /= 9.0;
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color = texture2D(Texture, texCoord);
|
||||
|
||||
float ambient = 0.4;
|
||||
float diffuseStrength = 0.6;
|
||||
|
||||
float diffuse = 1.0;
|
||||
vec3 n = fragNormal;
|
||||
if (dot(n, n) > 0.001)
|
||||
{
|
||||
n = normalize(n);
|
||||
diffuse = max(dot(n, -normalize(uLightDir)), 0.0);
|
||||
}
|
||||
|
||||
float shadow = computeShadow(fragPosLightSpace, fragNormal);
|
||||
|
||||
float lighting = ambient + diffuseStrength * diffuse * (1.0 - shadow);
|
||||
color.rgb *= lighting;
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
9
resources/shaders/shadow_depth.vertex
Normal file
9
resources/shaders/shadow_depth.vertex
Normal file
@ -0,0 +1,9 @@
|
||||
attribute vec3 vPosition;
|
||||
attribute vec2 vTexCoord;
|
||||
|
||||
uniform mat4 ProjectionModelViewMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
|
||||
}
|
||||
5
resources/shaders/shadow_depth_desktop.fragment
Normal file
5
resources/shaders/shadow_depth_desktop.fragment
Normal file
@ -0,0 +1,5 @@
|
||||
void main()
|
||||
{
|
||||
// Depth is written automatically by the GPU.
|
||||
// Nothing to do here.
|
||||
}
|
||||
47
resources/shaders/shadow_depth_skinning.vertex
Normal file
47
resources/shaders/shadow_depth_skinning.vertex
Normal file
@ -0,0 +1,47 @@
|
||||
attribute vec3 vPosition;
|
||||
attribute vec2 vTexCoord;
|
||||
attribute vec4 aBoneIndices0;
|
||||
attribute vec2 aBoneIndices1;
|
||||
attribute vec4 aBoneWeights0;
|
||||
attribute vec2 aBoneWeights1;
|
||||
|
||||
uniform mat4 ProjectionModelViewMatrix;
|
||||
uniform mat4 uBoneMatrices[64];
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 originalPos = vec4(vPosition, 1.0);
|
||||
float totalWeight = 0.0;
|
||||
|
||||
if (aBoneWeights0.x > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
|
||||
totalWeight += aBoneWeights0.x;
|
||||
}
|
||||
if (aBoneWeights0.y > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
|
||||
totalWeight += aBoneWeights0.y;
|
||||
}
|
||||
if (aBoneWeights0.z > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
|
||||
totalWeight += aBoneWeights0.z;
|
||||
}
|
||||
if (aBoneWeights0.w > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
|
||||
totalWeight += aBoneWeights0.w;
|
||||
}
|
||||
if (aBoneWeights1.x > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
|
||||
totalWeight += aBoneWeights1.x;
|
||||
}
|
||||
if (aBoneWeights1.y > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
|
||||
totalWeight += aBoneWeights1.y;
|
||||
}
|
||||
|
||||
if (totalWeight < 0.001) {
|
||||
skinnedPos = originalPos;
|
||||
}
|
||||
|
||||
gl_Position = ProjectionModelViewMatrix * skinnedPos;
|
||||
}
|
||||
6
resources/shaders/shadow_depth_web.fragment
Normal file
6
resources/shaders/shadow_depth_web.fragment
Normal file
@ -0,0 +1,6 @@
|
||||
precision mediump float;
|
||||
|
||||
void main()
|
||||
{
|
||||
// Depth is written automatically by the GPU.
|
||||
}
|
||||
65
resources/shaders/skinning_shadow.vertex
Normal file
65
resources/shaders/skinning_shadow.vertex
Normal file
@ -0,0 +1,65 @@
|
||||
attribute vec3 vPosition;
|
||||
attribute vec2 vTexCoord;
|
||||
attribute vec3 vNormal;
|
||||
attribute vec4 aBoneIndices0;
|
||||
attribute vec2 aBoneIndices1;
|
||||
attribute vec4 aBoneWeights0;
|
||||
attribute vec2 aBoneWeights1;
|
||||
|
||||
varying vec2 texCoord;
|
||||
varying vec4 fragPosLightSpace;
|
||||
varying vec3 fragNormal;
|
||||
|
||||
uniform mat4 ProjectionModelViewMatrix;
|
||||
uniform mat4 ModelViewMatrix;
|
||||
uniform mat4 uLightFromCamera;
|
||||
uniform mat4 uBoneMatrices[64];
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec3 skinnedNormal = vec3(0.0, 0.0, 0.0);
|
||||
vec4 originalPos = vec4(vPosition, 1.0);
|
||||
float totalWeight = 0.0;
|
||||
|
||||
if (aBoneWeights0.x > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
|
||||
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.x)]) * vNormal * aBoneWeights0.x;
|
||||
totalWeight += aBoneWeights0.x;
|
||||
}
|
||||
if (aBoneWeights0.y > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
|
||||
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.y)]) * vNormal * aBoneWeights0.y;
|
||||
totalWeight += aBoneWeights0.y;
|
||||
}
|
||||
if (aBoneWeights0.z > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
|
||||
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.z)]) * vNormal * aBoneWeights0.z;
|
||||
totalWeight += aBoneWeights0.z;
|
||||
}
|
||||
if (aBoneWeights0.w > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
|
||||
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.w)]) * vNormal * aBoneWeights0.w;
|
||||
totalWeight += aBoneWeights0.w;
|
||||
}
|
||||
if (aBoneWeights1.x > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
|
||||
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.x)]) * vNormal * aBoneWeights1.x;
|
||||
totalWeight += aBoneWeights1.x;
|
||||
}
|
||||
if (aBoneWeights1.y > 0.0) {
|
||||
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
|
||||
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.y)]) * vNormal * aBoneWeights1.y;
|
||||
totalWeight += aBoneWeights1.y;
|
||||
}
|
||||
|
||||
if (totalWeight < 0.001) {
|
||||
skinnedPos = originalPos;
|
||||
skinnedNormal = vNormal;
|
||||
}
|
||||
|
||||
gl_Position = ProjectionModelViewMatrix * skinnedPos;
|
||||
texCoord = vTexCoord;
|
||||
fragPosLightSpace = uLightFromCamera * ModelViewMatrix * skinnedPos;
|
||||
fragNormal = mat3(ModelViewMatrix) * skinnedNormal;
|
||||
}
|
||||
@ -249,7 +249,11 @@ void Character::draw(Renderer& renderer) {
|
||||
}
|
||||
|
||||
void Character::prepareGpuSkinningVBOs(AnimationData& anim) {
|
||||
if (anim.gpuSkinningPrepared) return;
|
||||
if (anim.gpuSkinningPrepared)
|
||||
{
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
anim.model.PrepareGpuSkinningData();
|
||||
|
||||
@ -284,13 +288,18 @@ void Character::prepareGpuSkinningVBOs(AnimationData& anim) {
|
||||
void Character::drawGpuSkinning(Renderer& renderer) {
|
||||
AnimationState drawState = resolveActiveState();
|
||||
auto it = animations.find(drawState);
|
||||
if (it == animations.end() || !texture) return;
|
||||
if (it == animations.end() || !texture)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& anim = it->second;
|
||||
prepareGpuSkinningVBOs(anim);
|
||||
|
||||
if (anim.skinningMatrices.empty()) return;
|
||||
|
||||
if (anim.skinningMatrices.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
static const std::string skinningShaderName = "skinning";
|
||||
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
||||
|
||||
@ -354,4 +363,217 @@ void Character::drawGpuSkinning(Renderer& renderer) {
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
// ==================== Shadow depth pass ====================
|
||||
|
||||
void Character::drawShadowDepth(Renderer& renderer) {
|
||||
if (useGpuSkinning) {
|
||||
drawShadowDepthGpuSkinning(renderer);
|
||||
} else {
|
||||
drawShadowDepthCpu(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
void Character::drawShadowDepthCpu(Renderer& renderer) {
|
||||
AnimationState drawState = resolveActiveState();
|
||||
auto it = animations.find(drawState);
|
||||
if (it == animations.end()) return;
|
||||
|
||||
// The caller has already pushed the shadow_depth shader and the
|
||||
// light's projection + view onto the renderer stacks.
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ position.x(), position.y(), position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(modelScale);
|
||||
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
auto& anim = it->second;
|
||||
anim.modelMutable.AssignFrom(anim.model.mesh);
|
||||
anim.modelMutable.RefreshVBO();
|
||||
renderer.DrawVertexRenderStruct(anim.modelMutable);
|
||||
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
|
||||
void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
|
||||
AnimationState drawState = resolveActiveState();
|
||||
auto it = animations.find(drawState);
|
||||
if (it == animations.end()) return;
|
||||
|
||||
auto& anim = it->second;
|
||||
prepareGpuSkinningVBOs(anim);
|
||||
if (anim.skinningMatrices.empty()) return;
|
||||
|
||||
static const std::string shadowSkinningShader = "shadow_depth_skinning";
|
||||
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
||||
|
||||
// Switch to the skinning depth shader (caller has the static depth shader active).
|
||||
renderer.shaderManager.PushShader(shadowSkinningShader);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ position.x(), position.y(), position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(modelScale);
|
||||
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||
static_cast<int>(anim.skinningMatrices.size()), false,
|
||||
anim.skinningMatrices[0].data());
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __ANDROID__
|
||||
if (anim.bindPoseMutable.vao) {
|
||||
glBindVertexArray(anim.bindPoseMutable.vao->getBuffer());
|
||||
renderer.shaderManager.EnableVertexAttribArrays();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.positionVBO->getBuffer());
|
||||
renderer.VertexAttribPointer3fv("vPosition", 0, NULL);
|
||||
|
||||
if (anim.bindPoseMutable.texCoordVBO) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.texCoordVBO->getBuffer());
|
||||
renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices0VBO->getBuffer());
|
||||
renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices1VBO->getBuffer());
|
||||
renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights0VBO->getBuffer());
|
||||
renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights1VBO->getBuffer());
|
||||
renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(anim.bindPoseMutable.data.PositionData.size()));
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
// ==================== Main pass with shadows ====================
|
||||
|
||||
void Character::drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
|
||||
if (useGpuSkinning) {
|
||||
drawGpuSkinningWithShadow(renderer, lightFromCamera, shadowMapTex, lightDirCamera);
|
||||
} else {
|
||||
drawCpuWithShadow(renderer, lightFromCamera, shadowMapTex, lightDirCamera);
|
||||
}
|
||||
}
|
||||
|
||||
void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
|
||||
AnimationState drawState = resolveActiveState();
|
||||
auto it = animations.find(drawState);
|
||||
if (it == animations.end() || !texture) return;
|
||||
|
||||
static const std::string shadowShader = "default_shadow";
|
||||
|
||||
renderer.shaderManager.PushShader(shadowShader);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.RenderUniform1i("uShadowMap", 1);
|
||||
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, shadowMapTex);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ position.x(), position.y(), position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(modelScale);
|
||||
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
auto& anim = it->second;
|
||||
anim.modelMutable.AssignFrom(anim.model.mesh);
|
||||
anim.modelMutable.RefreshVBO();
|
||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(anim.modelMutable);
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
|
||||
AnimationState drawState = resolveActiveState();
|
||||
auto it = animations.find(drawState);
|
||||
if (it == animations.end() || !texture) return;
|
||||
|
||||
auto& anim = it->second;
|
||||
prepareGpuSkinningVBOs(anim);
|
||||
if (anim.skinningMatrices.empty()) return;
|
||||
|
||||
static const std::string skinningShadowShader = "skinning_shadow";
|
||||
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
||||
|
||||
renderer.shaderManager.PushShader(skinningShadowShader);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.RenderUniform1i("uShadowMap", 1);
|
||||
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, shadowMapTex);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ position.x(), position.y(), position.z() });
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
renderer.ScaleMatrix(modelScale);
|
||||
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||
|
||||
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||
static_cast<int>(anim.skinningMatrices.size()), false,
|
||||
anim.skinningMatrices[0].data());
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __ANDROID__
|
||||
if (anim.bindPoseMutable.vao) {
|
||||
glBindVertexArray(anim.bindPoseMutable.vao->getBuffer());
|
||||
renderer.shaderManager.EnableVertexAttribArrays();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.positionVBO->getBuffer());
|
||||
renderer.VertexAttribPointer3fv("vPosition", 0, NULL);
|
||||
|
||||
if (anim.bindPoseMutable.texCoordVBO) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.texCoordVBO->getBuffer());
|
||||
renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL);
|
||||
}
|
||||
|
||||
// Bind normals for diffuse lighting (skinning_shadow shader skins them)
|
||||
if (anim.bindPoseMutable.normalVBO) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.normalVBO->getBuffer());
|
||||
renderer.VertexAttribPointer3fv("vNormal", 0, NULL);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices0VBO->getBuffer());
|
||||
renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices1VBO->getBuffer());
|
||||
renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights0VBO->getBuffer());
|
||||
renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights1VBO->getBuffer());
|
||||
renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(anim.bindPoseMutable.data.PositionData.size()));
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
|
||||
@ -34,6 +34,8 @@ public:
|
||||
void setTarget(const Eigen::Vector3f& target,
|
||||
std::function<void()> onArrived = nullptr);
|
||||
void draw(Renderer& renderer);
|
||||
void drawShadowDepth(Renderer& renderer);
|
||||
void drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera);
|
||||
|
||||
// Public: read by Game for camera tracking and ray-cast origin
|
||||
Eigen::Vector3f position = Eigen::Vector3f(0.f, 0.f, 0.f);
|
||||
@ -97,6 +99,12 @@ private:
|
||||
void prepareGpuSkinningVBOs(AnimationData& anim);
|
||||
// GPU skinning: draw using shader-based skinning
|
||||
void drawGpuSkinning(Renderer& renderer);
|
||||
// Shadow: draw into depth map (no texture, no projection push)
|
||||
void drawShadowDepthGpuSkinning(Renderer& renderer);
|
||||
void drawShadowDepthCpu(Renderer& renderer);
|
||||
// Shadow: draw with shadow-aware shaders
|
||||
void drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera);
|
||||
void drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera);
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
|
||||
182
src/Game.cpp
182
src/Game.cpp
@ -250,7 +250,25 @@ namespace ZL
|
||||
npcs.push_back(std::move(npc02));
|
||||
|
||||
std::cout << "Load resurces step 12" << std::endl;
|
||||
|
||||
|
||||
// Shadow mapping shaders
|
||||
#ifdef EMSCRIPTEN
|
||||
renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
#else
|
||||
renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||
#endif
|
||||
|
||||
// Create shadow map (2048x2048, ortho size 40, near 0.1, far 100)
|
||||
shadowMap = std::make_unique<ShadowMap>(2048, 40.0f, 0.1f, 100.0f);
|
||||
shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f));
|
||||
std::cout << "Shadow map initialized" << std::endl;
|
||||
|
||||
loadingCompleted = true;
|
||||
|
||||
dialogueSystem.init(renderer, CONST_ZIP_FILE);
|
||||
@ -455,6 +473,161 @@ namespace ZL
|
||||
|
||||
}
|
||||
|
||||
void Game::drawShadowDepthPass()
|
||||
{
|
||||
if (!shadowMap) return;
|
||||
|
||||
const Eigen::Vector3f& sceneCenter = player ? player->position : Eigen::Vector3f::Zero();
|
||||
shadowMap->updateLightSpaceMatrix(sceneCenter);
|
||||
|
||||
shadowMap->bind();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// Use front-face culling during depth pass to reduce shadow acne on lit faces
|
||||
//glCullFace(GL_FRONT);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
renderer.shaderManager.PushShader("shadow_depth");
|
||||
|
||||
// Set up light's orthographic projection
|
||||
const Eigen::Matrix4f& lightProj = shadowMap->getLightProjectionMatrix();
|
||||
const Eigen::Matrix4f& lightView = shadowMap->getLightViewMatrix();
|
||||
|
||||
// Push the light's projection matrix via the 6-param ortho overload won't
|
||||
// match our pre-computed matrix. Instead, use the raw stack approach:
|
||||
// push a dummy projection then overwrite via PushSpecialMatrix-style.
|
||||
// Simpler: push ortho then push the light view as modelview.
|
||||
renderer.PushProjectionMatrix(
|
||||
-40.0f, 40.0f,
|
||||
-40.0f, 40.0f,
|
||||
0.1f, 100.0f);
|
||||
|
||||
const Eigen::Vector3f& lightDir = shadowMap->getLightDirection();
|
||||
Eigen::Vector3f lightPos = sceneCenter - lightDir * 50.0f;
|
||||
Eigen::Vector3f up(0.0f, 1.0f, 0.0f);
|
||||
if (std::abs(lightDir.dot(up)) > 0.99f) {
|
||||
up = Eigen::Vector3f(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
// Build the light view matrix and push it
|
||||
renderer.PushSpecialMatrix(lightView);
|
||||
|
||||
// Draw static geometry
|
||||
renderer.DrawVertexRenderStruct(roomMesh);
|
||||
|
||||
for (auto& [name, gameObj] : gameObjects) {
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.isActive && intObj.texture) {
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix(intObj.position);
|
||||
renderer.DrawVertexRenderStruct(intObj.mesh);
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw characters (they handle their own skinning shader switch internally)
|
||||
if (player) player->drawShadowDepth(renderer);
|
||||
for (auto& npc : npcs) npc->drawShadowDepth(renderer);
|
||||
|
||||
renderer.PopMatrix(); // light view
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
|
||||
//glCullFace(GL_BACK);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
shadowMap->unbind();
|
||||
}
|
||||
|
||||
void Game::drawGameWithShadows()
|
||||
{
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
|
||||
#ifdef DEBUG_LIGHT
|
||||
// Debug mode: render from the light's point of view using the plain
|
||||
// textured shader so we can see what the shadow map "sees".
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
|
||||
renderer.PushProjectionMatrix(
|
||||
-40.0f, 40.0f,
|
||||
-40.0f, 40.0f,
|
||||
0.1f, 1000.0f);
|
||||
|
||||
renderer.PushSpecialMatrix(shadowMap->getLightViewMatrix());
|
||||
|
||||
#else
|
||||
static const std::string shadowShaderName = "default_shadow";
|
||||
|
||||
renderer.shaderManager.PushShader(shadowShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.RenderUniform1i("uShadowMap", 1);
|
||||
|
||||
// Bind shadow map texture to unit 1
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthTexture());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
renderer.PushMatrix();
|
||||
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraInclination, Eigen::Vector3f::UnitX())).toRotationMatrix());
|
||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraAzimuth, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
const Eigen::Vector3f& camTarget = player ? player->position : Eigen::Vector3f::Zero();
|
||||
renderer.TranslateMatrix({ -camTarget.x(), -camTarget.y(), -camTarget.z() });
|
||||
|
||||
// Capture the camera view matrix and compute uLightFromCamera
|
||||
cameraViewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
Eigen::Matrix4f cameraViewInverse = cameraViewMatrix.inverse();
|
||||
Eigen::Matrix4f lightFromCamera = shadowMap->getLightSpaceMatrix() * cameraViewInverse;
|
||||
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||
|
||||
// Light direction in camera space for diffuse lighting
|
||||
Eigen::Vector3f lightDirCamera = cameraViewMatrix.block<3,3>(0,0) * shadowMap->getLightDirection();
|
||||
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
|
||||
#endif
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, roomTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(roomMesh);
|
||||
|
||||
for (auto& [name, gameObj] : gameObjects) {
|
||||
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.isActive) {
|
||||
intObj.draw(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LIGHT
|
||||
// In debug-light mode characters use the plain shaders (draw normally
|
||||
// but from the light's viewpoint — projection/view already on stack).
|
||||
if (player) player->draw(renderer);
|
||||
for (auto& npc : npcs) npc->draw(renderer);
|
||||
#else
|
||||
// Characters use their own shadow-aware shaders
|
||||
if (player) player->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
||||
for (auto& npc : npcs) npc->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
||||
#endif
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
void Game::drawScene() {
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
if (!loadingCompleted) {
|
||||
@ -462,7 +635,12 @@ namespace ZL
|
||||
}
|
||||
else
|
||||
{
|
||||
drawGame();
|
||||
if (shadowMap) {
|
||||
drawShadowDepthPass();
|
||||
drawGameWithShadows();
|
||||
} else {
|
||||
drawGame();
|
||||
}
|
||||
drawUI();
|
||||
}
|
||||
CheckGlError();
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include "ScriptEngine.h"
|
||||
#include <unordered_map>
|
||||
#include "dialogue/DialogueSystem.h"
|
||||
#include "render/ShadowMap.h"
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ZL {
|
||||
@ -98,6 +99,8 @@ namespace ZL {
|
||||
void drawUI();
|
||||
void drawGame();
|
||||
void drawLoading();
|
||||
void drawShadowDepthPass();
|
||||
void drawGameWithShadows();
|
||||
void handleDown(int64_t fingerId, int mx, int my);
|
||||
void handleUp(int64_t fingerId, int mx, int my);
|
||||
void handleMotion(int64_t fingerId, int mx, int my);
|
||||
@ -121,6 +124,9 @@ namespace ZL {
|
||||
//MenuManager menuManager;
|
||||
Dialogue::DialogueSystem dialogueSystem;
|
||||
//ScriptEngine scriptEngine;
|
||||
|
||||
std::unique_ptr<ShadowMap> shadowMap;
|
||||
Eigen::Matrix4f cameraViewMatrix = Eigen::Matrix4f::Identity();
|
||||
};
|
||||
|
||||
|
||||
|
||||
124
src/render/ShadowMap.cpp
Normal file
124
src/render/ShadowMap.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
#include "ShadowMap.h"
|
||||
#include "Environment.h"
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
// Build a look-at view matrix (column-major, same convention as the engine).
|
||||
static Eigen::Matrix4f lookAt(const Eigen::Vector3f& eye,
|
||||
const Eigen::Vector3f& center,
|
||||
const Eigen::Vector3f& up) {
|
||||
Eigen::Vector3f f = (center - eye).normalized();
|
||||
Eigen::Vector3f s = f.cross(up).normalized();
|
||||
Eigen::Vector3f u = s.cross(f);
|
||||
|
||||
Eigen::Matrix4f m = Eigen::Matrix4f::Identity();
|
||||
m(0, 0) = s.x(); m(0, 1) = s.y(); m(0, 2) = s.z();
|
||||
m(1, 0) = u.x(); m(1, 1) = u.y(); m(1, 2) = u.z();
|
||||
m(2, 0) = -f.x(); m(2, 1) = -f.y(); m(2, 2) = -f.z();
|
||||
|
||||
m(0, 3) = -s.dot(eye);
|
||||
m(1, 3) = -u.dot(eye);
|
||||
m(2, 3) = f.dot(eye);
|
||||
return m;
|
||||
}
|
||||
|
||||
// Build an orthographic projection matrix that matches the engine's
|
||||
// MakeOrthoMatrix(xmin, xmax, ymin, ymax, zNear, zFar) exactly.
|
||||
static Eigen::Matrix4f ortho(float left, float right,
|
||||
float bottom, float top,
|
||||
float zNear, float zFar) {
|
||||
float width = right - left;
|
||||
float height = top - bottom;
|
||||
float depthRange = zFar - zNear;
|
||||
|
||||
Eigen::Matrix4f m = Eigen::Matrix4f::Zero();
|
||||
m(0, 0) = 2.0f / width;
|
||||
m(1, 1) = 2.0f / height;
|
||||
m(2, 2) = -1.0f / depthRange;
|
||||
m(0, 3) = -(right + left) / width;
|
||||
m(1, 3) = -(top + bottom) / height;
|
||||
m(2, 3) = zNear / depthRange;
|
||||
m(3, 3) = 1.0f;
|
||||
return m;
|
||||
}
|
||||
|
||||
ShadowMap::ShadowMap(int res, float orthoSz, float zNear, float zFar)
|
||||
: resolution(res)
|
||||
, orthoSize(orthoSz)
|
||||
, nearPlane(zNear)
|
||||
, farPlane(zFar)
|
||||
, lightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f).normalized())
|
||||
, lightViewMatrix(Eigen::Matrix4f::Identity())
|
||||
, lightProjectionMatrix(Eigen::Matrix4f::Identity())
|
||||
, lightSpaceMatrix(Eigen::Matrix4f::Identity())
|
||||
{
|
||||
glGenFramebuffers(1, &fbo);
|
||||
|
||||
glGenTextures(1, &depthTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, depthTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
|
||||
resolution, resolution, 0,
|
||||
GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_TEXTURE_2D, depthTexture, 0);
|
||||
|
||||
// No color buffer for this FBO — depth only.
|
||||
glDrawBuffer(GL_NONE);
|
||||
glReadBuffer(GL_NONE);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
std::cerr << "Error: Shadow map framebuffer is not complete!" << std::endl;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
ShadowMap::~ShadowMap() {
|
||||
if (fbo) glDeleteFramebuffers(1, &fbo);
|
||||
if (depthTexture) glDeleteTextures(1, &depthTexture);
|
||||
}
|
||||
|
||||
void ShadowMap::setLightDirection(const Eigen::Vector3f& dir) {
|
||||
lightDirection = dir.normalized();
|
||||
}
|
||||
|
||||
void ShadowMap::updateLightSpaceMatrix(const Eigen::Vector3f& sceneCenter) {
|
||||
// Place the light "camera" looking at the scene center from the light direction.
|
||||
Eigen::Vector3f lightPos = sceneCenter - lightDirection * (farPlane * 0.5f);
|
||||
|
||||
// Choose an up vector that isn't parallel to the light direction.
|
||||
Eigen::Vector3f up(0.0f, 1.0f, 0.0f);
|
||||
if (std::abs(lightDirection.dot(up)) > 0.99f) {
|
||||
up = Eigen::Vector3f(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
lightViewMatrix = lookAt(lightPos, sceneCenter, up);
|
||||
|
||||
lightProjectionMatrix = ortho(
|
||||
-orthoSize, orthoSize,
|
||||
-orthoSize, orthoSize,
|
||||
nearPlane, farPlane);
|
||||
|
||||
lightSpaceMatrix = lightProjectionMatrix * lightViewMatrix;
|
||||
}
|
||||
|
||||
void ShadowMap::bind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glViewport(0, 0, resolution, resolution);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void ShadowMap::unbind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
44
src/render/ShadowMap.h
Normal file
44
src/render/ShadowMap.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#include <Eigen/Dense>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
class ShadowMap {
|
||||
private:
|
||||
GLuint fbo = 0;
|
||||
GLuint depthTexture = 0;
|
||||
int resolution;
|
||||
|
||||
Eigen::Vector3f lightDirection;
|
||||
Eigen::Matrix4f lightViewMatrix;
|
||||
Eigen::Matrix4f lightProjectionMatrix;
|
||||
Eigen::Matrix4f lightSpaceMatrix;
|
||||
|
||||
float orthoSize;
|
||||
float nearPlane;
|
||||
float farPlane;
|
||||
|
||||
public:
|
||||
ShadowMap(int resolution = 2048, float orthoSize = 40.0f,
|
||||
float zNear = 0.1f, float zFar = 100.0f);
|
||||
~ShadowMap();
|
||||
|
||||
ShadowMap(const ShadowMap&) = delete;
|
||||
ShadowMap& operator=(const ShadowMap&) = delete;
|
||||
|
||||
void setLightDirection(const Eigen::Vector3f& dir);
|
||||
void updateLightSpaceMatrix(const Eigen::Vector3f& sceneCenter);
|
||||
|
||||
void bind();
|
||||
void unbind();
|
||||
|
||||
GLuint getDepthTexture() const { return depthTexture; }
|
||||
int getResolution() const { return resolution; }
|
||||
const Eigen::Matrix4f& getLightSpaceMatrix() const { return lightSpaceMatrix; }
|
||||
const Eigen::Matrix4f& getLightViewMatrix() const { return lightViewMatrix; }
|
||||
const Eigen::Matrix4f& getLightProjectionMatrix() const { return lightProjectionMatrix; }
|
||||
const Eigen::Vector3f& getLightDirection() const { return lightDirection; }
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
Loading…
Reference in New Issue
Block a user