Перенос поверхности на другую без объединения альфы

В проекте pygame, над которым я работаю, спрайты персонажей и объектов отбрасывают тень на местность. И тень, и ландшафт являются обычными поверхностями pygame, поэтому, чтобы показать их, тень наносится на ландшафт. Когда другой тени нет (только одна тень и местность) все работает нормально, но когда персонаж входит в область тени, отбрасывая собственную тень, обе тени объединяют свои альфа-значения, еще больше скрывая местность. Я хочу избежать такого поведения, сохраняя стабильное значение альфа. Есть ли способ сделать это?

РЕДАКТИРОВАТЬ: это изображение, которое я сделал в Photoshop, чтобы показать проблему введите здесь описание изображения

EDIT2: ответ @sloth в порядке, но я забыл прокомментировать, что мой проект сложнее этого. Тени не целые квадраты, а больше похожи на «трафареты». Как и настоящие тени, они представляют собой силуэты объектов, от которых они отбрасываются, и поэтому им нужны альфа-каналы для каждого пикселя, которые несовместимы с цветовым ключом и целыми значениями альфа-канала.

Вот видео YouTube, в котором проблема показана немного яснее.


person Zeniel Danaku    schedule 20.10.2018    source источник


Ответы (3)


Простой способ решить эту проблему — сначала наложить тени на другой Surface, у которого есть значение альфа, но не для каждого пикселя. Затем перенесите это Surface на свой экран.

Вот простой пример, показывающий результат:

from pygame import *
import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))

# we create two "shadow" surfaces, a.k.a. black with alpha channel set to something
# we use these to illustrate the problem
shadow = pygame.Surface((128, 128), pygame.SRCALPHA)
shadow.fill((0, 0, 0, 100))
shadow2 = shadow.copy()

# a helper surface we use later for the fixed shadows
shadow_surf = pygame.Surface((800, 600))
# we set a colorkey to easily make this surface transparent
colorkey_color = (2,3,4)
shadow_surf.set_colorkey(colorkey_color)
# the alpha value of our shadow
shadow_surf.set_alpha(100)

# just something to see the shadow effect
test_surface = pygame.Surface((800, 100))
test_surface.fill(pygame.Color('cyan'))

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

    screen.fill(pygame.Color('white'))

    screen.blit(test_surface, (0, 150))

    # first we blit the alpha channel shadows directly to the screen 
    screen.blit(shadow, (100, 100))
    screen.blit(shadow2, (164, 164))

    # here we draw the shadows to the helper surface first
    # since the helper surface has no per-pixel alpha, the shadows
    # will be fully black, but the alpha value for the full Surface image
    # is set to 100, so we still have transparent shadows
    shadow_surf.fill(colorkey_color)
    shadow_surf.blit(shadow, (100, 100))
    shadow_surf.blit(shadow2, (164, 164))

    screen.blit(shadow_surf, (400, 0))

    pygame.display.update()

введите здесь описание изображения

person sloth    schedule 22.10.2018

Вы можете создать функцию, которая проверяет столкновение теней и соответствующим образом корректирует значения смешивания теней.

person lakam99    schedule 21.10.2018

Вы можете комбинировать попиксельные альфа-тени, накладывая их на вспомогательную поверхность, а затем заполняя эту поверхность прозрачным белым цветом и передавая флаг pygame.BLEND_RGBA_MIN в качестве аргумента special_flags. Альфа-значение цвета заливки должно быть равно или ниже альфа-канала теней. Передача флага pygame.BLEND_RGBA_MIN означает, что для каждого пикселя будет взято меньшее значение каждого цветового канала, поэтому он уменьшит увеличенную альфу перекрывающихся теней до альфы цвета заливки.

import pygame as pg


pg.init()
screen = pg.display.set_mode((800, 600))
clock = pg.time.Clock()
shadow = pg.image.load('shadow.png').convert_alpha()
# Shadows will be blitted onto this surface.
shadow_surf = pg.Surface((800, 600), pg.SRCALPHA)

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

    screen.fill((130, 130, 130))
    screen.blit(shadow, (100, 100))
    screen.blit(shadow, (154, 154))

    shadow_surf.fill((0, 0, 0, 0))  # Clear the shadow_surf each frame.
    shadow_surf.blit(shadow, (100, 100))
    shadow_surf.blit(shadow, (154, 154))
    # Now adjust the alpha values of each pixel by filling the `shadow_surf` with a
    # transparent white and passing the pygame.BLEND_RGBA_MIN flag. This will take
    # the lower value of each channel, therefore the alpha should be lower than
    # the shadow alphas.
    shadow_surf.fill((255, 255, 255, 120), special_flags=pg.BLEND_RGBA_MIN)
    # Finally, blit the shadow_surf onto the screen.
    screen.blit(shadow_surf, (300, 0))

    pg.display.update()
    clock.tick(60)

введите здесь описание изображения

Вот тень.png.

введите здесь описание изображения

person skrx    schedule 24.10.2018