Friday, December 26, 2025

Ambient Scene

 Been working away with Copilot on that winter shimmering snow effect. Below:

I've changed the backgound color to :  screen.fill((135, 140, 155)). Could I have a small proportion of my snowflakes shimmer. (In actual fact, if I am motionless standing outside, the same shimmering elements will go on shimmering. It is only as I move that the shimmering elements change, and at the speed that I am imoving). Here, we are trying to catch the shimmer effect on a scene where the wind and flake fall cause the shimmer!!

Louise, this is a beautiful observation — and you’re absolutely right about the physics.

The shimmer you see outdoors isn’t random flicker; it’s angle‑dependent specular reflection. When you move, the angle changes, so different crystals catch the light. When you stand still, the same ones keep shimmering.

In your ambient winter scene, the “camera” is fixed, but the snowflakes are moving, so we can simulate shimmer by letting only a small subset of flakes have angle‑dependent brightness changes.

Let’s add that effect cleanly and elegantly.


                                                                               


The code:

import pygame
import random
import math

# ----------------------------
# Settings
# ----------------------------
WIDTH, HEIGHT = 1000, 650

# Crystals
N_CRYSTALS = 300
MIN_RADIUS = 3
MAX_RADIUS = 6
MAX_ALPHA = 120
FADE_SPEED = 0.08
ON_PROB = 0.01
OFF_PROB = 0.01

# Snow
N_SNOW_FOREGROUND = 120
N_SNOW_BACKGROUND = 80
FG_SPEED = 1.2
BG_SPEED = 0.4
FG_SIZE = (2, 4)
BG_SIZE = (1, 2)

# ----------------------------
# Crystal class
# ----------------------------
class Crystal:
def __init__(self):
self.x = random.uniform(0, WIDTH)
self.y = random.uniform(0, HEIGHT)
self.radius = random.uniform(MIN_RADIUS, MAX_RADIUS)

icy_palette = [
(180, 210, 255),
(200, 230, 255),
(220, 220, 255),
(230, 200, 255),
(200, 220, 240),
]
self.color = random.choice(icy_palette)

self.alpha = random.uniform(20, MAX_ALPHA)
self.target_on = True

def update(self):
if self.target_on and random.random() < OFF_PROB:
self.target_on = False
elif not self.target_on and random.random() < ON_PROB:
self.target_on = True

target_alpha = MAX_ALPHA if self.target_on else 0
self.alpha += (target_alpha - self.alpha) * FADE_SPEED
self.alpha = max(0, min(MAX_ALPHA, self.alpha))

def draw(self, surface):
layer = pygame.Surface((self.radius*4, self.radius*4), pygame.SRCALPHA)
pygame.draw.circle(
layer,
(*self.color, int(self.alpha)),
(int(self.radius*2), int(self.radius*2)),
int(self.radius)
)
surface.blit(layer, (self.x - self.radius*2, self.y - self.radius*2))


# ----------------------------
# Snowflake class (all shimmer)
# ----------------------------
class Snowflake:
def __init__(self, foreground=True):
self.x = random.uniform(0, WIDTH)
self.y = random.uniform(0, HEIGHT)
self.foreground = foreground

if foreground:
self.speed = FG_SPEED
self.size = random.randint(*FG_SIZE)
else:
self.speed = BG_SPEED
self.size = random.randint(*BG_SIZE)

# --- soft shimmer (all flakes) ---
self.soft_shimmer_phase = random.uniform(0, 2 * math.pi)
self.soft_shimmer_speed = random.uniform(0.02, 0.05)

# --- strong shimmer (10% of flakes) ---
self.strong_shimmer = random.random() < 0.10
self.strong_shimmer_phase = random.uniform(0, 2 * math.pi)
self.strong_shimmer_strength = random.uniform(80, 160)

def update(self):
# Falling motion
self.y += self.speed
self.x += math.sin(self.soft_shimmer_phase + self.y * 0.01) * 0.3

if self.y > HEIGHT:
self.y = -5
self.x = random.uniform(0, WIDTH)

# Update shimmer phases
self.soft_shimmer_phase += self.soft_shimmer_speed
if self.strong_shimmer:
self.strong_shimmer_phase += 0.03 * self.speed

def draw(self, surface):
# Soft shimmer
soft_offset = math.sin(self.soft_shimmer_phase) * 40
brightness = 220 + soft_offset

# Strong shimmer
if self.strong_shimmer:
strong_offset = math.sin(self.strong_shimmer_phase) * self.strong_shimmer_strength
brightness = brightness - strong_offset

brightness = int(max(150, min(255, brightness)))

pygame.draw.circle(surface, (brightness, brightness, brightness),
(int(self.x), int(self.y)), self.size)


# ----------------------------
# Pygame setup
# ----------------------------
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Ambient Winter Scene")
clock = pygame.time.Clock()

# Create objects
crystals = [Crystal() for _ in range(N_CRYSTALS)]
snow_fg = [Snowflake(foreground=True) for _ in range(N_SNOW_FOREGROUND)]
snow_bg = [Snowflake(foreground=False) for _ in range(N_SNOW_BACKGROUND)]

# ----------------------------
# Main loop
# ----------------------------
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False

# Your chosen background color
screen.fill((135, 140, 155))

# Background snow
for flake in snow_bg:
flake.update()
flake.draw(screen)

# Shimmering crystals
for c in crystals:
c.update()
c.draw(screen)

# Foreground snow
for flake in snow_fg:
flake.update()
flake.draw(screen)

pygame.display.flip()
clock.tick(60)

pygame.quit()

So now I have a background with shimmer, and falling snow with shimmer at a different rate...

No comments: