Pygame에서 조명(Lighting) 효과를 구현하는 가장 일반적이고 효율적인 방법은 **"어둠 레이어(Mask)"를 만들고 빛이 있는 부분만 밝게 처리한 뒤, 이를 게임 화면과 합성(Blending)"**하는 것입니다.
가장 널리 쓰이는 BLEND_RGBA_MULT (곱하기 블렌딩) 방식을 사용한 예제 코드를 작성해 드립니다. 이 방식은 어두운 곳은 가리고, 빛이 있는 곳은 원본 색상을 유지하는 "손전등" 같은 효과를 냅니다.
Pygame 조명 효과 예제 코드
이 코드는 실행 시 마우스를 따라다니는 부드러운 빛을 생성합니다.

import pygame
import sys
# 1. 초기화
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pygame Lighting Effect Example")
clock = pygame.time.Clock()
# 2. 색상 정의
DARK_COLOR = (20, 20, 20) # 주변의 어두운 정도 (완전 검정은 0,0,0)
LIGHT_COLOR = (255, 255, 255) # 빛의 색상 (흰색이어야 원본 색이 그대로 보임)
# 3. 조명 이미지 생성 함수 (부드러운 그라데이션)
def create_light_surface(radius):
# 알파 채널이 있는 서피스 생성
light_surf = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA)
# 픽셀 단위로 직접 그리기보다 원을 겹쳐서 그라데이션 효과를 냄 (성능상 이점)
# 중심은 투명도 255(완전 불투명), 바깥은 0(투명)
for r in range(radius, 0, -2):
alpha = int(255 * (1 - (r / radius))) # 안쪽일수록 더 밝게(흰색에 가깝게)
# 빛의 색상은 흰색 베이스에 알파값만 조절
color = (*LIGHT_COLOR, alpha)
# 부드러운 원 그리기
pygame.draw.circle(light_surf, (255, 255, 255, alpha), (radius, radius), r)
return light_surf
# 빛 이미지 미리 생성 (매 프레임 생성하면 느려짐)
light_radius = 150
light_img = create_light_surface(light_radius)
# 4. 배경 및 객체 생성 (조명 효과 확인용)
background = pygame.Surface((WIDTH, HEIGHT))
for x in range(0, WIDTH, 40):
for y in range(0, HEIGHT, 40):
color = (50 + (x % 100), 100 + (y % 100), 50)
pygame.draw.rect(background, color, (x, y, 38, 38))
# 5. 메인 루프
running = True
while running:
# 이벤트 처리
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 마우스 위치 가져오기 (빛의 중심)
mx, my = pygame.mouse.get_pos()
# --- 그리기 단계 ---
# A. 원본 게임 화면 그리기
screen.blit(background, (0, 0))
# (여기에 플레이어, 적 등 게임 오브젝트를 그립니다)
pygame.draw.rect(screen, (255, 0, 0), (WIDTH//2 - 25, HEIGHT//2 - 25, 50, 50)) # 중앙의 빨간 상자
# B. 어둠(Filter) 레이어 생성
# 화면 전체를 덮는 서피스 생성
filter_surf = pygame.Surface((WIDTH, HEIGHT))
filter_surf.fill(DARK_COLOR) # 전체를 어둡게 칠함
# C. 어둠 레이어에 빛 그리기
# 빛 이미지를 마우스 위치 중심으로 그림
# BLEND_RGBA_ADD를 사용하여 어두운 회색 위에 빛을 "더하기" 함 -> 그 부분만 하얗게 됨
filter_surf.blit(light_img, (mx - light_radius, my - light_radius), special_flags=pygame.BLEND_RGBA_ADD)
# D. 최종 합성 (Multiplication)
# 어둠 레이어(filter_surf)를 게임 화면(screen)에 곱하기 모드로 합성
# 원리:
# - 필터의 흰색 부분(빛) * 게임 화면 = 게임 화면 (보임)
# - 필터의 검은 부분(어둠) * 게임 화면 = 검은색 (안 보임)
screen.blit(filter_surf, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
# 화면 갱신
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
코드 상세 설명
이 효과는 '레이어 합성(Blending)' 기술을 사용합니다. 이해를 돕기 위해 시각적 설명을 추가하겠습니다.
1. 그라데이션 빛 만들기 (create_light_surface)
단색 원을 그리면 빛이 아니라 구멍 뚫린 종이처럼 보입니다. 부드러운 빛을 표현하기 위해 투명도(Alpha)가 다른 원을 여러 번 겹쳐 그려서 Radial Gradient(방사형 그라데이션) 효과를 만듭니다.
- 중심: 완전한 흰색 (가장 밝음)
- 가장자리: 투명 (어두움)
2. 어둠 레이어 (filter_surf)
화면 전체를 덮는 Surface를 만들고 DARK_COLOR로 채웁니다. 이 색상이 (0, 0, 0)이면 빛이 없는 곳은 완전한 암흑이 되고, (50, 50, 50) 정도면 희미하게 보이는 밤이 됩니다.
3. 합성 모드의 마법 (special_flags)
이 코드의 핵심은 blit 함수에 있는 special_flags입니다.
- 1단계 (BLEND_RGBA_ADD):
- 어두운 회색으로 칠해진 filter_surf 위에 하얀색 빛 이미지를 그립니다.
- ADD(더하기) 모드를 쓰면 어두운 배경색에 빛의 밝기가 더해져서, 빛이 있는 부분은 **하얀색(최대 밝기)**이 됩니다.
- 2단계 (BLEND_RGBA_MULT):
- 완성된 filter_surf(검은 배경 + 하얀 빛)를 실제 게임 화면인 screen 위에 덮어씌웁니다.
- MULT(곱하기) 모드의 수학적 원리:
- 게임 화면 색상 × 흰색(1.0) = 게임 화면 색상 그대로 (빛 받는 부분)
- 게임 화면 색상 × 검은색(0.0) = 검은색 (어두운 부분)
팁: 성능 최적화
위 코드는 이해를 돕기 위해 매 프레임 filter_surf를 새로 생성하고 fill을 합니다. 실제 게임에서는:
- filter_surf는 게임 시작 시 한 번만 생성(pygame.Surface)하고, 매 프레임 fill만 다시 하거나,
- 빛이 움직이지 않는 정적 조명이라면 빛이 그려진 filter_surf 자체를 캐싱해두는 것이 좋습니다.
이 코드를 복사해서 실행해보시고, DARK_COLOR 값을 변경해보며 어두운 정도를 조절해 보세요!
이 예제는 2D 게임에서 조명을 구현하는 가장 표준적이고 강력한 방법인 "Multiplicative Blending (곱하기 블렌딩)" 기법을 사용합니다.
동작 원리 핵심
- 게임 월드 그리기: 평소처럼 게임 화면을 다 그립니다.
- 조명 마스크(Darkness Mask) 생성:
- 화면 전체를 덮는 검은색(또는 아주 어두운 회색) 레이어를 만듭니다. 이것이 '주변광(Ambient Light)'이 됩니다.
- 이 어두운 레이어 위에 "가운데가 흰색이고 가장자리가 검은색인 그라데이션 원 이미지"를 더하기(ADD) 모드로 그립니다.
- 결과: 횃불이 있는 곳은 하얗고, 나머지는 어두운 마스크 이미지가 완성됩니다.
- 최종 합성 (곱하기):
- 완성된 조명 마스크를 게임 화면에 곱하기(MULT) 모드로 덮어씌웁니다.
- 게임 화면 × 흰색 = 원래 밝기
- 게임 화면 × 검은색 = 완전 어둠
Pygame 횃불 조명 효과 예제 코드

import pygame
import sys
# --- 설정 ---
WIDTH, HEIGHT = 800, 600
FPS = 60
# 조명 설정
# AMBIENT_COLOR: 주변의 기본 어두운 정도. (0,0,0)은 완전 암흑, (50,50,50)은 희미한 달빛.
AMBIENT_COLOR = (30, 30, 30)
TORCH_RADIUS = 200 # 횃불의 반경
# --- 초기화 ---
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pygame Torchlight Gradient Example")
clock = pygame.time.Clock()
# --- 1. 그라데이션 조명 이미지 생성 함수 (핵심) ---
def create_gradient_light_surface(radius):
"""
중심은 흰색(가장 밝음), 가장자리는 검은색(어두움)으로 변하는
부드러운 방사형 그라데이션 이미지를 생성합니다.
"""
# 빛을 담을 서피스 생성 (정사각형)
light_surf = pygame.Surface((radius * 2, radius * 2))
# 일단 검은색으로 채움 (빛이 닿지 않는 영역)
light_surf.fill((0, 0, 0))
# 바깥쪽에서 안쪽으로 원을 겹쳐 그리며 그라데이션 생성
# radius부터 1까지 1씩 줄어들며 반복
for r in range(radius, 0, -1):
# 중심부로부터의 거리 비율 (0.0 ~ 1.0)
# 안쪽(r이 작음)일수록 distance_ratio가 0에 가까움
distance_ratio = r / radius
# 밝기 강도 계산 (1.0 - 거리 비율)
# 중심이 1.0(최대 밝기), 가장자리가 0.0
intensity = 1.0 - distance_ratio
# (선택 사항) 더 자연스러운 횃불 느낌을 위한 비선형 감쇠
# 강도를 제곱하면 빛이 퍼지는 느낌이 더 부드러워집니다.
# intensity = intensity * intensity
# 강도를 0~255 RGB 값으로 변환
color_val = int(255 * intensity)
# 값의 범위 안전장치
color_val = max(0, min(255, color_val))
# 그라데이션 색상 (흰색 -> 회색 -> 검정색)
color = (color_val, color_val, color_val)
# 중심에 원 그리기. 안쪽 원이 바깥 원을 덮어쓰면서 그라데이션이 됨.
pygame.draw.circle(light_surf, color, (radius, radius), r)
# 이 이미지는 나중에 '더하기' 모드로 사용되므로 검은색 배경이 중요합니다.
return light_surf
# --- 리소스 사전 로딩 ---
# 매 프레임 생성하면 느리므로 미리 만들어둡니다.
torch_light_img = create_gradient_light_surface(TORCH_RADIUS)
# 테스트용 배경 이미지 생성
background = pygame.Surface((WIDTH, HEIGHT))
background.fill((100, 100, 100)) # 기본 회색 바닥
# 타일 패턴 그리기
for x in range(0, WIDTH, 50):
pygame.draw.line(background, (80, 80, 80), (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, 50):
pygame.draw.line(background, (80, 80, 80), (0, y), (WIDTH, y))
# 무작위 오브젝트 몇 개 배치
pygame.draw.rect(background, (200, 50, 50), (150, 150, 100, 100)) # 빨간 상자
pygame.draw.circle(background, (50, 200, 50), (600, 400), 80) # 초록 원
# --- 메인 루프 ---
running = True
# 어둠 마스크용 서피스 생성 (화면 크기)
darkness_mask = pygame.Surface((WIDTH, HEIGHT))
while running:
# 1. 이벤트 처리
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 마우스 위치 가져오기 (횃불 중심)
mx, my = pygame.mouse.get_pos()
# --- 2. 렌더링 파이프라인 ---
# [단계 A] 게임 월드 그리기
# 실제 게임 화면을 먼저 다 그립니다.
screen.blit(background, (0, 0))
# 플레이어 캐릭터 등을 그리는 위치...
pygame.draw.circle(screen, (255, 255, 0), (mx, my), 10) # 마우스 위치 표시 (작은 노란 점)
# [단계 B] 조명 마스크(Darkness Mask) 준비
# B-1. 마스크 전체를 주변광(Ambient Color)으로 채움
darkness_mask.fill(AMBIENT_COLOR)
# B-2. 마스크에 횃불 그라데이션 조명 추가 (BLEND_RGB_ADD)
# 어두운 마스크 위에 밝은 조명 이미지를 '더하기' 합니다.
# 조명 이미지의 검은 부분은 더해도 변화가 없고, 흰 부분은 마스크를 하얗게 만듭니다.
light_pos = (mx - TORCH_RADIUS, my - TORCH_RADIUS)
darkness_mask.blit(torch_light_img, light_pos, special_flags=pygame.BLEND_RGB_ADD)
# [단계 C] 최종 합성 (BLEND_RGB_MULT)
# 완성된 조명 마스크를 게임 화면에 '곱하기' 합니다.
# 마스크의 하얀 부분(횃불)은 원래 색을 유지하고, 어두운 부분은 화면을 어둡게 만듭니다.
screen.blit(darkness_mask, (0, 0), special_flags=pygame.BLEND_RGB_MULT)
# --- 화면 업데이트 ---
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
sys.exit()
코드 주요 포인트 설명
- AMBIENT_COLOR = (30, 30, 30):
- 이 값이 전체적인 어둠의 농도를 결정합니다. (0, 0, 0)으로 설정하면 횃불 밖은 아무것도 안 보이는 완전한 암흑이 됩니다. 값을 조금 올려서 희미하게 보이게 하는 것이 더 자연스럽습니다.
- create_gradient_light_surface(radius) 함수:
- 이전 예제와 달리 알파(투명도)가 아니라 **색상의 밝기(Grayscale)**를 조절합니다.
- 중심은 (255, 255, 255) 흰색이고 가장자리는 (0, 0, 0) 검은색인 원을 촘촘하게 겹쳐 그려서 부드러운 그라데이션 이미지를 만듭니다.
- 팁: 주석 처리된 intensity = intensity * intensity 부분의 주석을 해제하면 빛이 선형적으로 줄어들지 않고 중심에 더 집중된 형태로 바뀝니다 (더 리얼한 횃불 느낌).
- 블렌딩 모드 (special_flags)의 마법:
- darkness_mask.blit(..., special_flags=pygame.BLEND_RGB_ADD): 어두운 기본 배경에 빛 이미지를 더해서 빛이 있는 부분을 밝게 만듭니다.
- screen.blit(darkness_mask, ..., special_flags=pygame.BLEND_RGB_MULT): 준비된 마스크를 게임 화면에 곱해서 최종적으로 어둡게 만듭니다. 이 단계에서 마스크의 흰색 영역은 게임 화면을 그대로 보여주고, 검은색 영역은 가리게 됩니다.
이 코드를 실행하면 마우스 커서를 따라다니는 부드러운 빛이 주변 어둠을 밝히는 효과를 볼 수 있습니다.