Fable:"我想要一个渲染《黄之王》中 Lost Carcosa 的 twigl shader。"
(Shader 完全使用数学过程生成)
twigl.app?ol=true&ss=-P-bSSQ…
Along the shore the cloud waves break,
The twin suns sink behind the lake,
The shadows lengthen
In Carcosa
岸边云浪碎裂,
双日落于湖后,
影渐长
在卡科萨。
黑星升起的夜何等诡秘,
奇异的月在天空环绕
但更奇的是
失落的卡科萨。
许阿得斯将要唱的歌,
王旗破碎飘摇之处,
必寂灭无闻于
幽暗的卡科萨。
我灵魂之歌,声音已死;
你亦无歌而死,如未落的泪
将干涸消逝于
失落的卡科萨。
// 失落的卡科萨 ——
twigl.app
, mode: classic (WebGL 1.0)
// 岸边云浪碎裂。双日落于哈利湖后。
// 黑星升起。奇异之月环绕。塔也沉在水下。
precision highp float;
uniform vec2 resolution;
uniform float time;
#define
T time
vec3 SA, SB, M1, M2; // 双日,双月
float CX, GL, GK; // 镜头晃动,那道目光,是哪道目光
float hash(vec2 p){ p=fract(p*vec2(127.1,311.7)); p+=dot(p,p+19.19); return fract(p.x*p.y); }
float noise(vec2 p){
vec2 i=floor(p), f=fract(p); f=f*f*(3.-2.*f);
return mix(mix(hash(i),hash(i+vec2(1,0)),f.x), mix(hash(i+vec2(0,1)),hash(i+1.),f.x), f.y);
}
float fbm(vec2 p){ float a=.5,s=0.; for(int i=0;i<4;i++){ s+=a*noise(p); p=mat2(1.6,1.2,-1.2,1.6)*p+5.2; a*=.5; } return s; }
float fbm2(vec2 p){ float a=.5,s=0.; for(int i=0;i<2;i++){ s+=a*noise(p); p=mat2(1.6,1.2,-1.2,1.6)*p+5.2; a*=.5; } return s; }
// 雾朝日处是硫磺色,别处是淤紫
vec3 fogColor(vec3 rd){
float s=max(dot(normalize(vec3(rd.x,0.,rd.z)), normalize(vec3(SA.x,0.,SA.z))),0.);
return mix(vec3(.20,.11,.19), vec3(.78,.46,.13), pow(s,3.));
}
vec3 skyBase(vec3 rd){
float el=rd.y;
vec3 c=mix(vec3(.72,.46,.13), vec3(.36,.31,.11), smoothstep(-.05,.13,el));
c=mix(c, vec3(.07,.04,.11), smoothstep(.06,.48,el));
// 一层淡淡的发光薄雾,让黑星有洞可钻
float az=atan(rd.x,rd.z);
float neb=fbm(vec2(az*2.5,el*5.)+vec2(T*.004,0.));
c+=vec3(.13,.07,.18)*neb*smoothstep(.04,.35,el);
// 撕裂感:破碎的黄色流苏被拖过天穹高处
float tat=fbm(vec2(az*5.+T*.012, el*16.+T*.004));
c=mix(c, vec3(.72,.58,.16), smoothstep(.56,.82,tat)*.35*smoothstep(.14,.32,el)*smoothstep(.75,.45,el));
return c;
}
// 星是空缺:暗核带淡色边缘
vec3 blackStars(vec3 c, vec3 rd){
vec2 sp=vec2(atan(rd.x,rd.z)+T*.005, asin(clamp(rd.y,-1.,1.))-T*.0025);
vec2 g=sp*15.;
vec2 id=floor(g), f=fract(g)-.5;
float h=hash(id);
vec2 o=(vec2(hash(id+3.1),hash(id+7.7))-.5)*.6;
float d=length(f-o);
float r=.05+.1*hash(id+1.3);
float vis=step(.58,h)*smoothstep(.03,.22,rd.y)*(.8+.2*sin(T*.4+h*50.));
float core=smoothstep(r,r*.5,d);
float ring=exp(-pow((d-r)/(r*.35),2.));
c*=1.-core*vis*.97;
c+=ring*vis*vec3(.55,.5,.75)*.2;
return c;
}
vec3 hyades(vec3 rd){
vec3 hc=normalize(vec3(.62, .22+.003*T, 1.));
vec3 c=vec3(0.);
for(int i=0;i<9;i++){
float fi=float(i);
vec3 sd=normalize(hc+vec3(hash(vec2(fi,1.))-.5, (hash(vec2(fi,2.))-.5)*.6, 0.)*.14);
float d=length(rd-sd);
float tw=.7+.3*sin(T*1.3+fi*5.);
vec3 col = i==0 ? vec3(1.,.35,.2) : vec3(.95,.92,.85);
c+=col*exp(-d*d*9.e4)*tw*(.6+.6*hash(vec2(fi,3.)));
}
return c*smoothstep(-.02,.06,rd.y);
}
vec3 suns(vec3 rd){
float a=length(rd-SA), b=length(rd-SB);
vec3 c=vec3(1.,.32,.07)*exp(-a*5.5)*.5; // 肿胀的红光
c+=vec3(1.,.85,.55)*exp(-b*11.)*.4; // 小而白的辉光
float dA=smoothstep(.074,.068,a);
float dB=smoothstep(.033,.029,b);
c+=vec3(1.2,.32,.05)*dA*(1.-.55*smoothstep(.035,.074,a)); // 临边昏暗
c+=vec3(1.5,1.3,1.)*dB;
return c;
}
vec3 moon(vec3 c, vec3 rd, vec3 md, float r, vec3 col, vec2 ph){
float d=length(rd-md);
float disc=smoothstep(r,r-.004,d);
float dark=smoothstep(r*1.03,r*1.03-.004,length(rd-md-vec3(ph*r,0.)));
float mot=.78+.4*fbm2((rd.xy-md.xy)*45.+vec2(3.,1.));
vec3 mc=col*mot*(1.-dark*.9);
c=mix(c,mc,disc);
c+=col*exp(-d*25.)*.12;
return c;
}
// 天际线高度按方位角变化:层叠基座、穹顶、尖塔
float towers(float az, float sc, float seed, float hmul, float dens){
float x=az*sc+seed*7.31;
float id=floor(x), f=fract(x)-.5;
float h1=hash(vec2(id,seed)), h2=hash(vec2(id*1.7,seed+4.2)), h3=hash(vec2(id*.3,seed+9.1)), h4=hash(vec2(id*2.3,seed+1.7));
float w=.3+.55*h2;
float hh=(.004+.10*pow(h1,2.))*hmul*step(1.-dens,h3);
float r=abs(f)/(w*.5);
float base =hh*.5*smoothstep(1.,.95,r);
float upper=hh*.35*smoothstep(
查看英文原文
Fable: "I want a twigl shader that renders Lost Carcosa from The King in Yellow."
(Shaders are procedurally generated using math alone)
twigl.app?ol=true&ss=-P-bSSQ…
Along the shore the cloud waves break,
The twin suns sink behind the lake,
The shadows lengthen
In Carcosa
Along the shore the cloud waves break,
The twin suns sink behind the lake,
The shadows lengthen
In Carcosa.
Strange is the night where black stars rise,
And strange moons circle through the skies
But stranger still is
Lost Carcosa.
Songs that the Hyades shall sing,
Where flap the tatters of the King,
Must die unheard in
Dim Carcosa.
Song of my soul, my voice is dead;
Die thou, unsung, as tears unshed
Shall dry and die in
Lost Carcosa.
// LOST CARCOSA —
twigl.app
, mode: classic (WebGL 1.0)
// Along the shore the cloud waves break. Twin suns sink behind Lake Hali.
// Black stars rise. Strange moons circle. The towers are also beneath the water.
precision highp float;
uniform vec2 resolution;
uniform float time;
#define
T time
vec3 SA, SB, M1, M2; // twin suns, two moons
float CX, GL, GK; // camera sway, the glance, which glance
float hash(vec2 p){ p=fract(p*vec2(127.1,311.7)); p+=dot(p,p+19.19); return fract(p.x*p.y); }
float noise(vec2 p){
vec2 i=floor(p), f=fract(p); f=f*f*(3.-2.*f);
return mix(mix(hash(i),hash(i+vec2(1,0)),f.x), mix(hash(i+vec2(0,1)),hash(i+1.),f.x), f.y);
}
float fbm(vec2 p){ float a=.5,s=0.; for(int i=0;i<4;i++){ s+=a*noise(p); p=mat2(1.6,1.2,-1.2,1.6)*p+5.2; a*=.5; } return s; }
float fbm2(vec2 p){ float a=.5,s=0.; for(int i=0;i<2;i++){ s+=a*noise(p); p=mat2(1.6,1.2,-1.2,1.6)*p+5.2; a*=.5; } return s; }
// fog is sulfur toward the suns, bruise-violet elsewhere
vec3 fogColor(vec3 rd){
float s=max(dot(normalize(vec3(rd.x,0.,rd.z)), normalize(vec3(SA.x,0.,SA.z))),0.);
return mix(vec3(.20,.11,.19), vec3(.78,.46,.13), pow(s,3.));
}
vec3 skyBase(vec3 rd){
float el=rd.y;
vec3 c=mix(vec3(.72,.46,.13), vec3(.36,.31,.11), smoothstep(-.05,.13,el));
c=mix(c, vec3(.07,.04,.11), smoothstep(.06,.48,el));
// a faint luminous haze, so the black stars have something to be holes in
float az=atan(rd.x,rd.z);
float neb=fbm(vec2(az*2.5,el*5.)+vec2(T*.004,0.));
c+=vec3(.13,.07,.18)*neb*smoothstep(.04,.35,el);
// tatters: ragged yellow streamers dragged across the upper sky
float tat=fbm(vec2(az*5.+T*.012, el*16.+T*.004));
c=mix(c, vec3(.72,.58,.16), smoothstep(.56,.82,tat)*.35*smoothstep(.14,.32,el)*smoothstep(.75,.45,el));
return c;
}
// stars as absences: dark cores with a pale rim
vec3 blackStars(vec3 c, vec3 rd){
vec2 sp=vec2(atan(rd.x,rd.z)+T*.005, asin(clamp(rd.y,-1.,1.))-T*.0025);
vec2 g=sp*15.;
vec2 id=floor(g), f=fract(g)-.5;
float h=hash(id);
vec2 o=(vec2(hash(id+3.1),hash(id+7.7))-.5)*.6;
float d=length(f-o);
float r=.05+.1*hash(id+1.3);
float vis=step(.58,h)*smoothstep(.03,.22,rd.y)*(.8+.2*sin(T*.4+h*50.));
float core=smoothstep(r,r*.5,d);
float ring=exp(-pow((d-r)/(r*.35),2.));
c*=1.-core*vis*.97;
c+=ring*vis*vec3(.55,.5,.75)*.2;
return c;
}
vec3 hyades(vec3 rd){
vec3 hc=normalize(vec3(.62, .22+.003*T, 1.));
vec3 c=vec3(0.);
for(int i=0;i<9;i++){
float fi=float(i);
vec3 sd=normalize(hc+vec3(hash(vec2(fi,1.))-.5, (hash(vec2(fi,2.))-.5)*.6, 0.)*.14);
float d=length(rd-sd);
float tw=.7+.3*sin(T*1.3+fi*5.);
vec3 col = i==0 ? vec3(1.,.35,.2) : vec3(.95,.92,.85);
c+=col*exp(-d*d*9.e4)*tw*(.6+.6*hash(vec2(fi,3.)));
}
return c*smoothstep(-.02,.06,rd.y);
}
vec3 suns(vec3 rd){
float a=length(rd-SA), b=length(rd-SB);
vec3 c=vec3(1.,.32,.07)*exp(-a*5.5)*.5; // swollen red glow
c+=vec3(1.,.85,.55)*exp(-b*11.)*.4; // small white glow
float dA=smoothstep(.074,.068,a);
float dB=smoothstep(.033,.029,b);
c+=vec3(1.2,.32,.05)*dA*(1.-.55*smoothstep(.035,.074,a)); // limb-darkened
c+=vec3(1.5,1.3,1.)*dB;
return c;
}
vec3 moon(vec3 c, vec3 rd, vec3 md, float r, vec3 col, vec2 ph){
float d=length(rd-md);
float disc=smoothstep(r,r-.004,d);
float dark=smoothstep(r*1.03,r*1.03-.004,length(rd-md-vec3(ph*r,0.)));
float mot=.78+.4*fbm2((rd.xy-md.xy)*45.+vec2(3.,1.));
vec3 mc=col*mot*(1.-dark*.9);
c=mix(c,mc,disc);
c+=col*exp(-d*25.)*.12;
return c;
}
// skyline height as a function of azimuth: tiered bases, domes, spires
float towers(float az, float sc, float seed, float hmul, float dens){
float x=az*sc+seed*7.31;
float id=floor(x), f=fract(x)-.5;
float h1=hash(vec2(id,seed)), h2=hash(vec2(id*1.7,seed+4.2)), h3=hash(vec2(id*.3,seed+9.1)), h4=hash(vec2(id*2.3,seed+1.7));
float w=.3+.55*h2;
float hh=(.004+.10*pow(h1,2.))*hmul*step(1.-dens,h3);
float r=abs(f)/(w*.5);
float base =hh*.5*smoothstep(1.,.95,r);
float upper=hh*.35*smoothstep(.6,.55,r);
float third=hh*.15*smoothstep(.32,.28,r);
float dome =hh*.3*sqrt(max(0.,1.-r*r/.36))*step(.45,h4);
float spire=hh*.7*smoothstep(.09,.05,abs(r-(h4-.5)*.4))*step(h4,.45);
return base+upper+third+dome+spire;
}
vec2 king(float az, float el){
float ka=-.75+1.5*hash(vec2(GK,13.));
float x=az-ka;
float H=.27+.06*hash(vec2(GK,5.));
float u=clamp(el/H,0.,1.);
float w=mix(.05,.034,u); // a column of robe
w+=.02*(1.-u)*fbm(vec2(el*50.,GK*3.)); // ragged edge
w=mix(w,.006,smoothstep(.84,.9,u)); // neck
float robe=smoothstep(w+.003,w-.003,abs(x))*step(el,H*.9)*step(0.,el);
float holes=smoothstep(.5,.62,fbm(vec2(x*70.+GK*7.,el*5.)))*pow(1.-u,2.)*1.4; // tatters hang open
robe*=clamp(1.-holes,0.,1.);
float head=smoothstep(.017,.014,length(vec2(x,el-H*.92)*vec2(1.,1.15)));
return vec2(robe,head);
}
// everything at infinity. seed>0 means we are looking into the lake.
vec3 scene(vec3 rd, float seed){
vec3 c=skyBase(rd);
c=blackStars(c,rd);
c+=suns(rd);
c+=hyades(rd);
c=moon(c,rd,M1,.045,vec3(.72,.78,.6),vec2(.45,.15));
float az=atan(rd.x,rd.z), el=rd.y;
vec3 fc=fogColor(rd);
vec3 dark=vec3(.035,.02,.05);
float H,m;
H=towers(az,70.,1.,.6,.9); c=mix(c,mix(dark,fc,.75),smoothstep(H+.0012,H-.0012,el));
H=towers(az,9.,2.,3.2,.22); c=mix(c,mix(dark,fc,.5), smoothstep(H+.0012,H-.0012,el));
if(seed>.5){ // drowned spires: present only in the reflection
H=towers(az,9.,7.,3.6,.18); c=mix(c,mix(dark,fc,.35),smoothstep(H+.0012,H-.0012,el));
}
H=towers(az+CX*.025,40.,3.,1.,.8); c=mix(c,mix(dark,fc,.45),smoothstep(H+.0012,H-.0012,el));
H=towers(az+CX*.06,22.,4.,1.6,.65);
m=smoothstep(H+.0012,H-.0012,el);
// a few windows still lit, guttering
vec2 wg=vec2((az+CX*.06)*110.,el*150.);
vec2 wid=floor(wg), wf=fract(wg);
float lit=step(.965,hash(wid))*step(abs(wf.x-.5),.2)*step(abs(wf.y-.5),.32);
lit*=.35+.65*pow(.5+.5*sin(T*.9+hash(wid+1.)*30.),3.);
c=mix(c,mix(dark,fc,.2)+vec3(.9,.7,.25)*lit*.7,m);
// mist off Hali, eating the base of the city
float mist=smoothstep(.1,-.02,el)*(.3+.5*fbm2(vec2(az*3.,T*.03)));
c=mix(c,fc,mist*.6);
// something vast stands in the lake, for a moment
vec2 kg=king(az,el);
c=mix(c, dark*.5, kg.x*GL*.9);
c=mix(c, vec3(.55,.5,.42), kg.y*GL*.9); // the mask
// the small moon passes in front of the towers
c=moon(c,rd,M2,.022,vec3(.85,.8,.65),vec2(-.4,-.2));
return c;
}
// cloud waves: rolling crests that lean and break toward the shore
float cden(vec3 p, float lo){
float z=p.z+T*1.0;
z+=3.5*noise(p.xz*.08+vec2(0.,T*.02));
float crest=pow(.5+.5*cos(z*.45),4.);
float amp=.45+.7*noise(vec2(p.x*.09,z*.05)+3.);
float hmax=.08+2.3*crest*amp;
vec2 q=vec2(p.x, p.z+p.y*p.y*.45*crest);
float n;
if(lo>.5) n=fbm2(q*.5+vec2(0.,-T*.25));
else n=fbm(q*.5+vec2(0.,-T*.25))+.45*fbm2(q*3.+vec2(T*.2,-T*.8))-.22;
float env=smoothstep(hmax,hmax*.45,p.y);
float m=n*.8+env*.75-.72;
return smoothstep(0.,.25,m)*smoothstep(0.,.12,p.y)*smoothstep(3.,5.,p.z);
}
vec4 clouds(vec3 ro, vec3 rd, float tmax){
float t0=.3;
float tend = rd.y>0. ? (2.5-ro.y)/rd.y : (.02-ro.y)/rd.y;
tend=min(tend,min(tmax,26.));
if(tend<=t0) return vec4(0.,0.,0.,1.);
float dt=(tend-t0)/32.;
float t=t0+dt*hash(gl_FragCoord.xy);
vec3 L=normalize(SA+vec3(0.,.2,0.));
vec3 acc=vec3(0.); float tr=1.;
for(int i=0;i<32;i++){
vec3 p=ro+rd*t;
float d=cden(p,0.);
if(d>.01){
float dl=cden(p+L*.8,1.);
float lit=exp(-dl*4.);
vec3 col=mix(vec3(.09,.06,.13), vec3(1.1,.55,.18), lit*lit);
col*=1.-.45*d;
col+=vec3(.3,.2,.35)*pow(p.y/2.4,2.);
col=mix(col,fogColor(rd),1.-exp(-t*.03));
float a=1.-exp(-d*dt*1.6);
acc+=col*a*tr;
tr*=1.-a;
if(tr<.02) break;
}
t+=dt;
}
return vec4(acc,tr);
}
float wh(vec2 p){
float rr=length(p-vec2(1.5,13.));
return noise(p*1.2+vec2(T*.25,T*.15))*.5+noise(p*3.5+vec2(-T*.3,T*.5))*.25+noise(p*9.+vec2(T*.9,-T*.4))*.1
+.25*sin(rr*2.2-T*1.4)*exp(-rr*.1);
}
void main(){
vec2 uv=(gl_FragCoord.xy*2.-resolution)/resolution.y;
SA=normalize(vec3(-.38, .03+.012*sin(T*.06), 1.));
SB=normalize(vec3( .20, .075+.01*sin(T*.08+1.), 1.));
M1=normalize(vec3(1.1*sin(T*.04+2.), .32+.18*cos(T*.04+2.), 1.));
M2=normalize(vec3(-1.4*sin(T*.09), .12+.1*cos(T*.09), 1.));
float gph=T*.21+1.3;
GL=pow(max(sin(gph),0.),40.);
GK=floor(gph/6.2832);
vec3 ro=vec3(.3*sin(T*.06), 2.4+.05*sin(T*.2), 0.);
CX=ro.x;
vec3 ta=vec3(ro.x+.5*sin(T*.045), 2.75, 8.);
vec3 fw=normalize(ta-ro), rt=normalize(cross(vec3(0.,1.,0.),fw)), up=cross(fw,rt);
vec3 rd=normalize(fw*(1.5+.04*sin(T*.13))+uv.x*rt+uv.y*up);
vec3 col; float tmax=100.;
if(rd.y<0.){
float t=-ro.y/rd.y; tmax=t;
vec3 p=ro+rd*t;
vec2 e=vec2(.03,0.);
float h=wh(p.xz);
float amp=.25/(1.+t*.08);
vec3 n=normalize(vec3((h-wh(p.xz+e))*amp, e.x, (h-wh(p.xz+e.yx))*amp));
vec3 rr=reflect(rd,n); rr.y=max(rr.y,.005);
vec3 refl=scene(rr,1.);
float F=.03+.97*pow(1.-max(dot(n,-rd),0.),5.);
col=mix(vec3(.03,.02,.035),refl,F);
col=mix(col,fogColor(rd),1.-exp(-t*.015));
} else {
col=scene(rd,0.);
}
vec4 cl=clouds(ro,rd,tmax);
col=col*cl.a+cl.rgb;
// every so often, something looks back
float lum=dot(col,vec3(.3,.59,.11));
col=mix(col, vec3(lum)*vec3(1.1,.9,.3)*1.5, GL*.6);
col=pow(clamp(col,0.,1.),vec3(1.02));
col*=1.-.5*pow(length(uv*vec2(.55,.85)),3.);
col+=(hash(gl_FragCoord.xy+fract(T*7.)*100.)-.5)*.045;
gl_FragColor=vec4(col,1.);
}