99 lines
2.7 KiB
Python
99 lines
2.7 KiB
Python
|
import pygame
|
||
|
import math
|
||
|
import sys
|
||
|
|
||
|
pygame.init()
|
||
|
WIDTH, HEIGHT = 800, 600
|
||
|
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||
|
pygame.display.set_caption("Ray Marching - Single Ray with Steps")
|
||
|
clock = pygame.time.Clock()
|
||
|
|
||
|
# Colors
|
||
|
BLACK = (0, 0, 0)
|
||
|
WHITE = (255, 255, 255)
|
||
|
GRAY = (180, 180, 180)
|
||
|
RED = (255, 0, 0)
|
||
|
GREEN = (0, 255, 0)
|
||
|
BLUE = (0, 0, 255)
|
||
|
|
||
|
# SDF primitives
|
||
|
def sdf_circle(p, center, radius):
|
||
|
return math.hypot(p[0] - center[0], p[1] - center[1]) - radius
|
||
|
|
||
|
def sdf_box(p, center, size):
|
||
|
dx = abs(p[0] - center[0]) - size[0] / 2
|
||
|
dy = abs(p[1] - center[1]) - size[1] / 2
|
||
|
dx = max(dx, 0)
|
||
|
dy = max(dy, 0)
|
||
|
return math.hypot(dx, dy)
|
||
|
|
||
|
objects = [
|
||
|
{"sdf": lambda p: sdf_circle(p, (400, 300), 60), "color": GREEN},
|
||
|
{"sdf": lambda p: sdf_box(p, (200, 150), (100, 100)), "color": BLUE},
|
||
|
{"sdf": lambda p: sdf_box(p, (600, 400), (80, 150)), "color": RED},
|
||
|
]
|
||
|
|
||
|
def scene_sdf(p):
|
||
|
return min(obj["sdf"](p) for obj in objects)
|
||
|
|
||
|
def ray_march(origin, direction, max_steps=100, max_dist=1000, epsilon=1.0):
|
||
|
p = list(origin)
|
||
|
total_dist = 0
|
||
|
steps = []
|
||
|
for _ in range(max_steps):
|
||
|
dist = scene_sdf(p)
|
||
|
steps.append((tuple(p), dist))
|
||
|
if dist < epsilon or total_dist > max_dist:
|
||
|
break
|
||
|
p[0] += direction[0] * dist
|
||
|
p[1] += direction[1] * dist
|
||
|
total_dist += dist
|
||
|
return steps
|
||
|
|
||
|
# Main loop
|
||
|
running = True
|
||
|
while running:
|
||
|
screen.fill(BLACK)
|
||
|
mouse_pos = pygame.mouse.get_pos()
|
||
|
origin = (600, 200)
|
||
|
dx = mouse_pos[0] - origin[0]
|
||
|
dy = mouse_pos[1] - origin[1]
|
||
|
length = math.hypot(dx, dy)
|
||
|
if length == 0:
|
||
|
direction = (0, 0)
|
||
|
else:
|
||
|
direction = (dx / length, dy / length)
|
||
|
|
||
|
# Draw scene objects
|
||
|
for obj in objects:
|
||
|
if obj["color"] == GREEN:
|
||
|
pygame.draw.circle(screen, obj["color"], (400, 300), 60, 2)
|
||
|
elif obj["color"] == BLUE:
|
||
|
pygame.draw.rect(screen, obj["color"], pygame.Rect(150, 100, 100, 100), 2)
|
||
|
elif obj["color"] == RED:
|
||
|
pygame.draw.rect(screen, obj["color"], pygame.Rect(560, 325, 80, 150), 2)
|
||
|
|
||
|
# Perform ray march
|
||
|
steps = ray_march(origin, direction)
|
||
|
|
||
|
# Draw ray steps
|
||
|
for point, dist in steps:
|
||
|
pygame.draw.circle(screen, GRAY, (int(point[0]), int(point[1])), int(dist), 1)
|
||
|
|
||
|
# Draw final point
|
||
|
if steps:
|
||
|
pygame.draw.circle(screen, WHITE, (int(steps[-1][0][0]), int(steps[-1][0][1])), 3)
|
||
|
|
||
|
pygame.draw.line(screen, WHITE, origin, mouse_pos, 1)
|
||
|
pygame.draw.circle(screen, WHITE, origin, 4)
|
||
|
|
||
|
for event in pygame.event.get():
|
||
|
if event.type == pygame.QUIT:
|
||
|
running = False
|
||
|
|
||
|
pygame.display.flip()
|
||
|
clock.tick(60)
|
||
|
|
||
|
pygame.quit()
|
||
|
sys.exit()
|