lighting.py
At 8d263dfCopy the commit ID
Preview
Code · 1.6 KiB"""Compact analytical colored-light fields calibrated offline.
The optional JSON holds Gaussian lobe amplitudes, not an image or pixel texture.
Every lobe has a fixed geometric position, scale, and contour-distance profile.
"""
from pathlib import Path
import json
import numpy as np
PARAMETERS = Path(__file__).with_name('lighting.json')
SPECS = {'outer': (54., 1141., 399.), 'middle': (54., 953., 338.), 'front': (54., 697., 258.)}
_CACHE = None
def fields(name, x, y, distance):
top,bottom,halfwidth = SPECS[name]
u=(x-490.)/halfwidth
v=(y-top)/(bottom-top)
for cy in np.linspace(0,1,10):
gy=np.exp(-.5*((v-cy)/.135)**2)
for cx in np.linspace(-1,1,5):
yield gy*np.exp(-.5*((u-cx)/.37)**2)
for center,sigma in [(1.5,1.5),(6.,4.),(17.,9.),(38.,18.)]:
gd=np.exp(-.5*((distance-center)/sigma)**2)
for cy,sy in [(cy,.075) for cy in np.linspace(0,1,12)]+[(.015,.016),(.04,.023),(.075,.035)]:
gy=np.exp(-.5*((v-cy)/sy)**2)
for side in [-1,1]:
yield gd*gy*(1/(1+np.exp(-np.clip(side*u*12,-60,60))))
def apply(name, colors, x, y, distance):
global _CACHE
if _CACHE is None:
_CACHE=json.loads(PARAMETERS.read_text()) if PARAMETERS.exists() else {}
if name not in _CACHE:
return colors
correction=np.zeros_like(colors,dtype=np.float32)
for field,coefficient in zip(fields(name,x,y,distance), _CACHE[name]):
correction+=field[...,None]*np.asarray(coefficient,dtype=np.float32)
# Fit is deliberately smooth; clamp only when the completed render is exported.
return colors+correction