You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame中模糊投影为何在半透明Surface上变亮而非变暗?

问题:Pygame合并半透明Surface时阴影变亮的解决方法

我对Alpha混合的基础原理可能存在误解。在Pygame中制作模糊投影的步骤如下:

  • 绘制白色物体
  • 在另一个Surface上绘制其黑色版本
  • 对黑色版本应用高斯模糊
  • 将模糊后的黑色版本与白色版本依次blit到新Surface上

到这一步都正常,但当把这个Surface叠加到填充了半透明颜色的Surface上,再blit到背景时,阴影的alpha似乎影响了半透明Surface,导致阴影变亮而非黑色。实际项目中必须返回合并后的单一Surface,无法独立blit两个Surface到背景。

错误效果:白色圆形带浅灰色投影
正常效果(半透明Surface完全透明时):白色圆形带黑色投影

尝试调整blit的special_flags改变混合模式,但所有组合都无效。

以下是可复现代码:

import pygame as pg


pg.init()
screen = pg.display.set_mode((400, 400))
clock = pg.time.Clock()

black_circle_surface = pg.Surface((100, 100), pg.SRCALPHA)
pg.draw.circle(black_circle_surface, (0, 0, 0), (50, 50), 25)

white_circle_surface = pg.Surface((100, 100), pg.SRCALPHA)
pg.draw.circle(white_circle_surface, (255, 255, 255), (50, 50), 25)

circle_and_shadow_surface = pg.Surface((100, 100), pg.SRCALPHA)
circle_and_shadow_surface.blit(
    pg.transform.gaussian_blur(black_circle_surface, radius=10), (0, 0)
)
circle_and_shadow_surface.blit(white_circle_surface, (0, 0))

semi_transparent_surface = pg.Surface((200, 200), pg.SRCALPHA)
semi_transparent_surface.fill(
    (255, 255, 255, 1)  # 把1改成0问题就消失
)
semi_transparent_surface.blit(
    circle_and_shadow_surface,
    (50, 50),
)

running = True
while running:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    screen.fill((20, 70, 80))

    screen.blit(semi_transparent_surface, (100, 100))

    pg.display.update()
    clock.tick(60)
解决方案

问题出在Alpha混合的叠加逻辑上:模糊后的阴影自身带alpha通道(高斯模糊会让边缘alpha值降低),当它被blit到半透明的Surface上时,会和Surface的底色(白色半透明)进行混合,导致黑色阴影被冲淡成灰色。

要解决这个问题,需要在合并阴影、物体和半透明Surface时,调整混合模式,让阴影的黑色能够正确覆盖半透明底色,同时保留原本的透明度效果。具体有两种方法:

方法一:使用BLEND_RGBA_MULT混合模式绘制阴影

在将模糊后的阴影blit到circle_and_shadow_surface时,使用special_flags=pg.BLEND_RGBA_MULT,这样阴影的黑色会和目标Surface的底色(透明)相乘,保留纯黑的颜色信息,避免后续叠加半透明Surface时被冲淡。

修改这部分代码:

circle_and_shadow_surface = pg.Surface((100, 100), pg.SRCALPHA)
# 用乘法混合模式绘制阴影
circle_and_shadow_surface.blit(
    pg.transform.gaussian_blur(black_circle_surface, radius=10), 
    (0, 0),
    special_flags=pg.BLEND_RGBA_MULT
)
circle_and_shadow_surface.blit(white_circle_surface, (0, 0))

方法二:先绘制半透明底色,再叠加阴影和物体

换个顺序操作:先在最终Surface上填充半透明颜色,再依次blit阴影和白色物体,直接得到合并后的结果。这样阴影是直接绘制在半透明底色上,混合逻辑更可控。

修改代码如下:

# 直接创建带半透明底色的最终Surface
final_surface = pg.Surface((200, 200), pg.SRCALPHA)
final_surface.fill((255, 255, 255, 1))

# 绘制阴影到最终Surface的对应位置
final_surface.blit(
    pg.transform.gaussian_blur(black_circle_surface, radius=10), 
    (50, 50),
    special_flags=pg.BLEND_RGBA_MULT
)
# 绘制白色物体
final_surface.blit(white_circle_surface, (50, 50))

# 后续直接使用final_surface即可,替换原来的semi_transparent_surface

原理说明

  • BLEND_RGBA_MULT模式会将源像素和目标像素的RGBA值分别相乘,对于黑色(0,0,0,alpha)来说,相乘后会保留黑色,同时保留阴影的透明度渐变,避免和半透明底色混合时被提亮。
  • 调整绘制顺序可以让阴影直接和半透明底色进行混合,而非先合并阴影和物体,再与半透明底色二次混合,减少了混合层级带来的颜色偏差。

内容的提问来源于stack exchange,提问作者Jack Gaino

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 01:15:55