diff --git a/include/aurora/gfx.h b/include/aurora/gfx.h index 0319345..e353a4b 100644 --- a/include/aurora/gfx.h +++ b/include/aurora/gfx.h @@ -32,6 +32,22 @@ const AuroraStats* aurora_get_stats(); void aurora_enable_vsync(bool enabled); +void aurora_set_enhanced_lighting(bool enabled); +bool aurora_get_enhanced_lighting(); + +void aurora_set_specular_lighting(bool enabled); +bool aurora_get_specular_lighting(); +void aurora_set_rim_lighting(bool enabled); +bool aurora_get_rim_lighting(); +void aurora_set_specular_intensity(float intensity); +float aurora_get_specular_intensity(); +void aurora_set_rim_intensity(float intensity); +float aurora_get_rim_intensity(); +void aurora_set_ambient_multiplier(float multiplier); +float aurora_get_ambient_multiplier(); +void aurora_set_diffuse_multiplier(float multiplier); +float aurora_get_diffuse_multiplier(); + #ifdef __cplusplus } #endif diff --git a/lib/gfx/common.cpp b/lib/gfx/common.cpp index 3ee901c..1ccc73f 100644 --- a/lib/gfx/common.cpp +++ b/lib/gfx/common.cpp @@ -1058,3 +1058,19 @@ void pop_debug_group() { } const AuroraStats* aurora_get_stats() { return &aurora::gfx::g_stats; } + +void aurora_set_enhanced_lighting(const bool enabled) { aurora::gx::g_enhancedLighting = enabled; } +bool aurora_get_enhanced_lighting() { return aurora::gx::g_enhancedLighting; } + +void aurora_set_specular_lighting(const bool enabled) { aurora::gx::g_enableSpecular = enabled; } +bool aurora_get_specular_lighting() { return aurora::gx::g_enableSpecular; } +void aurora_set_rim_lighting(const bool enabled) { aurora::gx::g_enableRim = enabled; } +bool aurora_get_rim_lighting() { return aurora::gx::g_enableRim; } +void aurora_set_specular_intensity(const float intensity) { aurora::gx::g_specularIntensity = intensity; } +float aurora_get_specular_intensity() { return aurora::gx::g_specularIntensity; } +void aurora_set_rim_intensity(const float intensity) { aurora::gx::g_rimIntensity = intensity; } +float aurora_get_rim_intensity() { return aurora::gx::g_rimIntensity; } +void aurora_set_ambient_multiplier(const float multiplier) { aurora::gx::g_ambientMultiplier = multiplier; } +float aurora_get_ambient_multiplier() { return aurora::gx::g_ambientMultiplier; } +void aurora_set_diffuse_multiplier(const float multiplier) { aurora::gx::g_diffuseMultiplier = multiplier; } +float aurora_get_diffuse_multiplier() { return aurora::gx::g_diffuseMultiplier; } diff --git a/lib/gx/gx.cpp b/lib/gx/gx.cpp index ad6fd08..7b2be00 100644 --- a/lib/gx/gx.cpp +++ b/lib/gx/gx.cpp @@ -724,6 +724,14 @@ u8 comp_cnt_count(GXAttr attr, GXCompCnt cnt) noexcept { Log.fatal("comp_cnt_count: Unsupported attr/cnt {} {}", attr, cnt); } +bool g_enhancedLighting = true; +bool g_enableSpecular = true; +bool g_enableRim = true; +float g_specularIntensity = 0.2f; +float g_rimIntensity = 0.08f; +float g_ambientMultiplier = 1.0f; +float g_diffuseMultiplier = 1.0f; + void populate_pipeline_config(PipelineConfig& config, GXPrimitive primitive, GXVtxFmt fmt) noexcept { ZoneScoped; @@ -787,6 +795,9 @@ void populate_pipeline_config(PipelineConfig& config, GXPrimitive primitive, GXV config.shaderConfig.indStages[i] = g_gxState.indStages[i]; } config.shaderConfig.numIndStages = g_gxState.numIndStages; + config.shaderConfig.enhancedLighting = g_enhancedLighting ? 1 : 0; + config.shaderConfig.enableSpecular = g_enableSpecular ? 1 : 0; + config.shaderConfig.enableRim = g_enableRim ? 1 : 0; for (u8 i = 0; i < MaxColorChannels; ++i) { const auto& cc = g_gxState.colorChannelConfig[i]; if (cc.lightingEnabled) { diff --git a/lib/gx/gx.hpp b/lib/gx/gx.hpp index 3b27077..1bdcee5 100644 --- a/lib/gx/gx.hpp +++ b/lib/gx/gx.hpp @@ -47,7 +47,7 @@ constexpr float GX_LARGE_NUMBER = -1048576.0f; namespace aurora::gx { constexpr bool EnableNormalVisualization = false; constexpr bool EnableDebugPrints = false; -constexpr bool UsePerPixelLighting = false; +constexpr bool UsePerPixelLighting = true; constexpr bool UseReversedZ = true; constexpr u32 MaxTextures = GX_MAX_TEXMAP; @@ -383,6 +383,13 @@ struct GXState { void clearVtxSizeCache() { lastVtxFmt = GX_MAX_VTXFMT; } }; extern GXState g_gxState; +extern bool g_enhancedLighting; +extern bool g_enableSpecular; +extern bool g_enableRim; +extern float g_specularIntensity; +extern float g_rimIntensity; +extern float g_ambientMultiplier; +extern float g_diffuseMultiplier; struct ShaderInfo; void initialize() noexcept; @@ -439,7 +446,10 @@ struct ShaderConfig { u8 fogType = GX_FOG_NONE; u8 vtxStride = 0; u8 lineMode : 2 = 0; // 1 = GX_LINES, 2 = GX_LINESTRIP, 3 = GX_POINTS - u8 pad1 : 6 = 0; + u8 enhancedLighting : 1 = 1; + u8 enableSpecular : 1 = 1; + u8 enableRim : 1 = 1; + u8 pad1 : 3 = 0; u8 pad2 = 0; std::array attrs; std::array tevSwapTable; @@ -476,6 +486,7 @@ struct ShaderInfo { u32 uniformSize = 0; bool usesFog : 1 = false; bool lightingEnabled : 1 = false; + bool enhancedLighting : 1 = false; u8 lineMode : 2 = 0; }; struct BindGroupRanges { diff --git a/lib/gx/shader.cpp b/lib/gx/shader.cpp index ed6764e..fc0f8ea 100644 --- a/lib/gx/shader.cpp +++ b/lib/gx/shader.cpp @@ -592,7 +592,7 @@ auto lighting_func(const ShaderConfig& config, const ColorChannelConfig& cc, u8 std::string_view swizzle = alpha ? ".a"sv : ""sv; std::string outVar; std::string_view posVar; - if (UsePerPixelLighting) { + if (config.enhancedLighting) { outVar = fmt::format("rast{}", i); posVar = "in.mv_pos"sv; } else { @@ -601,7 +601,7 @@ auto lighting_func(const ShaderConfig& config, const ColorChannelConfig& cc, u8 } std::string ambSrc, matSrc; if (cc.ambSrc == GX_SRC_VTX) { - if (UsePerPixelLighting) { + if (config.enhancedLighting) { ambSrc = fmt::format("in.clr{}", i); } else { ambSrc = vtx_attr(config, static_cast(GX_VA_CLR0 + i)); @@ -610,7 +610,7 @@ auto lighting_func(const ShaderConfig& config, const ColorChannelConfig& cc, u8 ambSrc = fmt::format("ubuf.cc{0}{1}_amb", i, alpha ? "a"sv : ""sv); } if (cc.matSrc == GX_SRC_VTX) { - if (UsePerPixelLighting) { + if (config.enhancedLighting) { matSrc = fmt::format("in.clr{}", i); } else { matSrc = vtx_attr(config, static_cast(GX_VA_CLR0 + i)); @@ -632,7 +632,7 @@ auto lighting_func(const ShaderConfig& config, const ColorChannelConfig& cc, u8 var dist_attn = dot(light.dist_att, vec3f(1.0, dist, dist2)); attn = max(0.0, cos_attn / dist_attn);)"""); } else if (cc.attnFn == GX_AF_SPEC) { - std::string_view normal = UsePerPixelLighting ? "in.mv_nrm"sv : "mv_nrm"sv; + std::string_view normal = config.enhancedLighting ? "in.mv_nrm"sv : "mv_nrm"sv; std::string dist_attn = diffFn != GX_DF_NONE ? "max(0.0, dot(normalize(light.dist_att), vec3f(1.0, attn, attn * attn)));" : "max(0.0, dot(light.dist_att, vec3f(1.0, attn, attn * attn)));"; @@ -647,21 +647,49 @@ auto lighting_func(const ShaderConfig& config, const ColorChannelConfig& cc, u8 if (diffFn == GX_DF_NONE) { lightDiffFn = "1.0"sv; } else if (diffFn == GX_DF_SIGN) { - if (UsePerPixelLighting) { + if (config.enhancedLighting) { lightDiffFn = "dot(ldir, in.mv_nrm)"sv; } else { lightDiffFn = "dot(ldir, mv_nrm)"sv; } } else if (diffFn == GX_DF_CLAMP) { - if (UsePerPixelLighting) { + if (config.enhancedLighting) { lightDiffFn = "max(0.0, dot(ldir, in.mv_nrm))"sv; } else { lightDiffFn = "max(0.0, dot(ldir, mv_nrm))"sv; } } + // Blinn-Phong specular + rim lighting for per-pixel lighting + // Skips GX_AF_SPEC (which already computes specular in a worse way) + std::string ambientScale; + std::string diffuseScale; + std::string viewDirCode; + std::string specularCode; + std::string postLoopCode; + if (config.enhancedLighting) { + ambientScale = " * ubuf.ambient_multiplier"; + diffuseScale = " * ubuf.diffuse_multiplier"; + if (!alpha && diffFn != GX_DF_NONE && cc.attnFn != GX_AF_SPEC && + (config.enableSpecular || config.enableRim)) { + viewDirCode = R"""( + let view_dir = normalize(-in.mv_pos);)"""; + if (config.enableSpecular) { + specularCode = R"""( + if (diff > 0.0) { + let h = normalize(ldir + view_dir); + spec_contrib = pow(max(0.0, dot(in.mv_nrm, h)), 32.0) * ubuf.specular_intensity; + })"""; + } + if (config.enableRim) { + postLoopCode = R"""( + let rim = pow(1.0 - max(0.0, dot(view_dir, in.mv_nrm)), 3.0) * ubuf.rim_intensity; + lighting = lighting + vec4f(rim, rim, rim, 0.0);)"""; + } + } + } return fmt::format(R"""( {{ - var lighting = {5}; + var lighting = {5}{13};{11} for (var i = 0u; i < {1}u; i++) {{ if ((ubuf.lightState{0}{9} & (1u << i)) == 0u) {{ continue; }} var light = ubuf.lights[i]; @@ -671,12 +699,13 @@ auto lighting_func(const ShaderConfig& config, const ColorChannelConfig& cc, u8 ldir = ldir / dist; var attn: f32;{2} var diff = {3}; - lighting = lighting + (attn * diff * light.color); - }} + var spec_contrib: f32 = 0.0;{10} + lighting = lighting + (attn * (diff{14} + spec_contrib) * light.color); + }}{12} {7}{8} = ({4} * clamp(lighting, vec4f(0.0), vec4f(1.0))){8}; }})""", i, GX::MaxLights, lightAttnFn, lightDiffFn, matSrc, ambSrc, posVar, outVar, swizzle, - alpha ? "a"sv : ""sv); + alpha ? "a"sv : ""sv, specularCode, viewDirCode, postLoopCode, ambientScale, diffuseScale); } wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept { @@ -927,6 +956,12 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept { lightState0a: u32, lightState1a: u32,)"""), GX::MaxLights); + if (config.enhancedLighting) { + uniBufAttrs += "\n specular_intensity: f32,"; + uniBufAttrs += "\n rim_intensity: f32,"; + uniBufAttrs += "\n ambient_multiplier: f32,"; + uniBufAttrs += "\n diffuse_multiplier: f32,"; + } uniformPre += "\n" "struct Light {\n" @@ -936,7 +971,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept { " cos_att: vec3f,\n" " dist_att: vec3f,\n" "};"; - if (UsePerPixelLighting) { + if (config.enhancedLighting) { vtxOutAttrs += fmt::format("\n @location({}) mv_pos: vec3f,", vtxOutIdx++); vtxOutAttrs += fmt::format("\n @location({}) mv_nrm: vec3f,", vtxOutIdx++); vtxXfrAttrs += fmt::format(FMT_STRING(R"""( @@ -966,7 +1001,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept { } // Output vertex color if necessary - if (UsePerPixelLighting) { + if (config.enhancedLighting) { if ((cc.lightingEnabled && cc.ambSrc == GX_SRC_VTX) || cc.matSrc == GX_SRC_VTX || (cca.lightingEnabled && cca.ambSrc == GX_SRC_VTX) || cca.matSrc == GX_SRC_VTX) { vtxOutAttrs += fmt::format("\n @location({}) clr{}: vec4f,", vtxOutIdx++, i); @@ -974,7 +1009,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept { } } - if (UsePerPixelLighting) { + if (config.enhancedLighting) { fragmentFnPre += fmt::format("\n var rast{}: vec4f;", i); fragmentFnPre += lighting_func(config, cc, i, false); fragmentFnPre += lighting_func(config, cca, i, true); diff --git a/lib/gx/shader_info.cpp b/lib/gx/shader_info.cpp index 31ba6ed..790327f 100644 --- a/lib/gx/shader_info.cpp +++ b/lib/gx/shader_info.cpp @@ -265,8 +265,13 @@ ShaderInfo build_shader_info(const ShaderConfig& config) noexcept { } } if (info.lightingEnabled) { + info.enhancedLighting = config.enhancedLighting; // Lights + light state for all channels info.uniformSize += 16 + sizeof(Light) * GX::MaxLights; + if (info.enhancedLighting) { + // specular_intensity, rim_intensity, ambient_multiplier, diffuse_multiplier + info.uniformSize += 16; + } } for (int i = 0; i < info.sampledColorChannels.size(); ++i) { if (info.sampledColorChannels.test(i)) { @@ -406,6 +411,12 @@ gfx::Range build_uniform(const ShaderInfo& info, u32 vtxStart, const BindGroupRa for (int i = 0; i < 4; ++i) { buf.append(g_gxState.colorChannelState[i].lightMask.to_ulong()); } + if (info.enhancedLighting) { + buf.append(g_specularIntensity); + buf.append(g_rimIntensity); + buf.append(g_ambientMultiplier); + buf.append(g_diffuseMultiplier); + } } for (int i = 0; i < info.sampledColorChannels.size(); ++i) { if (!info.sampledColorChannels.test(i)) {