diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 3b727720ad..a9ed2f45b8 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -223,7 +223,7 @@ void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) { Uint8x3ToFloat4(ub->materialDiffuse, gstate.materialdiffuse); } if (dirtyUniforms & DIRTY_MATSPECULAR) { - Uint8x3ToFloat4_Alpha(ub->materialSpecular, gstate.materialspecular, std::max(0.0f, PSPSpecularCoef(getFloat24(gstate.materialspecularcoef)))); + Uint8x3ToFloat4_Alpha(ub->materialSpecular, gstate.materialspecular, std::max(0.0f, PSPLightExponent(getFloat24(gstate.materialspecularcoef)))); } if (dirtyUniforms & DIRTY_MATEMISSIVE) { // We're not touching the fourth f32 here, because we store an u32 of control bits in it. @@ -243,7 +243,7 @@ void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) { // ldir is only used for spotlights. Prenormalize it. ExpandFloat24x3ToFloat4AndNormalize(ub->ldir[i], &gstate.ldir[i * 3]); ExpandFloat24x3ToFloat4(ub->latt[i], &gstate.latt[i * 3]); - float lightAngle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), getFloat24(gstate.lconv[i]) }; + float lightAngle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), PSPLightExponent(getFloat24(gstate.lconv[i])) }; CopyFloat2To4(ub->lightAngle_SpotCoef[i], lightAngle_spotCoef); Uint8x3ToFloat4(ub->lightAmbient[i], gstate.lcolor[i * 3]); Uint8x3ToFloat4(ub->lightDiffuse[i], gstate.lcolor[i * 3 + 1]); diff --git a/GPU/Common/TransformCommon.cpp b/GPU/Common/TransformCommon.cpp index 8668637569..634fcd42b1 100644 --- a/GPU/Common/TransformCommon.cpp +++ b/GPU/Common/TransformCommon.cpp @@ -40,14 +40,14 @@ Lighter::Lighter(int vertType) { materialDiffuse.a = 1.0f; materialSpecular.GetFromRGB(gstate.materialspecular); materialSpecular.a = 1.0f; - specCoef_ = PSPSpecularCoef(getFloat24(gstate.materialspecularcoef)); + specCoef_ = PSPLightExponent(getFloat24(gstate.materialspecularcoef)); viewDir_ = PSPViewDirection(gstate.viewMatrix); bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0; materialUpdate_ = hasColor ? (gstate.materialupdate & 7) : 0; for (int l = 0; l < 4; l++) { lcutoff[l] = getFloat24(gstate.lcutoff[l]); - lconv[l] = getFloat24(gstate.lconv[l]); + lconv[l] = PSPLightExponent(getFloat24(gstate.lconv[l])); int i = l * 3; if (gstate.isLightChanEnabled(l)) { lpos[l] = Vec3fFromGE(&gstate.lpos[i]); diff --git a/GPU/Common/TransformCommon.h b/GPU/Common/TransformCommon.h index 139a4e1084..a2fa4c565f 100644 --- a/GPU/Common/TransformCommon.h +++ b/GPU/Common/TransformCommon.h @@ -81,8 +81,15 @@ inline float PSPLightPow(float x, float e) { return y; } -// The GE only uses the top 4 bits of the specular coefficient's mantissa. -inline float PSPSpecularCoef(float e) { +// The exponent of the GE's lighting pow (specular and spot): the top 4 bits of the mantissa, truncated, and +// saturated below 512, so 512 and up, infinity and NaN all act as 496 (gpu/probe exp221-223). +inline float PSPLightExponent(float e) { + if (std::isnan(e)) { + return std::signbit(e) ? 0.0f : 496.0f; + } + if (e >= 512.0f) { + return 496.0f; + } u32 bits; memcpy(&bits, &e, sizeof(bits)); bits &= 0xFFF80000; diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index b93ebd085f..8d27eaa676 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -440,7 +440,8 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag // pow is close enough. WRITE(p, "float pspPow(float x, float e) {\n"); if (compat.bitwiseOps) { - WRITE(p, " float t = max(e, 0.0) * float(floatBitsToInt(max(x, 1e-30)) - 0x3F800000) + 1065353216.0;\n"); + // 1065353216 is 1.0's bits. Not written in hex: HLSL makes 0x3F800000 a uint, and the subtraction wraps. + WRITE(p, " float t = max(e, 0.0) * float(floatBitsToInt(max(x, 1e-30)) - 1065353216) + 1065353216.0;\n"); WRITE(p, " return x > 0.0 || e <= 0.0 ? intBitsToFloat(int(max(t, 0.0))) : 0.0;\n"); } else { WRITE(p, " return e <= 0.0 ? 1.0 : pow(max(x, 0.0), e);\n"); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 0ed21507aa..b43c87b522 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -550,7 +550,7 @@ void LinkedShader::UpdateUniforms(const ShaderID &vsid, const ShaderLanguageDesc SetColorUniform3(render_, &u_matemissive, gstate.materialemissive); } if (dirty & DIRTY_MATSPECULAR) { - SetColorUniform3ExtraFloat(render_, &u_matspecular, gstate.materialspecular, PSPSpecularCoef(getFloat24(gstate.materialspecularcoef))); + SetColorUniform3ExtraFloat(render_, &u_matspecular, gstate.materialspecular, PSPLightExponent(getFloat24(gstate.materialspecularcoef))); } for (int i = 0; i < 4; i++) { @@ -564,7 +564,7 @@ void LinkedShader::UpdateUniforms(const ShaderID &vsid, const ShaderLanguageDesc if (u_lightdir[i] != -1) SetFloat24Uniform3Normalized(render_, &u_lightdir[i], &gstate.ldir[i * 3]); if (u_lightatt[i] != -1) SetFloat24Uniform3(render_, &u_lightatt[i], &gstate.latt[i * 3]); if (u_lightangle_spotCoef[i] != -1) { - float lightangle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), getFloat24(gstate.lconv[i]) }; + float lightangle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), PSPLightExponent(getFloat24(gstate.lconv[i])) }; SetFloatUniform2(render_, &u_lightangle_spotCoef[i], lightangle_spotCoef); } if (u_lightambient[i] != -1) SetColorUniform3(render_, &u_lightambient[i], gstate.lcolor[i * 3]); diff --git a/GPU/Software/Lighting.cpp b/GPU/Software/Lighting.cpp index 38e18cd9ca..d214ac6631 100644 --- a/GPU/Software/Lighting.cpp +++ b/GPU/Software/Lighting.cpp @@ -141,11 +141,9 @@ void ComputeState(State *state, bool hasColor0) { if (std::isnan(lstate.spotCutoff) && std::signbit(lstate.spotCutoff)) lstate.spotCutoff = 0.0f; - lstate.spotExp = getFloat24(gstate.lconv[light]); + lstate.spotExp = PSPLightExponent(getFloat24(gstate.lconv[light])); if (lstate.spotExp <= 0.0f) lstate.spotExp = 0.0f; - else if (std::isnan(lstate.spotExp)) - lstate.spotExp = std::signbit(lstate.spotExp) ? 0.0f : INFINITY; } } @@ -181,11 +179,9 @@ void ComputeState(State *state, bool hasColor0) { } if (anyDiffuse || anySpecular) { - state->specularExp = PSPSpecularCoef(gstate.getMaterialSpecularCoef()); + state->specularExp = PSPLightExponent(gstate.getMaterialSpecularCoef()); if (state->specularExp <= 0.0f) state->specularExp = 0.0f; - else if (std::isnan(state->specularExp)) - state->specularExp = std::signbit(state->specularExp) ? 0.0f : INFINITY; } state->baseAmbientColorFactor = LightColorFactor(gstate.getAmbientRGBA(), ones); diff --git a/unittest/TestGEMath.cpp b/unittest/TestGEMath.cpp index 21e1b22532..03b5391af5 100644 --- a/unittest/TestGEMath.cpp +++ b/unittest/TestGEMath.cpp @@ -23,6 +23,7 @@ #include #include +#include "GPU/Common/TransformCommon.h" #include "GPU/Software/GEMath.h" #include "unittest/UnitTest.h" @@ -166,6 +167,14 @@ static bool TestGELightPow() { // multiply gives 0x3DA64400, and flooring 0x3F69FFFC (one step lower in the 8-bit factor, exp102). EXPECT_EQ_HEX(ToBits(GELightPow(FromBits(0x3DC22200), 1.0625f)), 0x3DA64420U); EXPECT_EQ_HEX(ToBits(GELightPow(FromBits(0x3F35E500), 0.296875f)), 0x3F6A0000U); + // The exponent keeps 5 significant bits, truncated, and saturates below 512 (exp221-223). + EXPECT_EQ_FLOAT(PSPLightExponent(127.0f), 124.0f); + EXPECT_EQ_FLOAT(PSPLightExponent(150.0f), 144.0f); + EXPECT_EQ_FLOAT(PSPLightExponent(511.0f), 496.0f); + EXPECT_EQ_FLOAT(PSPLightExponent(512.0f), 496.0f); + EXPECT_EQ_FLOAT(PSPLightExponent(INFINITY), 496.0f); + EXPECT_EQ_FLOAT(PSPLightExponent(NAN), 496.0f); + EXPECT_EQ_FLOAT(PSPLightExponent(-2.0f), -2.0f); return true; }