struct material { pen[] p; // diffusepen,emissivepen,specularpen real opacity; real shininess; real metallic; real fresnel0; // Reflectance rate at a perfect normal angle. void operator init(pen diffusepen=black, pen emissivepen=black, pen specularpen=mediumgray, real opacity=opacity(diffusepen), real shininess=defaultshininess, real metallic=defaultmetallic, real fresnel0=defaultfresnel0) { p=new pen[] {diffusepen,emissivepen,specularpen}; this.opacity=opacity; this.shininess=shininess; this.metallic=metallic; this.fresnel0=fresnel0; } void operator init(material m) { p=copy(m.p); opacity=m.opacity; shininess=m.shininess; metallic=m.metallic; fresnel0=m.fresnel0; } pen diffuse() {return p[0];} pen emissive() {return p[1];} pen specular() {return p[2];} void diffuse(pen q) {p[0]=q;} void emissive(pen q) {p[1]=q;} void specular(pen q) {p[2]=q;} } material operator init() { return material(); } void write(file file, string s="", material x, suffix suffix=none) { write(file,s); write(file,"{"); write(file,"diffuse=",x.diffuse()); write(file,", emissive=",x.emissive()); write(file,", specular=",x.specular()); write(file,", opacity=",x.opacity); write(file,", shininess=",x.shininess); write(file,", metallic=",x.metallic); write(file,", F0=",x.fresnel0); write(file,"}",suffix); } void write(string s="", material x, suffix suffix=endl) { write(stdout,s,x,suffix); } bool operator == (material m, material n) { return all(m.p == n.p) && m.opacity == n.opacity && m.shininess == n.shininess && m.metallic == n.metallic && m.fresnel0 == n.fresnel0; } material operator cast(pen p) { return material(p); } material[] operator cast(pen[] p) { return sequence(new material(int i) {return p[i];},p.length); } pen operator ecast(material m) { return m.p.length > 0 ? m.diffuse() : nullpen; } material emissive(material m, bool colors=false) { return material(black+opacity(m.opacity),colors ? m.emissive() : m.diffuse()+m.emissive(),black,m.opacity,1); } pen color(triple normal, material m, light light, transform3 T=light.T) { triple[] position=light.position; if(invisible((pen) m)) return invisible; if(position.length == 0) return m.diffuse(); normal=unit(transpose(inverse(shiftless(T)))*normal); if(settings.twosided) normal *= sgn(normal.z); real s=m.shininess*128; real[] Diffuse=rgba(m.diffuse()); real[] Specular=rgba(m.specular()); real[] p=rgba(m.emissive()); real[] diffuse={0,0,0,0}; real[] specular={0,0,0,0}; for(int i=0; i < position.length; ++i) { triple L=position[i]; real dotproduct=abs(dot(normal,L)); diffuse += dotproduct*light.diffuse[i]; dotproduct=abs(dot(normal,unit(L+Z))); // Phong-Blinn model of specular reflection specular += dotproduct^s*light.specular[i]; } p += diffuse*Diffuse; // Apply specularfactor to partially compensate non-pixel-based rendering. p += specular*Specular*light.specularfactor; return rgb(p[0],p[1],p[2])+opacity(opacity(m.diffuse())); } light operator * (transform3 t, light light) { light light=light(light); return light; } light operator cast(triple v) {return light(v);} light Viewport=light(specularfactor=3,(0.25,-0.25,1)); light White=light(new pen[] {rgb(0.38,0.38,0.45),rgb(0.6,0.6,0.67), rgb(0.5,0.5,0.57)},specularfactor=3, new triple[] {(-2,-1.5,-0.5),(2,1.1,-2.5),(-0.5,0,2)}); light Headlamp=light(white,specular=gray(0.7), specularfactor=3,dir(42,48)); currentlight=Headlamp; light nolight;