mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Merge pull request #3516 from wwylele/shadow-sw
SwRasterizer: Implement shadow mapping
This commit is contained in:
		
						commit
						384849232b
					
				
					 7 changed files with 172 additions and 10 deletions
				
			
		|  | @ -15,6 +15,12 @@ | |||
| namespace Pica { | ||||
| 
 | ||||
| struct FramebufferRegs { | ||||
|     enum class FragmentOperationMode : u32 { | ||||
|         Default = 0, | ||||
|         Gas = 1, | ||||
|         Shadow = 3, | ||||
|     }; | ||||
| 
 | ||||
|     enum class LogicOp : u32 { | ||||
|         Clear = 0, | ||||
|         And = 1, | ||||
|  | @ -84,6 +90,7 @@ struct FramebufferRegs { | |||
| 
 | ||||
|     struct { | ||||
|         union { | ||||
|             BitField<0, 2, FragmentOperationMode> fragment_operation_mode; | ||||
|             // If false, logic blending is used
 | ||||
|             BitField<8, 1, u32> alphablend_enable; | ||||
|         }; | ||||
|  | @ -274,7 +281,14 @@ struct FramebufferRegs { | |||
|         ASSERT_MSG(false, "Unknown depth format %u", static_cast<u32>(format)); | ||||
|     } | ||||
| 
 | ||||
|     INSERT_PADDING_WORDS(0x20); | ||||
|     INSERT_PADDING_WORDS(0x10); // Gas related registers
 | ||||
| 
 | ||||
|     union { | ||||
|         BitField<0, 16, u32> constant; // float1.5.10
 | ||||
|         BitField<16, 16, u32> linear;  // float1.5.10
 | ||||
|     } shadow; | ||||
| 
 | ||||
|     INSERT_PADDING_WORDS(0xF); | ||||
| }; | ||||
| 
 | ||||
| static_assert(sizeof(FramebufferRegs) == 0x40 * sizeof(u32), | ||||
|  |  | |||
|  | @ -187,9 +187,15 @@ struct LightingRegs { | |||
|     BitField<0, 3, u32> max_light_index; // Number of enabled lights - 1
 | ||||
| 
 | ||||
|     union { | ||||
|         BitField<0, 1, u32> enable_shadow; | ||||
|         BitField<2, 2, LightingFresnelSelector> fresnel_selector; | ||||
|         BitField<4, 4, LightingConfig> config; | ||||
|         BitField<16, 1, u32> shadow_primary; | ||||
|         BitField<17, 1, u32> shadow_secondary; | ||||
|         BitField<18, 1, u32> shadow_invert; | ||||
|         BitField<19, 1, u32> shadow_alpha; | ||||
|         BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2
 | ||||
|         BitField<24, 2, u32> shadow_selector; | ||||
|         BitField<27, 1, u32> clamp_highlights; | ||||
|         BitField<28, 2, LightingBumpMode> bump_mode; | ||||
|         BitField<30, 1, u32> disable_bump_renorm; | ||||
|  | @ -198,6 +204,9 @@ struct LightingRegs { | |||
|     union { | ||||
|         u32 raw; | ||||
| 
 | ||||
|         // Each bit specifies whether shadow should be applied for the corresponding light.
 | ||||
|         BitField<0, 8, u32> disable_shadow; | ||||
| 
 | ||||
|         // Each bit specifies whether spot light attenuation should be applied for the corresponding
 | ||||
|         // light.
 | ||||
|         BitField<8, 8, u32> disable_spot_atten; | ||||
|  | @ -224,6 +233,10 @@ struct LightingRegs { | |||
|         return (config1.disable_spot_atten & (1 << index)) != 0; | ||||
|     } | ||||
| 
 | ||||
|     bool IsShadowDisabled(unsigned index) const { | ||||
|         return (config1.disable_shadow & (1 << index)) != 0; | ||||
|     } | ||||
| 
 | ||||
|     union { | ||||
|         BitField<0, 8, u32> index; ///< Index at which to set data in the LUT
 | ||||
|         BitField<8, 5, u32> type;  ///< Type of LUT for which to set data
 | ||||
|  |  | |||
|  | @ -158,7 +158,12 @@ struct TexturingRegs { | |||
|         return address * 8; | ||||
|     } | ||||
| 
 | ||||
|     INSERT_PADDING_WORDS(0x3); | ||||
|     union { | ||||
|         BitField<0, 1, u32> orthographic; // 0: enable perspective divide
 | ||||
|         BitField<1, 23, u32> bias;        // 23-bit fraction
 | ||||
|     } shadow; | ||||
| 
 | ||||
|     INSERT_PADDING_WORDS(0x2); | ||||
|     BitField<0, 4, TextureFormat> texture0_format; | ||||
|     BitField<0, 1, u32> fragment_lighting_enable; | ||||
|     INSERT_PADDING_WORDS(0x1); | ||||
|  |  | |||
|  | @ -359,5 +359,54 @@ u8 LogicOp(u8 src, u8 dest, FramebufferRegs::LogicOp op) { | |||
|     UNREACHABLE(); | ||||
| }; | ||||
| 
 | ||||
| // Decode/Encode for shadow map format. It is similar to D24S8 format, but the depth field is in
 | ||||
| // big-endian
 | ||||
| static const Math::Vec2<u32> DecodeD24S8Shadow(const u8* bytes) { | ||||
|     return {static_cast<u32>((bytes[0] << 16) | (bytes[1] << 8) | bytes[2]), bytes[3]}; | ||||
| } | ||||
| 
 | ||||
| static void EncodeD24X8Shadow(u32 depth, u8* bytes) { | ||||
|     bytes[2] = depth & 0xFF; | ||||
|     bytes[1] = (depth >> 8) & 0xFF; | ||||
|     bytes[0] = (depth >> 16) & 0xFF; | ||||
| } | ||||
| 
 | ||||
| static void EncodeX24S8Shadow(u8 stencil, u8* bytes) { | ||||
|     bytes[3] = stencil; | ||||
| } | ||||
| 
 | ||||
| void DrawShadowMapPixel(int x, int y, u32 depth, u8 stencil) { | ||||
|     const auto& framebuffer = g_state.regs.framebuffer.framebuffer; | ||||
|     const auto& shadow = g_state.regs.framebuffer.shadow; | ||||
|     const PAddr addr = framebuffer.GetColorBufferPhysicalAddress(); | ||||
| 
 | ||||
|     y = framebuffer.height - y; | ||||
| 
 | ||||
|     const u32 coarse_y = y & ~7; | ||||
|     u32 bytes_per_pixel = 4; | ||||
|     u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + | ||||
|                      coarse_y * framebuffer.width * bytes_per_pixel; | ||||
|     u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset; | ||||
| 
 | ||||
|     auto ref = DecodeD24S8Shadow(dst_pixel); | ||||
|     u32 ref_z = ref.x; | ||||
|     u32 ref_s = ref.y; | ||||
| 
 | ||||
|     if (depth < ref_z) { | ||||
|         if (stencil == 0) { | ||||
|             EncodeD24X8Shadow(depth, dst_pixel); | ||||
|         } else { | ||||
|             float16 constant = float16::FromRaw(shadow.constant); | ||||
|             float16 linear = float16::FromRaw(shadow.linear); | ||||
|             float16 x = float16::FromFloat32(static_cast<float>(depth) / ref_z); | ||||
|             float16 stencil_new = float16::FromFloat32(stencil) / (constant + linear * x); | ||||
|             stencil = static_cast<u8>(MathUtil::Clamp(stencil_new.ToFloat32(), 0.0f, 255.0f)); | ||||
| 
 | ||||
|             if (stencil < ref_s) | ||||
|                 EncodeX24S8Shadow(stencil, dst_pixel); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace Rasterizer
 | ||||
| } // namespace Pica
 | ||||
|  |  | |||
|  | @ -25,5 +25,7 @@ Math::Vec4<u8> EvaluateBlendEquation(const Math::Vec4<u8>& src, const Math::Vec4 | |||
| 
 | ||||
| u8 LogicOp(u8 src, u8 dest, FramebufferRegs::LogicOp op); | ||||
| 
 | ||||
| void DrawShadowMapPixel(int x, int y, u32 depth, u8 stencil); | ||||
| 
 | ||||
| } // namespace Rasterizer
 | ||||
| } // namespace Pica
 | ||||
|  |  | |||
|  | @ -25,6 +25,16 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors( | |||
|     const Math::Quaternion<float>& normquat, const Math::Vec3<float>& view, | ||||
|     const Math::Vec4<u8> (&texture_color)[4]) { | ||||
| 
 | ||||
|     Math::Vec4<float> shadow; | ||||
|     if (lighting.config0.enable_shadow) { | ||||
|         shadow = texture_color[lighting.config0.shadow_selector].Cast<float>() / 255.0f; | ||||
|         if (lighting.config0.shadow_invert) { | ||||
|             shadow = Math::MakeVec(1.0f, 1.0f, 1.0f, 1.0f) - shadow; | ||||
|         } | ||||
|     } else { | ||||
|         shadow = Math::MakeVec(1.0f, 1.0f, 1.0f, 1.0f); | ||||
|     } | ||||
| 
 | ||||
|     Math::Vec3<float> surface_normal; | ||||
|     Math::Vec3<float> surface_tangent; | ||||
| 
 | ||||
|  | @ -278,11 +288,38 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors( | |||
|         } | ||||
| 
 | ||||
|         auto diffuse = | ||||
|             light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f(); | ||||
|         diffuse_sum += Math::MakeVec(diffuse * dist_atten * spot_atten, 0.0f); | ||||
|             (light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f()) * | ||||
|             dist_atten * spot_atten; | ||||
|         auto specular = (specular_0 + specular_1) * clamp_highlights * dist_atten * spot_atten; | ||||
| 
 | ||||
|         specular_sum += Math::MakeVec( | ||||
|             (specular_0 + specular_1) * clamp_highlights * dist_atten * spot_atten, 0.0f); | ||||
|         if (!lighting.IsShadowDisabled(num)) { | ||||
|             if (lighting.config0.shadow_primary) { | ||||
|                 diffuse = diffuse * shadow.xyz(); | ||||
|             } | ||||
|             if (lighting.config0.shadow_secondary) { | ||||
|                 specular = specular * shadow.xyz(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         diffuse_sum += Math::MakeVec(diffuse, 0.0f); | ||||
|         specular_sum += Math::MakeVec(specular, 0.0f); | ||||
|     } | ||||
| 
 | ||||
|     if (lighting.config0.shadow_alpha) { | ||||
|         // Alpha shadow also uses the Fresnel selecotr to determine which alpha to apply
 | ||||
|         // Enabled for diffuse lighting alpha component
 | ||||
|         if (lighting.config0.fresnel_selector == | ||||
|                 LightingRegs::LightingFresnelSelector::PrimaryAlpha || | ||||
|             lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) { | ||||
|             diffuse_sum.a() *= shadow.w; | ||||
|         } | ||||
| 
 | ||||
|         // Enabled for the specular lighting alpha component
 | ||||
|         if (lighting.config0.fresnel_selector == | ||||
|                 LightingRegs::LightingFresnelSelector::SecondaryAlpha || | ||||
|             lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) { | ||||
|             specular_sum.a() *= shadow.w; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f); | ||||
|  |  | |||
|  | @ -74,8 +74,9 @@ static int SignedArea(const Math::Vec2<Fix12P4>& vtx1, const Math::Vec2<Fix12P4> | |||
| }; | ||||
| 
 | ||||
| /// Convert a 3D vector for cube map coordinates to 2D texture coordinates along with the face name
 | ||||
| static std::tuple<float24, float24, PAddr> ConvertCubeCoord(float24 u, float24 v, float24 w, | ||||
|                                                             const TexturingRegs& regs) { | ||||
| static std::tuple<float24, float24, float24, PAddr> ConvertCubeCoord(float24 u, float24 v, | ||||
|                                                                      float24 w, | ||||
|                                                                      const TexturingRegs& regs) { | ||||
|     const float abs_u = std::abs(u.ToFloat32()); | ||||
|     const float abs_v = std::abs(v.ToFloat32()); | ||||
|     const float abs_w = std::abs(w.ToFloat32()); | ||||
|  | @ -112,8 +113,9 @@ static std::tuple<float24, float24, PAddr> ConvertCubeCoord(float24 u, float24 v | |||
|         x = u; | ||||
|         z = w; | ||||
|     } | ||||
|     float24 z_abs = float24::FromFloat32(std::abs(z.ToFloat32())); | ||||
|     const float24 half = float24::FromFloat32(0.5f); | ||||
|     return std::make_tuple(x / z * half + half, y / z * half + half, addr); | ||||
|     return std::make_tuple(x / z * half + half, y / z * half + half, z_abs, addr); | ||||
| } | ||||
| 
 | ||||
| MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240)); | ||||
|  | @ -331,13 +333,16 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | |||
|                 // Only unit 0 respects the texturing type (according to 3DBrew)
 | ||||
|                 // TODO: Refactor so cubemaps and shadowmaps can be handled
 | ||||
|                 PAddr texture_address = texture.config.GetPhysicalAddress(); | ||||
|                 float24 shadow_z; | ||||
|                 if (i == 0) { | ||||
|                     switch (texture.config.type) { | ||||
|                     case TexturingRegs::TextureConfig::Texture2D: | ||||
|                         break; | ||||
|                     case TexturingRegs::TextureConfig::ShadowCube: | ||||
|                     case TexturingRegs::TextureConfig::TextureCube: { | ||||
|                         auto w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w); | ||||
|                         std::tie(u, v, texture_address) = ConvertCubeCoord(u, v, w, regs.texturing); | ||||
|                         std::tie(u, v, shadow_z, texture_address) = | ||||
|                             ConvertCubeCoord(u, v, w, regs.texturing); | ||||
|                         break; | ||||
|                     } | ||||
|                     case TexturingRegs::TextureConfig::Projection2D: { | ||||
|  | @ -346,6 +351,16 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | |||
|                         v /= tc0_w; | ||||
|                         break; | ||||
|                     } | ||||
|                     case TexturingRegs::TextureConfig::Shadow2D: { | ||||
|                         auto tc0_w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w); | ||||
|                         if (!regs.texturing.shadow.orthographic) { | ||||
|                             u /= tc0_w; | ||||
|                             v /= tc0_w; | ||||
|                         } | ||||
| 
 | ||||
|                         shadow_z = float24::FromFloat32(std::abs(tc0_w.ToFloat32())); | ||||
|                         break; | ||||
|                     } | ||||
|                     default: | ||||
|                         // TODO: Change to LOG_ERROR when more types are handled.
 | ||||
|                         LOG_DEBUG(HW_GPU, "Unhandled texture type %x", (int)texture.config.type); | ||||
|  | @ -394,6 +409,22 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | |||
|                     // TODO: Apply the min and mag filters to the texture
 | ||||
|                     texture_color[i] = Texture::LookupTexture(texture_data, s, t, info); | ||||
|                 } | ||||
| 
 | ||||
|                 if (i == 0 && (texture.config.type == TexturingRegs::TextureConfig::Shadow2D || | ||||
|                                texture.config.type == TexturingRegs::TextureConfig::ShadowCube)) { | ||||
| 
 | ||||
|                     s32 z_int = static_cast<s32>(std::min(shadow_z.ToFloat32(), 1.0f) * 0xFFFFFF); | ||||
|                     z_int -= regs.texturing.shadow.bias << 1; | ||||
|                     auto& color = texture_color[i]; | ||||
|                     s32 z_ref = (color.w << 16) | (color.z << 8) | color.y; | ||||
|                     u8 density; | ||||
|                     if (z_ref >= z_int) { | ||||
|                         density = color.x; | ||||
|                     } else { | ||||
|                         density = 0; | ||||
|                     } | ||||
|                     texture_color[i] = {density, density, density, density}; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // sample procedural texture
 | ||||
|  | @ -541,6 +572,17 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | |||
|             } | ||||
| 
 | ||||
|             const auto& output_merger = regs.framebuffer.output_merger; | ||||
| 
 | ||||
|             if (output_merger.fragment_operation_mode == | ||||
|                 FramebufferRegs::FragmentOperationMode::Shadow) { | ||||
|                 u32 depth_int = static_cast<u32>(depth * 0xFFFFFF); | ||||
|                 // use green color as the shadow intensity
 | ||||
|                 u8 stencil = combiner_output.y; | ||||
|                 DrawShadowMapPixel(x >> 4, y >> 4, depth_int, stencil); | ||||
|                 // skip the normal output merger pipeline if it is in shadow mode
 | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             // TODO: Does alpha testing happen before or after stencil?
 | ||||
|             if (output_merger.alpha_test.enable) { | ||||
|                 bool pass = false; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue