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/planet/StoneObject.h
|
||||||
../src/render/FrameBuffer.cpp
|
../src/render/FrameBuffer.cpp
|
||||||
../src/render/FrameBuffer.h
|
../src/render/FrameBuffer.h
|
||||||
|
../src/render/ShadowMap.cpp
|
||||||
|
../src/render/ShadowMap.h
|
||||||
../src/UiManager.cpp
|
../src/UiManager.cpp
|
||||||
../src/UiManager.h
|
../src/UiManager.h
|
||||||
../src/Projectile.h
|
../src/Projectile.h
|
||||||
@ -109,6 +111,7 @@ target_compile_definitions(space-game001 PRIVATE
|
|||||||
WIN32_LEAN_AND_MEAN
|
WIN32_LEAN_AND_MEAN
|
||||||
PNG_ENABLED
|
PNG_ENABLED
|
||||||
SDL_MAIN_HANDLED
|
SDL_MAIN_HANDLED
|
||||||
|
# DEBUG_LIGHT
|
||||||
# NETWORK
|
# NETWORK
|
||||||
# SIMPLIFIED
|
# 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) {
|
void Character::prepareGpuSkinningVBOs(AnimationData& anim) {
|
||||||
if (anim.gpuSkinningPrepared) return;
|
if (anim.gpuSkinningPrepared)
|
||||||
|
{
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
anim.model.PrepareGpuSkinningData();
|
anim.model.PrepareGpuSkinningData();
|
||||||
|
|
||||||
@ -284,13 +288,18 @@ void Character::prepareGpuSkinningVBOs(AnimationData& anim) {
|
|||||||
void Character::drawGpuSkinning(Renderer& renderer) {
|
void Character::drawGpuSkinning(Renderer& renderer) {
|
||||||
AnimationState drawState = resolveActiveState();
|
AnimationState drawState = resolveActiveState();
|
||||||
auto it = animations.find(drawState);
|
auto it = animations.find(drawState);
|
||||||
if (it == animations.end() || !texture) return;
|
if (it == animations.end() || !texture)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto& anim = it->second;
|
auto& anim = it->second;
|
||||||
prepareGpuSkinningVBOs(anim);
|
prepareGpuSkinningVBOs(anim);
|
||||||
|
|
||||||
if (anim.skinningMatrices.empty()) return;
|
if (anim.skinningMatrices.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
static const std::string skinningShaderName = "skinning";
|
static const std::string skinningShaderName = "skinning";
|
||||||
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
||||||
|
|
||||||
@ -354,4 +363,217 @@ void Character::drawGpuSkinning(Renderer& renderer) {
|
|||||||
renderer.shaderManager.PopShader();
|
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
|
} // namespace ZL
|
||||||
|
|||||||
@ -34,6 +34,8 @@ public:
|
|||||||
void setTarget(const Eigen::Vector3f& target,
|
void setTarget(const Eigen::Vector3f& target,
|
||||||
std::function<void()> onArrived = nullptr);
|
std::function<void()> onArrived = nullptr);
|
||||||
void draw(Renderer& renderer);
|
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
|
// Public: read by Game for camera tracking and ray-cast origin
|
||||||
Eigen::Vector3f position = Eigen::Vector3f(0.f, 0.f, 0.f);
|
Eigen::Vector3f position = Eigen::Vector3f(0.f, 0.f, 0.f);
|
||||||
@ -97,6 +99,12 @@ private:
|
|||||||
void prepareGpuSkinningVBOs(AnimationData& anim);
|
void prepareGpuSkinningVBOs(AnimationData& anim);
|
||||||
// GPU skinning: draw using shader-based skinning
|
// GPU skinning: draw using shader-based skinning
|
||||||
void drawGpuSkinning(Renderer& renderer);
|
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
|
} // namespace ZL
|
||||||
|
|||||||
182
src/Game.cpp
182
src/Game.cpp
@ -250,7 +250,25 @@ namespace ZL
|
|||||||
npcs.push_back(std::move(npc02));
|
npcs.push_back(std::move(npc02));
|
||||||
|
|
||||||
std::cout << "Load resurces step 12" << std::endl;
|
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;
|
loadingCompleted = true;
|
||||||
|
|
||||||
dialogueSystem.init(renderer, CONST_ZIP_FILE);
|
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() {
|
void Game::drawScene() {
|
||||||
glViewport(0, 0, Environment::width, Environment::height);
|
glViewport(0, 0, Environment::width, Environment::height);
|
||||||
if (!loadingCompleted) {
|
if (!loadingCompleted) {
|
||||||
@ -462,7 +635,12 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
drawGame();
|
if (shadowMap) {
|
||||||
|
drawShadowDepthPass();
|
||||||
|
drawGameWithShadows();
|
||||||
|
} else {
|
||||||
|
drawGame();
|
||||||
|
}
|
||||||
drawUI();
|
drawUI();
|
||||||
}
|
}
|
||||||
CheckGlError();
|
CheckGlError();
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
#include "ScriptEngine.h"
|
#include "ScriptEngine.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "dialogue/DialogueSystem.h"
|
#include "dialogue/DialogueSystem.h"
|
||||||
|
#include "render/ShadowMap.h"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
@ -98,6 +99,8 @@ namespace ZL {
|
|||||||
void drawUI();
|
void drawUI();
|
||||||
void drawGame();
|
void drawGame();
|
||||||
void drawLoading();
|
void drawLoading();
|
||||||
|
void drawShadowDepthPass();
|
||||||
|
void drawGameWithShadows();
|
||||||
void handleDown(int64_t fingerId, int mx, int my);
|
void handleDown(int64_t fingerId, int mx, int my);
|
||||||
void handleUp(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);
|
void handleMotion(int64_t fingerId, int mx, int my);
|
||||||
@ -121,6 +124,9 @@ namespace ZL {
|
|||||||
//MenuManager menuManager;
|
//MenuManager menuManager;
|
||||||
Dialogue::DialogueSystem dialogueSystem;
|
Dialogue::DialogueSystem dialogueSystem;
|
||||||
//ScriptEngine scriptEngine;
|
//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