Einzelnen Beitrag anzeigen

CHackbart

Registriert seit: 22. Okt 2012
260 Beiträge
 
#1

Eigener Firemonkey TEffect Shader

  Alt 23. Mai 2018, 10:53
Hallo,

ich versuche gerade für ein Open Source Projekt einen Effektfilter zu schreiben und Delphi schmeisst da ein paar kleinere Steine in den Weg. Das kleinere Problem ist, die Anzeige unter Windows zu Testzwecken auf DX9 zu stellen. Dafür musste ich in FMX.Context.DX11 die RegisterContext Funktion deaktivieren. Da es nur zum Testen des Shaders ist, ist das okay. Leider gibt es keinen anderen Weg das zu realisieren, denn GlobalUseDXInDX9Mode ist dafür nicht angedacht.

Mit der Hilfe von Zudomon habe ich gestern einen Pixelshader für ps_2_a (DX9) erstellt.

Code:
sampler s0 : register(s0);
float4 p0 : register(c0);
float4 p1 : register(c1);

#define screen_size float2(p0[0],p0[1])
#define hardScan float(p0[2])
#define hardPix float(p0[3])
#define warp float2(p1[0], p1[1])

// Amount of shadow mask.
#define maskDark float (p1[2])
#define maskLight float (p1[3])

// sRGB to Linear.
// Assuing using sRGB typed textures this should not be needed.
float ToLinear1(float c){return(c<=0.04045)?c/12.92:pow(abs((c+0.055)/1.055),2.4);}
float3 ToLinear(float3 c){return float3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));}

// Linear to sRGB.
// Assuing using sRGB typed textures this should not be needed.
float ToSrgb1(float c){return(c<0.0031308?c*12.92:1.055*pow(abs(c),0.41666)-0.055);}
float3 ToSrgb(float3 c){return float3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));}

// Nearest emulated sample given floating point position and texel offset.
// Also zero's off screen.
float3 Fetch(float2 pos,float2 off){
  //pos=(floor(pos*screen_size+off)+float2(0.5,0.5))/screen_size;
  if (max(abs(pos.x-0.5), abs(pos.y-0.5)) > 0.5) { return float3(0.0, 0.0, 0.0);}
  return ToLinear(1.2 * tex2D(s0,pos).rgb);}

// Distance in emulated pixels to nearest texel.
float2 Dist(float2 pos){return -((pos-floor(pos))-float2(0.5, 0.5));}
// 1D Gaussian.
float Gaus(float pos,float scale){return exp2(scale*pos*pos);}

// 3-tap Gaussian filter along horz line.
// 3-tap Gaussian filter along horz line.
float3 Horz3(float2 pos,float off){
  float3 b=Fetch(pos,float2(-1.0,off));
  float3 c=Fetch(pos,float2( 0.0,off));
  float3 d=Fetch(pos,float2( 1.0,off));
  float dst=Dist(pos).x;
  // Convert distance to weight.
  float scale=hardPix;
  float wb=Gaus(dst-1.0,scale);
  float wc=Gaus(dst+0.0,scale);
  float wd=Gaus(dst+1.0,scale);
  // Return filtered sample.
  return (b*wb+c*wc+d*wd)/(wb+wc+wd);}

// 5-tap Gaussian filter along horz line.
float3 Horz5(float2 pos,float off){
  float3 a=Fetch(pos,float2(-2.0,off));
  float3 b=Fetch(pos,float2(-1.0,off));
  float3 c=Fetch(pos,float2( 0.0,off));
  float3 d=Fetch(pos,float2( 1.0,off));
  float3 e=Fetch(pos,float2( 2.0,off));
  float dst=Dist(pos).x;
  // Convert distance to weight.
  float scale=hardPix;
  float wa=Gaus(dst-2.0,scale);
  float wb=Gaus(dst-1.0,scale);
  float wc=Gaus(dst+0.0,scale);
  float wd=Gaus(dst+1.0,scale);
  float we=Gaus(dst+2.0,scale);
  // Return filtered sample.
  return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);}


