import pygame
import math
import sys

# Initialize Pygame
pygame.init()

# Screen dimensions
WIDTH, HEIGHT = 800, 600
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("3D Sphere with Realistic Lighting and Sun Rays")

# Clock for controlling frame rate
CLOCK = pygame.time.Clock()
FPS = 30

# Sphere properties
SPHERE_RADIUS = 200
SPHERE_CENTER = (WIDTH // 2, HEIGHT // 2 + 50)  # Slightly lower to center vertically

# Light properties
# Initial light direction (unit vector)
light_dir = [1, 1, -1]
# Normalize the light direction
length = math.sqrt(light_dir[0]**2 + light_dir[1]**2 + light_dir[2]**2)
light_dir = [component / length for component in light_dir]

# Light movement step (radians)
LIGHT_STEP = 0.05  # Increased for smoother rotation

# Colors
BACKGROUND_COLOR = (10, 10, 50)
SPHERE_COLOR = (255, 165, 0)  # Orange
SUN_COLOR = (255, 255, 200)   # Soft yellow for the sun
RAY_COLOR = (255, 255, 200)   # Same as sun for consistency

def normalize(v):
    length = math.sqrt(sum([coord ** 2 for coord in v]))
    if length == 0:
        return [0, 0, 0]
    return [coord / length for coord in v]

def handle_input():
    global light_dir
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    keys = pygame.key.get_pressed()

    # Adjust light direction based on arrow keys and W/S keys
    # Using spherical coordinates for light direction adjustments
    if keys[pygame.K_LEFT]:
        # Rotate light direction to the left around Y axis
        angle = LIGHT_STEP
        new_x = light_dir[0] * math.cos(angle) - light_dir[2] * math.sin(angle)
        new_z = light_dir[0] * math.sin(angle) + light_dir[2] * math.cos(angle)
        light_dir[0], light_dir[2] = new_x, new_z
    if keys[pygame.K_RIGHT]:
        # Rotate light direction to the right around Y axis
        angle = -LIGHT_STEP
        new_x = light_dir[0] * math.cos(angle) - light_dir[2] * math.sin(angle)
        new_z = light_dir[0] * math.sin(angle) + light_dir[2] * math.cos(angle)
        light_dir[0], light_dir[2] = new_x, new_z
    if keys[pygame.K_UP]:
        # Rotate light direction up around X axis
        angle = LIGHT_STEP
        new_y = light_dir[1] * math.cos(angle) - light_dir[2] * math.sin(angle)
        new_z = light_dir[1] * math.sin(angle) + light_dir[2] * math.cos(angle)
        light_dir[1], light_dir[2] = new_y, new_z
    if keys[pygame.K_DOWN]:
        # Rotate light direction down around X axis
        angle = -LIGHT_STEP
        new_y = light_dir[1] * math.cos(angle) - light_dir[2] * math.sin(angle)
        new_z = light_dir[1] * math.sin(angle) + light_dir[2] * math.cos(angle)
        light_dir[1], light_dir[2] = new_y, new_z
    if keys[pygame.K_w]:
        # Rotate light direction forward around Z axis
        angle = LIGHT_STEP
        new_x = light_dir[0] * math.cos(angle) - light_dir[1] * math.sin(angle)
        new_y = light_dir[0] * math.sin(angle) + light_dir[1] * math.cos(angle)
        light_dir[0], light_dir[1] = new_x, new_y
    if keys[pygame.K_s]:
        # Rotate light direction backward around Z axis
        angle = -LIGHT_STEP
        new_x = light_dir[0] * math.cos(angle) - light_dir[1] * math.sin(angle)
        new_y = light_dir[0] * math.sin(angle) + light_dir[1] * math.cos(angle)
        light_dir[0], light_dir[1] = new_x, new_y

    # Normalize light direction after adjustments
    light_dir = normalize(light_dir)

def draw_sphere():
    # Draw the sphere pixel by pixel
    for y in range(SPHERE_CENTER[1] - SPHERE_RADIUS, SPHERE_CENTER[1] + SPHERE_RADIUS):
        dy = y - SPHERE_CENTER[1]
        for x in range(SPHERE_CENTER[0] - SPHERE_RADIUS, SPHERE_CENTER[0] + SPHERE_RADIUS):
            dx = x - SPHERE_CENTER[0]
            distance_sq = dx * dx + dy * dy
            if distance_sq <= SPHERE_RADIUS * SPHERE_RADIUS:
                # Calculate z coordinate on the sphere
                try:
                    dz = math.sqrt(SPHERE_RADIUS * SPHERE_RADIUS - distance_sq)
                except:
                    dz = 0  # Handle floating point errors

                # Surface normal (assuming sphere is centered at origin)
                normal = [dx, dy, dz]
                normal = normalize(normal)

                # Dot product between normal and light direction
                dot = normal[0] * light_dir[0] + normal[1] * light_dir[1] + normal[2] * light_dir[2]
                dot = max(dot, 0)  # Clamp to [0,1]

                # Calculate shading
                shading = dot

                # Apply shading to sphere color
                color = (
                    min(int(SPHERE_COLOR[0] * shading), 255),
                    min(int(SPHERE_COLOR[1] * shading), 255),
                    min(int(SPHERE_COLOR[2] * shading), 255),
                )

                # Draw the pixel
                SCREEN.set_at((x, y), color)

def draw_sun_and_rays():
    # Determine the sun's position based on the light direction
    # We'll place the sun at a distance outside the screen in the direction opposite to light_dir
    sun_distance = 1000  # Arbitrary large distance to place sun outside the screen
    sun_x = int(SPHERE_CENTER[0] - light_dir[0] * sun_distance)
    sun_y = int(SPHERE_CENTER[1] - light_dir[1] * sun_distance)

    # Draw the sun as a circle
    SUN_RADIUS = 30
    pygame.draw.circle(SCREEN, SUN_COLOR, (sun_x, sun_y), SUN_RADIUS)

    # Draw sun rays
    NUM_RAYS = 20  # Number of rays to draw
    RAY_LENGTH = 150  # Length of each ray
    angle_between_rays = (2 * math.pi) / NUM_RAYS

    for i in range(NUM_RAYS):
        angle = i * angle_between_rays
        # Calculate ray direction based on light_dir and ensure rays emanate from the sun
        ray_dir_x = light_dir[0] + 0.3 * math.cos(angle)  # Adding spread to rays
        ray_dir_y = light_dir[1] + 0.3 * math.sin(angle)
        ray_dir = normalize([ray_dir_x, ray_dir_y, light_dir[2]])
        
        # Starting point is the sun's position
        start_pos = (sun_x, sun_y)
        
        # Ending point is towards the sphere with some spread
        end_x = sun_x + int(ray_dir[0] * RAY_LENGTH)
        end_y = sun_y + int(ray_dir[1] * RAY_LENGTH)
        end_pos = (end_x, end_y)
        
        # Draw the ray as a line
        pygame.draw.line(SCREEN, RAY_COLOR, start_pos, end_pos, 2)

def main():
    while True:
        handle_input()

        # Clear screen
        SCREEN.fill(BACKGROUND_COLOR)

        # Draw sun rays and sun
        draw_sun_and_rays()

        # Draw sphere with current lighting
        draw_sphere()

        # Optional: Draw light direction vector for visualization
        # You can uncomment the following lines to visualize light direction
        """
        # Scale light direction for visualization
        light_visual_end = (
            int(SPHERE_CENTER[0] + light_dir[0] * 100),
            int(SPHERE_CENTER[1] + light_dir[1] * 100)
        )
        pygame.draw.line(SCREEN, (255, 255, 255), SPHERE_CENTER, light_visual_end, 2)
        pygame.draw.circle(SCREEN, (255, 255, 255), light_visual_end, 5)
        """

        # Update the display
        pygame.display.flip()

        # Maintain frame rate
        CLOCK.tick(FPS)

if __name__ == "__main__":
    main()

Display the source of light, add wet effect on surface (on/off), provide full updated code