cbuffer Camera : register(b0)
{
row_major float4x4 World;
row_major float4x4 ViewProjection;
float4 Eye;
float4 HologramColor;
float4 Detail; // viewport width/height, minimum line length, line opacity
float4 Sobel; // original distance scale, edge thickness, max distance, power
float4 TintHigh;
float4 TintLow;
float4 Scan; // original UV scale, display exposure, animation time
};
struct VSInput { float3 position : POSITION; float3 normal : NORMAL; };
struct PSInput { float4 position : SV_POSITION; float3 normal : NORMAL; float3 worldPosition : TEXCOORD0; float lineFade : TEXCOORD1; };
PSInput VSMain(VSInput input)
{
PSInput result;
float4 world = mul(float4(input.position, 1), World);
result.position = mul(world, ViewProjection);
result.worldPosition = world.xyz;
result.normal = normalize(mul(float4(input.normal, 0), World).xyz);
result.lineFade = 1;
return result;
}
float4 PSNormals(PSInput input) : SV_TARGET
{
return float4(normalize(input.normal), length(Eye.xyz - input.worldPosition));
}
PSInput VSLine(VSInput input)
{
PSInput result = VSMain(input);
if (Detail.z > 0)
{
float4 end = mul(mul(float4(input.position + input.normal, 1), World), ViewProjection);
float lengthPixels = length((end.xy / end.w - result.position.xy / result.position.w) * Detail.xy * .5);
result.lineFade = smoothstep(Detail.z, Detail.z + 5, lengthPixels);
}
result.position.z -= .000003 * result.position.w;
return result;
}
float4 PSLine(PSInput input) : SV_TARGET
{
float opacity = Detail.w * input.lineFade;
return float4(HologramColor.rgb * float3(.59, .69, .72) * (opacity / .70), opacity);
}
float4 PSBase(PSInput input) : SV_TARGET { return float4(.008, .021, .033, .10); }
Texture2D<float4> Image : register(t0);
Texture2D<float4> Glow : register(t1);
SamplerState LinearClamp : register(s0);
struct ScreenInput { float4 position : SV_POSITION; float2 uv : TEXCOORD0; };
ScreenInput VSFullscreen(uint id : SV_VertexID)
{
float2 uv = float2((id << 1) & 2, id & 2);
ScreenInput result;
result.position = float4(uv * float2(2, -2) + float2(-1, 1), 0, 1);
result.uv = uv;
return result;
}
float4 NormalAt(int2 pixel)
{
return Image.Load(int3(clamp(pixel, int2(0,0), int2(Detail.xy)-1),0));
}
float4 PSOutline(ScreenInput input) : SV_TARGET
{
int2 p=int2(input.position.xy);int s=max(1,(int)Sobel.y);
float4 centre=NormalAt(p);
if(centre.w<=0)return 0;
float4 tl=NormalAt(p+int2(-s,-s)),tc=NormalAt(p+int2(0,-s)),tr=NormalAt(p+int2(s,-s));
float4 ml=NormalAt(p+int2(-s,0)),mr=NormalAt(p+int2(s,0));
float4 bl=NormalAt(p+int2(-s,s)),bc=NormalAt(p+int2(0,s)),br=NormalAt(p+int2(s,s));
float3 nx=(tr.rgb+2*mr.rgb+br.rgb)-(tl.rgb+2*ml.rgb+bl.rgb);
float3 ny=(bl.rgb+2*bc.rgb+br.rgb)-(tl.rgb+2*tc.rgb+tr.rgb);
float normalEdge=sqrt(dot(nx,nx)+dot(ny,ny))*.25;
float depthX=(tr.w+2*mr.w+br.w)-(tl.w+2*ml.w+bl.w);
float depthY=(bl.w+2*bc.w+br.w)-(tl.w+2*tc.w+tr.w);
float depthEdge=length(float2(depthX,depthY))/max(centre.w,.001)*.25;
float response=saturate(max(normalEdge*.55,depthEdge)*Sobel.x);
float edge=pow(response,Sobel.w);
// The original FGM uses a screen-space scanline texture with this UV scale.
// Keep the phase in display pixels so a small preview has the same fine raster.
float scanline=.68+.32*cos(6.2831853*input.position.y*Scan.y*128);
float3 face=TintLow.rgb*.022*scanline;
return float4(face+TintHigh.rgb*edge*scanline,1);
}
float4 Blur(float2 uv,float2 step)
{
float4 color=Image.SampleLevel(LinearClamp,uv,0)*.227027;
color+=(Image.SampleLevel(LinearClamp,uv+step*1.384615,0)+Image.SampleLevel(LinearClamp,uv-step*1.384615,0))*.316216;
color+=(Image.SampleLevel(LinearClamp,uv+step*3.230769,0)+Image.SampleLevel(LinearClamp,uv-step*3.230769,0))*.070270;
return color;
}
float4 PSBlurHorizontal(ScreenInput input):SV_TARGET{return Blur(input.uv,float2(3/Detail.x,0));}
float4 PSBlurVertical(ScreenInput input):SV_TARGET{return Blur(input.uv,float2(0,3/Detail.y));}
float4 PSComposite(ScreenInput input):SV_TARGET
{
float4 sharp=Image.SampleLevel(LinearClamp,input.uv,0);
float3 bloom=Glow.SampleLevel(LinearClamp,input.uv,0).rgb;
float3 radiance=max(0,sharp.rgb+bloom*.38);
float3 color=pow(1-exp(-radiance*Scan.z),1/2.2);
float alpha=saturate(max(sharp.a*.94,max(color.r,max(color.g,color.b))));
return float4(min(color,alpha),alpha);
}
cbuffer Camera : register(b0)
{
row_major float4x4 World;
row_major float4x4 ViewProjection;
float4 Eye;
float4 HologramColor;
float4 Detail; // viewport width/height, minimum line length, line opacity
float4 Sobel; // original distance scale, edge thickness, max distance, power
float4 TintHigh;
float4 TintLow;
float4 Scan; // original UV scale, display exposure, animation time
};
struct VSInput { float3 position : POSITION; float3 normal : NORMAL; };
struct PSInput { float4 position : SV_POSITION; float3 normal : NORMAL; float3 worldPosition : TEXCOORD0; float lineFade : TEXCOORD1; };
PSInput VSMain(VSInput input)
{
PSInput result;
float4 world = mul(float4(input.position, 1), World);
result.position = mul(world, ViewProjection);
result.worldPosition = world.xyz;
result.normal = normalize(mul(float4(input.normal, 0), World).xyz);
result.lineFade = 1;
return result;
}
float4 PSNormals(PSInput input) : SV_TARGET
{
return float4(normalize(input.normal), length(Eye.xyz - input.worldPosition));
}
PSInput VSLine(VSInput input)
{
PSInput result = VSMain(input);
if (Detail.z > 0)
{
float4 end = mul(mul(float4(input.position + input.normal, 1), World), ViewProjection);
float lengthPixels = length((end.xy / end.w - result.position.xy / result.position.w) * Detail.xy * .5);
result.lineFade = smoothstep(Detail.z, Detail.z + 5, lengthPixels);
}
result.position.z -= .000003 * result.position.w;
return result;
}
float4 PSLine(PSInput input) : SV_TARGET
{
float opacity = Detail.w * input.lineFade;
return float4(HologramColor.rgb * float3(.59, .69, .72) * (opacity / .70), opacity);
}
float4 PSBase(PSInput input) : SV_TARGET { return float4(.008, .021, .033, .10); }
Texture2D<float4> Image : register(t0);
Texture2D<float4> Glow : register(t1);
SamplerState LinearClamp : register(s0);
struct ScreenInput { float4 position : SV_POSITION; float2 uv : TEXCOORD0; };
ScreenInput VSFullscreen(uint id : SV_VertexID)
{
float2 uv = float2((id << 1) & 2, id & 2);
ScreenInput result;
result.position = float4(uv * float2(2, -2) + float2(-1, 1), 0, 1);
result.uv = uv;
return result;
}
float4 NormalAt(int2 pixel)
{
return Image.Load(int3(clamp(pixel, int2(0,0), int2(Detail.xy)-1),0));
}
float4 PSOutline(ScreenInput input) : SV_TARGET
{
int2 p=int2(input.position.xy);int s=max(1,(int)Sobel.y);
float4 centre=NormalAt(p);
if(centre.w<=0)return 0;
float4 tl=NormalAt(p+int2(-s,-s)),tc=NormalAt(p+int2(0,-s)),tr=NormalAt(p+int2(s,-s));
float4 ml=NormalAt(p+int2(-s,0)),mr=NormalAt(p+int2(s,0));
float4 bl=NormalAt(p+int2(-s,s)),bc=NormalAt(p+int2(0,s)),br=NormalAt(p+int2(s,s));
float3 nx=(tr.rgb+2*mr.rgb+br.rgb)-(tl.rgb+2*ml.rgb+bl.rgb);
float3 ny=(bl.rgb+2*bc.rgb+br.rgb)-(tl.rgb+2*tc.rgb+tr.rgb);
float normalEdge=sqrt(dot(nx,nx)+dot(ny,ny))*.25;
float depthX=(tr.w+2*mr.w+br.w)-(tl.w+2*ml.w+bl.w);
float depthY=(bl.w+2*bc.w+br.w)-(tl.w+2*tc.w+tr.w);
float depthEdge=length(float2(depthX,depthY))/max(centre.w,.001)*.25;
float response=saturate(max(normalEdge*.55,depthEdge)*Sobel.x);
float edge=pow(response,Sobel.w);
// The original FGM uses a screen-space scanline texture with this UV scale.
// Keep the phase in display pixels so a small preview has the same fine raster.
float scanline=.68+.32*cos(6.2831853*input.position.y*Scan.y*128);
float3 face=TintLow.rgb*.022*scanline;
return float4(face+TintHigh.rgb*edge*scanline,1);
}
float4 Blur(float2 uv,float2 step)
{
float4 color=Image.SampleLevel(LinearClamp,uv,0)*.227027;
color+=(Image.SampleLevel(LinearClamp,uv+step*1.384615,0)+Image.SampleLevel(LinearClamp,uv-step*1.384615,0))*.316216;
color+=(Image.SampleLevel(LinearClamp,uv+step*3.230769,0)+Image.SampleLevel(LinearClamp,uv-step*3.230769,0))*.070270;
return color;
}
float4 PSBlurHorizontal(ScreenInput input):SV_TARGET{return Blur(input.uv,float2(3/Detail.x,0));}
float4 PSBlurVertical(ScreenInput input):SV_TARGET{return Blur(input.uv,float2(0,3/Detail.y));}
float4 PSComposite(ScreenInput input):SV_TARGET
{
float4 sharp=Image.SampleLevel(LinearClamp,input.uv,0);
float3 bloom=Glow.SampleLevel(LinearClamp,input.uv,0).rgb;
float3 radiance=max(0,sharp.rgb+bloom*.38);
float3 color=pow(1-exp(-radiance*Scan.z),1/2.2);
float alpha=saturate(max(sharp.a*.94,max(color.r,max(color.g,color.b))));
return float4(min(color,alpha),alpha);
}