cbuffer Camera : register(b0) { float4 viewport; }
struct BodyInput { float4 rect:RECT; float4 colour:COLOR; float4 light:LIGHT; float4 style:STYLE; };
struct BodyPixel { float4 position:SV_POSITION; float2 uv:TEXCOORD0; float4 colour:COLOR0; float4 light:TEXCOORD1; float4 style:TEXCOORD2; };
BodyPixel VSBody(BodyInput input,uint id:SV_VertexID)
{
float2 corners[6]={float2(-1,-1),float2(1,-1),float2(-1,1),float2(-1,1),float2(1,-1),float2(1,1)};
BodyPixel o; o.uv=corners[id]*1.8;
float2 screen=input.rect.xy+o.uv*input.rect.z;
o.position=float4(screen.x/viewport.x*2-1,1-screen.y/viewport.y*2,0,1);
o.colour=input.colour;o.light=input.light;o.style=input.style;return o;
}
float hash(float2 p){return frac(sin(dot(p,float2(127.1,311.7)))*43758.5453);}
float hash3(float3 p){p=frac(p*.1031);p+=dot(p,p.yzx+33.33);return frac((p.x+p.y)*p.z);}
float noise3(float3 p)
{
float3 b=floor(p),f=frac(p);f=f*f*(3-2*f);
return lerp(lerp(lerp(hash3(b),hash3(b+float3(1,0,0)),f.x),lerp(hash3(b+float3(0,1,0)),hash3(b+float3(1,1,0)),f.x),f.y),
lerp(lerp(hash3(b+float3(0,0,1)),hash3(b+float3(1,0,1)),f.x),lerp(hash3(b+float3(0,1,1)),hash3(b+1),f.x),f.y),f.z);
}
float granules(float3 p)
{
float3 cell=floor(p),f=frac(p);float nearest=2;
[unroll]for(int z=-1;z<=1;z++)[unroll]for(int y=-1;y<=1;y++)[unroll]for(int x=-1;x<=1;x++)
{
float3 at=float3(x,y,z);float3 v=at+float3(hash3(cell+at),hash3(cell+at+19.1),hash3(cell+at+43.7))-f;
nearest=min(nearest,dot(v,v));
}
return 1-smoothstep(.04,.62,nearest);
}
float4 PSBody(BodyPixel i):SV_TARGET
{
float r=length(i.uv);float3 rgb=0;float a=0;float kind=i.style.x;
if(kind<.5)
{
float edge=max(fwidth(r),.0015);float seed=i.style.z*3.71;
if(r<=1+edge)
{
float mu=sqrt(saturate(1-r*r));float3 sphere=float3(i.uv.x,-i.uv.y,mu);
// Surface detail follows a 3D sphere, with derivative filtering so
// distant stars do not shimmer or become a screen-space checker.
float3 p=sphere*78+seed;float detail=saturate(1-length(fwidth(p))*.3);
float cells=lerp(.52,granules(p),detail);
float convection=noise3(sphere*6+seed)*.6+noise3(sphere*13+seed)*.4;
float darkRegion=smoothstep(.8,.94,noise3(sphere*4.5+seed));
float limb=.36+.64*pow(mu,.55);
float luminance=(.9+(cells-.5)*.18+(convection-.5)*.15)*(1-darkRegion*.28)*limb;
float3 hot=lerp(i.colour.rgb,float3(1,.95,.84),.18+cells*.12);
rgb=1-exp(-pow(max(hot,.001),2.2)*luminance*3.5);a=1-smoothstep(1-edge,1+edge,r);
}
else
{
float angle=atan2(i.uv.y,i.uv.x);
float rays=.78+.12*sin(angle*11+seed)+.08*sin(angle*23-seed);
float haze=exp(-(r-1)*9)*.33+exp(-(r-1)*3.8)*.075;
// A narrow chromosphere and diffuse streamers, fading to zero
// inside the sprite boundary instead of a clipped orange halo.
a=haze*rays*(1-smoothstep(1.55,1.79,r));rgb=lerp(i.colour.rgb,float3(1,.8,.55),.13);
}
}
else if(kind<1.5)
{
if(r<1)
{
float3 n=float3(i.uv.x,-i.uv.y,sqrt(saturate(1-r*r)));
float light=saturate(dot(n,normalize(i.light.xyz)));
float variation=.82+.1*sin(n.x*17+n.z*13+i.style.z)+.06*sin(n.y*33+n.x*7);
float rim=pow(saturate(1-n.z),3);
rgb=i.colour.rgb*variation*(.12+.88*light)+float3(.12,.25,.36)*rim*.45;a=1;
}
}
else if(kind<2.5)
{
float hub=step(abs(i.uv.x),.19)*step(abs(i.uv.y),.92);
float ring=1-smoothstep(.075,.14,abs(length(float2(i.uv.x,i.uv.y*2.6))-.72));
float decks=step(abs(i.uv.x),.78)*step(abs(i.uv.y),.16);
a=saturate(hub+ring+decks);rgb=i.colour.rgb*(.55+.45*saturate(1-i.uv.y));
}
else if(kind<3.5)
{
float diamond=abs(i.uv.x)+abs(i.uv.y);
a=1-smoothstep(.06,.15,abs(diamond-.75));rgb=i.colour.rgb;
}
else
{
float angle=i.style.w;float2 p=float2(i.uv.x*cos(angle)-i.uv.y*sin(angle),i.uv.x*sin(angle)+i.uv.y*cos(angle));
a=step(-.9,p.y)*step(p.y,.72)*step(abs(p.x),(.9+p.y)*.48);rgb=i.colour.rgb;
}
if(i.style.y>.5)
{
float pixel=1/max(i.style.w,1);float ring=1-smoothstep(pixel*.5,pixel*1.5,abs(r-(1+6*pixel)));
if(ring>a){rgb=float3(1,.67,.28);a=ring;}
}
return float4(rgb*a,a);
}
struct LineInput { float4 position:POSITION;float4 colour:COLOR; };
struct LinePixel {float4 position:SV_POSITION;float4 colour:COLOR;};
LinePixel VSLine(LineInput i){LinePixel o;o.position=float4(i.position.x/viewport.x*2-1,1-i.position.y/viewport.y*2,0,1);o.colour=i.colour;return o;}
float4 PSLine(LinePixel i):SV_TARGET{return float4(i.colour.rgb*i.colour.a,i.colour.a);}
struct Backdrop {float4 position:SV_POSITION;float2 uv:TEXCOORD;};
Backdrop VSBackground(uint id:SV_VertexID){Backdrop o;o.uv=float2((id<<1)&2,id&2);o.position=float4(o.uv*float2(2,-2)+float2(-1,1),0,1);return o;}
float4 PSBackground(Backdrop i):SV_TARGET
{
float2 pixel=i.uv*viewport.xy;float n=hash(floor(pixel/5));
float star=step(.9986,n)*pow(saturate(1-length(frac(pixel/5)-.5)*2),6);
float glow=exp(-length((i.uv-float2(.3,.4))*float2(1,1.4))*3);
return float4(float3(.014,.027,.043)+glow*float3(.01,.026,.044)+star*float3(.25,.35,.45),1);
}
cbuffer Camera : register(b0) { float4 viewport; }
struct BodyInput { float4 rect:RECT; float4 colour:COLOR; float4 light:LIGHT; float4 style:STYLE; };
struct BodyPixel { float4 position:SV_POSITION; float2 uv:TEXCOORD0; float4 colour:COLOR0; float4 light:TEXCOORD1; float4 style:TEXCOORD2; };
BodyPixel VSBody(BodyInput input,uint id:SV_VertexID)
{
float2 corners[6]={float2(-1,-1),float2(1,-1),float2(-1,1),float2(-1,1),float2(1,-1),float2(1,1)};
BodyPixel o; o.uv=corners[id]*1.8;
float2 screen=input.rect.xy+o.uv*input.rect.z;
o.position=float4(screen.x/viewport.x*2-1,1-screen.y/viewport.y*2,0,1);
o.colour=input.colour;o.light=input.light;o.style=input.style;return o;
}
float hash(float2 p){return frac(sin(dot(p,float2(127.1,311.7)))*43758.5453);}
float hash3(float3 p){p=frac(p*.1031);p+=dot(p,p.yzx+33.33);return frac((p.x+p.y)*p.z);}
float noise3(float3 p)
{
float3 b=floor(p),f=frac(p);f=f*f*(3-2*f);
return lerp(lerp(lerp(hash3(b),hash3(b+float3(1,0,0)),f.x),lerp(hash3(b+float3(0,1,0)),hash3(b+float3(1,1,0)),f.x),f.y),
lerp(lerp(hash3(b+float3(0,0,1)),hash3(b+float3(1,0,1)),f.x),lerp(hash3(b+float3(0,1,1)),hash3(b+1),f.x),f.y),f.z);
}
float granules(float3 p)
{
float3 cell=floor(p),f=frac(p);float nearest=2;
[unroll]for(int z=-1;z<=1;z++)[unroll]for(int y=-1;y<=1;y++)[unroll]for(int x=-1;x<=1;x++)
{
float3 at=float3(x,y,z);float3 v=at+float3(hash3(cell+at),hash3(cell+at+19.1),hash3(cell+at+43.7))-f;
nearest=min(nearest,dot(v,v));
}
return 1-smoothstep(.04,.62,nearest);
}
float4 PSBody(BodyPixel i):SV_TARGET
{
float r=length(i.uv);float3 rgb=0;float a=0;float kind=i.style.x;
if(kind<.5)
{
float edge=max(fwidth(r),.0015);float seed=i.style.z*3.71;
if(r<=1+edge)
{
float mu=sqrt(saturate(1-r*r));float3 sphere=float3(i.uv.x,-i.uv.y,mu);
// Surface detail follows a 3D sphere, with derivative filtering so
// distant stars do not shimmer or become a screen-space checker.
float3 p=sphere*78+seed;float detail=saturate(1-length(fwidth(p))*.3);
float cells=lerp(.52,granules(p),detail);
float convection=noise3(sphere*6+seed)*.6+noise3(sphere*13+seed)*.4;
float darkRegion=smoothstep(.8,.94,noise3(sphere*4.5+seed));
float limb=.36+.64*pow(mu,.55);
float luminance=(.9+(cells-.5)*.18+(convection-.5)*.15)*(1-darkRegion*.28)*limb;
float3 hot=lerp(i.colour.rgb,float3(1,.95,.84),.18+cells*.12);
rgb=1-exp(-pow(max(hot,.001),2.2)*luminance*3.5);a=1-smoothstep(1-edge,1+edge,r);
}
else
{
float angle=atan2(i.uv.y,i.uv.x);
float rays=.78+.12*sin(angle*11+seed)+.08*sin(angle*23-seed);
float haze=exp(-(r-1)*9)*.33+exp(-(r-1)*3.8)*.075;
// A narrow chromosphere and diffuse streamers, fading to zero
// inside the sprite boundary instead of a clipped orange halo.
a=haze*rays*(1-smoothstep(1.55,1.79,r));rgb=lerp(i.colour.rgb,float3(1,.8,.55),.13);
}
}
else if(kind<1.5)
{
if(r<1)
{
float3 n=float3(i.uv.x,-i.uv.y,sqrt(saturate(1-r*r)));
float light=saturate(dot(n,normalize(i.light.xyz)));
float variation=.82+.1*sin(n.x*17+n.z*13+i.style.z)+.06*sin(n.y*33+n.x*7);
float rim=pow(saturate(1-n.z),3);
rgb=i.colour.rgb*variation*(.12+.88*light)+float3(.12,.25,.36)*rim*.45;a=1;
}
}
else if(kind<2.5)
{
float hub=step(abs(i.uv.x),.19)*step(abs(i.uv.y),.92);
float ring=1-smoothstep(.075,.14,abs(length(float2(i.uv.x,i.uv.y*2.6))-.72));
float decks=step(abs(i.uv.x),.78)*step(abs(i.uv.y),.16);
a=saturate(hub+ring+decks);rgb=i.colour.rgb*(.55+.45*saturate(1-i.uv.y));
}
else if(kind<3.5)
{
float diamond=abs(i.uv.x)+abs(i.uv.y);
a=1-smoothstep(.06,.15,abs(diamond-.75));rgb=i.colour.rgb;
}
else
{
float angle=i.style.w;float2 p=float2(i.uv.x*cos(angle)-i.uv.y*sin(angle),i.uv.x*sin(angle)+i.uv.y*cos(angle));
a=step(-.9,p.y)*step(p.y,.72)*step(abs(p.x),(.9+p.y)*.48);rgb=i.colour.rgb;
}
if(i.style.y>.5)
{
float pixel=1/max(i.style.w,1);float ring=1-smoothstep(pixel*.5,pixel*1.5,abs(r-(1+6*pixel)));
if(ring>a){rgb=float3(1,.67,.28);a=ring;}
}
return float4(rgb*a,a);
}
struct LineInput { float4 position:POSITION;float4 colour:COLOR; };
struct LinePixel {float4 position:SV_POSITION;float4 colour:COLOR;};
LinePixel VSLine(LineInput i){LinePixel o;o.position=float4(i.position.x/viewport.x*2-1,1-i.position.y/viewport.y*2,0,1);o.colour=i.colour;return o;}
float4 PSLine(LinePixel i):SV_TARGET{return float4(i.colour.rgb*i.colour.a,i.colour.a);}
struct Backdrop {float4 position:SV_POSITION;float2 uv:TEXCOORD;};
Backdrop VSBackground(uint id:SV_VertexID){Backdrop o;o.uv=float2((id<<1)&2,id&2);o.position=float4(o.uv*float2(2,-2)+float2(-1,1),0,1);return o;}
float4 PSBackground(Backdrop i):SV_TARGET
{
float2 pixel=i.uv*viewport.xy;float n=hash(floor(pixel/5));
float star=step(.9986,n)*pow(saturate(1-length(frac(pixel/5)-.5)*2),6);
float glow=exp(-length((i.uv-float2(.3,.4))*float2(1,1.4))*3);
return float4(float3(.014,.027,.043)+glow*float3(.01,.026,.044)+star*float3(.25,.35,.45),1);
}