// Return scanline weight.
float Scan(float2 pos,float off){
  float dst=Dist(pos).y;
  return Gaus(dst+off,hardScan);}


// Allow nearest three lines to effect pixel.
float3 Tri(float2 pos){
  float3 a=Horz3(pos,-1.0);
  float3 b=Horz5(pos, 0.0);
  float3 c=Horz3(pos, 1.0);
  float wa=Scan(pos,-1.0);
  float wb=Scan(pos, 0.0);
  float wc=Scan(pos, 1.0);
  return a*wa+b*wb+c*wc;}


// Distortion of scanlines, and end of screen alpha.
float2 Warp(float2 pos){
  pos=pos*2.0-1.0;
  pos*=float2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y);
  return pos*0.5+0.5;}

// Shadow mask.
float3 Mask(float2 pos){
  pos.x+=pos.y*3.0;
  float3 mask=float3(maskDark,maskDark,maskDark);
  pos.x=frac(pos.x/6.0);
  if(pos.x<0.333)mask.r=maskLight;
  else if(pos.x<0.666)mask.g=maskLight;
  else mask.b=maskLight;
  return mask;
}

float4 main(float2 tex : TEXCOORD0) : COLOR
{
   float2 pos = tex*screen_size;
   tex = Warp(tex);
   float4 color;
   
   color.rgb = ToSrgb(Tri(tex)*Mask(pos));
   return float4(color.r, color.g, color.b, 1);
}
Dieser basiert auf Lottes GLSL CRT Pixelfilter und die Idee ist dabei eine CRT Anzeige zu simulieren. Das ganze funktioniert zumindest mit DX9 recht gut. Ich kann die Werte für den Shader übergeben und er stellt das ganze auch (siehe Anhang) korrektet dar.

Delphi-Quellcode:
procedure TCrtFilter.LoadShaders;
begin
  TFilterManager.FilterContext.SetShaders(FVertexShader, FShaders[FPass]);
  TFilterManager.FilterContext.SetShaderVariable('C0',
    [Vector3D(InputSize.Width, InputSize.Height, ValuesAsFloat['HardScan'],
    ValuesAsFloat['HardPix'])]);

  TFilterManager.FilterContext.SetShaderVariable('C1',
    [Vector3D(ValuesAsFloat['WarpX'], ValuesAsFloat['WarpY'],
  ValuesAsFloat['MaskDark'], ValuesAsFloat['MaskLight'])]);
end;

class function TCrtFilter.FilterAttr: TFilterRec;
begin
  Result := TFilterRec.Create('CrtFilter',
    'An effect that simulates a CRT Monitor.',
    [TFilterValueRec.Create('HardScan', 'Hardness of scanline.',
    TFilterValueType.Float, -12, -8, -16), TFilterValueRec.Create('HardPix',
    'Hardness of pixels in scanline.', TFilterValueType.Float, -3, -2, -4),
    TFilterValueRec.Create('WarpX', 'Display Warp horizontal.',
    TFilterValueType.Float, 1.0 / 40.0, 0, -1 / 8),
    TFilterValueRec.Create('WarpY', 'Display Warp vertical.',
    TFilterValueType.Float, 1.0 / 44.0, 0, -1 / 8),
    TFilterValueRec.Create('MaskDark', 'Amount of minimum darkness shadow.',
    TFilterValueType.Float, 0.5, -10, 10), TFilterValueRec.Create('MaskLight',
    'Amount of minimum lightness shadow.', TFilterValueType.Float, 1.5,
    -10, 10)]);
end;
Das Problem welches ich jetzt habe ist den DX9 Shader auf DX11 zu portieren. Hat das jemand schon einmal gemacht?
Miniaturansicht angehängter Grafiken
screenshot-2018-05-23-um-11.45.32.jpg  
  Mit Zitat antworten Zitat