material.py

Preview

Code · 4.4 KiB
"""Analytic iridescent glass: spectral face paint, caustics, and contour bands."""
import numpy as np
from scipy.interpolate import PchipInterpolator
import lighting

def color_gradient(y,stops):
    return np.stack([PchipInterpolator([p[0] for p in stops],[p[1][c] for p in stops], extrapolate=False)(np.clip(y,stops[0][0],stops[-1][0])) for c in range(3)],axis=-1)

def blend(base,color,alpha):
    return base*(1-alpha[...,None])+np.asarray(color)*alpha[...,None]

def g(z,mu,sigma): return np.exp(-0.5*((z-mu)/sigma)**2)
def sigmoid(z): return 1/(1+np.exp(-np.clip(z,-60,60)))

def paint_front(x,y,d,calibration=True):
    c=color_gradient(y,[(55,(4,21,29)),(62,(100,134,145)),(70,(211,139,167)),(82,(248,88,100)),(100,(254,44,3)),(125,(243,45,16)),(150,(205,52,65)),(200,(123,62,124)),(250,(64,81,179)),(300,(31,98,222)),(400,(30,139,237)),(500,(88,182,236)),(600,(188,210,225)),(650,(217,217,226)),(678,(224,214,236)),(685,(221,202,252)),(695,(116,52,255))])
    c=blend(c,np.array([13,121,141]),.48*g(x,654,99)*g(y,294,117))
    c=blend(c,np.array([32,189,201]),.39*g(x,648,115)*g(y,465,116))
    c=blend(c,np.array([255,46,0]),.55*g(x,531,150)*g(y,125,34))
    c=blend(c,np.array([97,84,173]),.22*g(x,332,76)*g(y,215,60))
    # Edge highlight is wide on the left and below, with a dark slim right flank.
    c=blend(c,np.array([90,186,244]),.57*g(d,21,17)*sigmoid((y-200)/45)*sigmoid((440-x)/45))
    c=blend(c,np.array([235,223,234]),.50*g(d,20,19)*sigmoid((y-490)/40))
    c=blend(c,np.array([249,233,218]),.60*g(d,19,13)*sigmoid((y-540)/35)*sigmoid((x-540)/50))
    c=blend(c,np.array([79,84,128]),.54*g(d,6,3.5)*sigmoid((y-160)/40)*sigmoid((570-y)/40)*sigmoid((x-600)/50))
    c=blend(c,np.array([192,126,255]),.69*g(d,1.8,1.5)*sigmoid((y-135)/30))
    c=blend(c,np.array([246,170,190]),.65*g(d,14,8)*g(y,176,66)*sigmoid((x-590)/45))
    c=blend(c,np.array([214,214,242]),.43*g(d,15,10)*g(y,83,20))
    c=blend(c,np.array([44,123,98]),.60*g(d,1.5,1.6)*g(y,57,9))
    c=blend(c,np.array([3,15,24]),.79*g(d,.6,.7)*sigmoid((86-y)/7))
    if calibration:
        c=lighting.apply("front",c,x,y,d)
    return c

def paint_outer(x,y,d,calibration=True):
    c=color_gradient(y,[(54,(0,29,31)),(75,(212,50,50)),(140,(83,93,188)),(250,(41,145,233)),(380,(170,167,220)),(520,(246,218,238)),(650,(247,225,233)),(800,(239,228,228)),(950,(238,220,221)),(1080,(238,218,222)),(1138,(252,150,167))])
    c+=np.stack([15*g(x,156,85)*g(y,720,280),4*g(x,490,200),8*g(x,830,100)*g(y,720,300)],-1)
    c=blend(c,np.array([255,234,241]),.66*g(d,22,19)*sigmoid((y-320)/90))
    c=blend(c,np.array([255,196,246]),.48*g(d,3.3,2.6)*sigmoid((y-260)/95))
    lower=sigmoid((y-990)/35)
    c=blend(c,np.array([255,69,53]),.84*g(d,1.5,1.5)*lower)
    c=blend(c,np.array([255,122,56]),.43*g(d,5,3)*lower*g(x,490,135))
    c=blend(c,np.array([252,218,176]),.39*g(x,263,55)*g(y,1000,115))
    c=blend(c,np.array([250,244,240]),.68*g(d,40,23)*sigmoid((y-810)/45))
    if calibration:
        c=lighting.apply("outer",c,x,y,d)
    return c

def paint_middle(x,y,d,calibration=True):
    c=color_gradient(y,[(54,(0,12,21)),(100,(9,13,53)),(170,(26,31,155)),(250,(69,63,211)),(400,(129,156,241)),(600,(130,184,252)),(700,(138,174,242)),(800,(122,184,225)),(880,(102,185,185)),(900,(140,161,180)),(920,(173,115,137)),(940,(226,55,61)),(952,(82,82,240))])
    c=blend(c,np.array([195,115,246]),.74*g(x,190,50)*g(y,485,200))
    c=blend(c,np.array([168,165,255]),.65*g(x,778,56)*g(y,460,185))
    c=blend(c,np.array([26,70,230]),.77*g(d,12,13)*sigmoid((730-y)/55))
    c=blend(c,np.array([97,58,227]),.68*g(d,1.8,2.5)*sigmoid((y-200)/50)*sigmoid((760-y)/70))
    c=blend(c,np.array([196,182,255]),.62*g(d,29,9)*sigmoid((y-240)/70)*sigmoid((700-y)/70)*sigmoid((x-490)/35))
    c=blend(c,np.array([43,122,139]),.53*g(d,22,12)*sigmoid((y-735)/45))
    c=blend(c,np.array([252,50,18]),.68*g(d,12,10)*sigmoid((y-830)/40))
    c=blend(c,np.array([252,57,4]),.85*g(d,3.4,3.0)*sigmoid((y-735)/40))
    c=blend(c,np.array([112,20,101]),.70*g(d,1.2,1.1)*sigmoid((y-450)/50))
    c=blend(c,np.array([230,144,29]),.49*g(d,23,15)*sigmoid((y-800)/35)*sigmoid((x-540)/50))
    if calibration:
        c=lighting.apply("middle",c,x,y,d)
    c=blend(c,np.array([8,60,100]),.70*g(y,110,60)*g(x,490,180))
    return c

SPECS={"outer":(54,1141,399),"middle":(54,953,338),"front":(55,696,258)}
def paint(role,x,y,d,calibration=True):
    return {"outer":paint_outer,"middle":paint_middle,"front":paint_front}[role](x,y,d,calibration)

Files