pov.py

Created by schraf

Created on March 28, 2026

4.41 KB

Découvrez ma chaine YouTube pour d’autres applications et jeux pour votre NUMWORKS


import kandinsky
import math

W, H = 320, 222

CAM   = (0.0, 1.2, -4.0)
LOOK  = (0.0, 0.0,  1.0)
UP    = (0.0, 1.0,  0.0)
FOV   = 0.45

FLOOR_Y = 0.0

SPHERES = [
    (-2, 1.5, 1.2,  1.5,  200,  30,  30, 0.3),
    ( 1.4, 1.80, -0.6,  0.90,  30,  200,  30, 0.25),
    ( 0.0, 0.60, -0.9,  0.40,  30, 30, 200, 0.25),
]

LIGHT = (2.0, 6.0, -2.0)
AMB   = 0.6
MAX_BOUNCE = 2

def add(a, b):  return (a[0]+b[0], a[1]+b[1], a[2]+b[2])
def sub(a, b):  return (a[0]-b[0], a[1]-b[1], a[2]-b[2])
def mul(a, t):  return (a[0]*t, a[1]*t, a[2]*t)
def dot(a, b):  return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
def norm(a):
    n = math.sqrt(dot(a, a))
    return (a[0]/n, a[1]/n, a[2]/n) if n > 1e-9 else a
def reflect(d, n):
    c = 2.0 * dot(d, n)
    return sub(d, mul(n, c))

def hit_floor(ro, rd):
    if abs(rd[1]) < 1e-6:
        return -1.0
    t = (FLOOR_Y - ro[1]) / rd[1]
    return t if t > 0.001 else -1.0

def hit_sphere(ro, rd, s):
    cx, cy, cz, r = s[0], s[1], s[2], s[3]
    oc = sub(ro, (cx, cy, cz))
    b = dot(oc, rd)
    c = dot(oc, oc) - r*r
    disc = b*b - c
    if disc < 0:
        return -1.0
    sq = math.sqrt(disc)
    t = -b - sq
    if t > 0.001:
        return t
    t = -b + sq
    return t if t > 0.001 else -1.0

def nearest(ro, rd, exclude=-1):
    best_t = 1e18
    best   = ('none', -1, -1.0)

    # Sol
    t = hit_floor(ro, rd)
    if 0 < t < best_t:
        best_t = t
        best   = ('floor', 0, t)

    for i, s in enumerate(SPHERES):
        if i == exclude:
            continue
        t = hit_sphere(ro, rd, s)
        if 0 < t < best_t:
            best_t = t
            best   = ('sphere', i, t)

    return best

def checker(px, pz):
    ix = int(math.floor(px))
    iz = int(math.floor(pz))
    if (ix + iz) % 2 == 0:
        return (210, 210,  50)
    else:
        return ( 30, 140,  30)

def in_shadow(pos, nrm):
    ldir = norm(sub(LIGHT, pos))
    orig = add(pos, mul(nrm, 0.01))
    kind, _, _ = nearest(orig, ldir)
    return kind != 'none'

def shade_floor(pos, rd, depth):
    nrm  = (0.0, 1.0, 0.0)
    base = checker(pos[0], pos[2])
    ldir = norm(sub(LIGHT, pos))
    diff = max(0.0, dot(nrm, ldir))

    shadow = in_shadow(pos, nrm)
    if shadow:
        diff *= 0.4

    lit = tuple(int(min(255, b * (AMB + diff * 0.82))) for b in base)

    if depth < MAX_BOUNCE:
        ref_dir = reflect(rd, nrm)
        rc = trace(add(pos, mul(nrm, 0.01)), ref_dir, depth+1)
        r = int(min(255, lit[0]*0.85 + rc[0]*0.15))
        g = int(min(255, lit[1]*0.85 + rc[1]*0.15))
        b = int(min(255, lit[2]*0.85 + rc[2]*0.15))
        return (r, g, b)
    return lit

def shade_sphere(pos, rd, idx, depth):
    s    = SPHERES[idx]
    cx, cy, cz, r, sr, sg, sb, refl = s
    nrm  = norm(sub(pos, (cx, cy, cz)))
    ldir = norm(sub(LIGHT, pos))

    diff = max(0.0, dot(nrm, ldir))
    hv   = norm(sub(ldir, rd))
    spec = max(0.0, dot(nrm, hv)) ** 60

    shadow = in_shadow(pos, nrm)
    if shadow:
        diff *= 0.12
        spec  = 0.0

    dr = int(min(255, sr * (AMB + diff * (1-refl))))
    dg = int(min(255, sg * (AMB + diff * (1-refl))))
    db = int(min(255, sb * (AMB + diff * (1-refl))))

    if depth < MAX_BOUNCE and refl > 0:
        ref_dir = reflect(rd, nrm)
        rc = trace(add(pos, mul(nrm, 0.001)), ref_dir, depth+1, exclude=idx)
        r2 = int(min(255, dr*(1-refl) + rc[0]*refl + spec*255))
        g2 = int(min(255, dg*(1-refl) + rc[1]*refl + spec*255))
        b2 = int(min(255, db*(1-refl) + rc[2]*refl + spec*255))
        return (r2, g2, b2)

    sp = int(spec * 255)
    return (min(255, dr+sp), min(255, dg+sp), min(255, db+sp))

def sky(rd):
    t = max(0.0, rd[1])
    r = int(t * 10)
    g = int(t * 10)
    b = int(10 + t * 40)
    return (r, g, b)

def trace(ro, rd, depth=0, exclude=-1):
    kind, idx, t = nearest(ro, rd, exclude)
    if kind == 'none':
        return sky(rd)
    pos = add(ro, mul(rd, t))
    if kind == 'floor':
        return shade_floor(pos, rd, depth)
    else:
        return shade_sphere(pos, rd, idx, depth)

right = norm((LOOK[2], 0.0, -LOOK[0]))
up    = UP

def make_ray(px, py):
    nx =  (px / W - 0.5) * 2.0
    ny = -(py / H - 0.5) * 2.0
    aspect = W / H
    dx = nx * aspect * FOV
    dy = ny * FOV
    d  = add(LOOK, add(mul(right, dx), mul(up, dy)))
    return norm(d)

def render():
    for x in range(W):
        for y in range(H):
            rd = make_ray(x, y)
            c  = trace(CAM, rd)
            kandinsky.set_pixel(x, y, kandinsky.color(c[0], c[1], c[2]))

render()

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.