diff --git a/miniwin/src/d3drm/backends/software/renderer.cpp b/miniwin/src/d3drm/backends/software/renderer.cpp index 0833bdb5..8882098e 100644 --- a/miniwin/src/d3drm/backends/software/renderer.cpp +++ b/miniwin/src/d3drm/backends/software/renderer.cpp @@ -231,16 +231,21 @@ SDL_Color Direct3DRMSoftwareRenderer::ApplyLighting( } lightVec = Normalize(lightVec); - float dotNL = normal.x * lightVec.x + normal.y * lightVec.y + normal.z * lightVec.z; + float dotNL = DotProduct(normal, lightVec); if (dotNL > 0.0f) { // Diffuse contribution diffuse.r += dotNL * lightColor.r; diffuse.g += dotNL * lightColor.g; diffuse.b += dotNL * lightColor.b; - if (appearance.shininess != 0.0f) { - // Using dotNL ignores view angle, but this matches DirectX 5 behavior. - float spec = std::pow(dotNL, appearance.shininess * m_shininessFactor); + // Specular + if (appearance.shininess > 0.0f && light.directional == 1.0f) { + D3DVECTOR viewVec = Normalize({-position.x, -position.y, -position.z}); + D3DVECTOR H = Normalize({lightVec.x + viewVec.x, lightVec.y + viewVec.y, lightVec.z + viewVec.z}); + + float dotNH = std::max(DotProduct(normal, H), 0.0f); + float spec = std::pow(dotNH, appearance.shininess); + specular.r += spec * lightColor.r; specular.g += spec * lightColor.g; specular.b += spec * lightColor.b; diff --git a/miniwin/src/d3drm/d3drmviewport.cpp b/miniwin/src/d3drm/d3drmviewport.cpp index 3f60c368..c87866a7 100644 --- a/miniwin/src/d3drm/d3drmviewport.cpp +++ b/miniwin/src/d3drm/d3drmviewport.cpp @@ -595,11 +595,6 @@ bool RayIntersectsBox(const Ray& ray, const D3DRMBOX& box, float& outT) return true; } -inline float DotProduct(const D3DVECTOR& a, const D3DVECTOR& b) -{ - return a.x * b.x + a.y * b.y + a.z * b.z; -} - // Convert screen (x,y) in viewport to picking ray in world space Ray BuildPickingRay( float x, diff --git a/miniwin/src/internal/mathutils.h b/miniwin/src/internal/mathutils.h index 460a7156..4a8588e9 100644 --- a/miniwin/src/internal/mathutils.h +++ b/miniwin/src/internal/mathutils.h @@ -25,6 +25,11 @@ inline D3DVECTOR Normalize(const D3DVECTOR& v) return {0, 0, 0}; } +inline float DotProduct(const D3DVECTOR& a, const D3DVECTOR& b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + inline D3DVECTOR CrossProduct(const D3DVECTOR& a, const D3DVECTOR& b) { return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};