// SPDX-License-Identifier: LGPL-3.0-linking-exception
unit BGRABilateralFilter;
{$mode objfpc}{$H+}
interface
uses
BGRAOpenGL3D, BGRABitmapTypes, BGRACanvasGL, BGRAOpenGLType;
type
TBGLBilateralFilter = class(TBGLShader3D)
private
function GetCanvasSize: TPointF;
procedure SetCanvasSize(AValue: TPointF);
protected
// time
FTime: TUniformVariableSingle;
FTimer : Single;
// intensity
Fintensity : TUniformVariableSingle;
FintensityV : Single;
// Color mode
FMode : TUniformVariableInteger;
FModeV : Integer;
FImage0: TUniformVariableInteger;
FImage0V : Integer;
FImage1: TUniformVariableInteger;
FImage1V : Integer;
FCanvasSize: TUniformVariablePointF;
procedure StartUse; override;
public
constructor Create(ACanvas: TBGLCustomCanvas);
function Render(ATexture,BTexture: IBGLTexture): IBGLTexture; overload;
procedure RenderOnCanvas;
// propriete ecriture des uniforms
property Time: Single read FTimer write FTimer;
property Intensity : Single read FintensityV write FintensityV;
property Cmode : integer read FModeV write FModeV ;
property Image0: integer read FImage0V write FImage0V;
property Image1: integer read FImage1V write FImage1V;
property CanvasSize: TPointF read GetCanvasSize write SetCanvasSize;
end;
var s_sin : single;
implementation
function TBGLBilateralFilter.GetCanvasSize: TPointF;
begin
result := FCanvasSize.Value;
end;
procedure TBGLBilateralFilter.SetCanvasSize(AValue: TPointF);
begin
FCanvasSize.Value := AValue;
end;
{ TBGLBilateralFilter }
constructor TBGLBilateralFilter.Create(ACanvas: TBGLCustomCanvas);
begin
// vertex
inherited Create(ACanvas,
'uniform vec2 canvasSize;'#10 +
'void main(void) {'#10 +
' gl_Position = gl_ProjectionMatrix * gl_Vertex ;'#10 +
' // gl_FrontColor = gl_Color;'#10 +
' //texCoord = gl_Vertex.xy / canvasSize;'#10 +
' texCoord = vec2(gl_MultiTexCoord0) ;'#10 +
'}',
// fragment shader
'uniform float pixelWidth; '#10+
'uniform float pixelHeight; '#10+
'vec2 iResolution = vec2( 1./pixelWidth, 1./pixelHeight); '#10+
'//out vec4 FragmentColor;'#10 +
'uniform float time;'#10+
'uniform sampler2D img0;'#10+
'uniform sampler2D img1;'#10+
'uniform float intensity; '#10+
'uniform int c_mode; '#10+
// Flag fx Gigatron
'void main()'#10 +
'{'#10 +
'vec2 uv = texCoord.xy ; '#10+
// flip y
' uv.y = 1.-uv.y; '#10+
// resolution down
'//uv = floor(256.0*uv)/256.0; '#10+
// texture xy_position
//' vec2 xy_pos = vec2(0.09,0.25);'#10+
'float radius = 0.3 ; '#10+
'float zoom = 0.15 ; '#10+
' vec2 offset = vec2(0.5 * cos(time * 0.5), 0.50 * sin(time))*0.5, '#10+
' center = uv - vec2(0.50+offset.x,0.5+offset.y) ; '#10+
' center.x *= 512.0 / 512.0+0.33; '#10+
' '#10+
' float dist = length(center); '#10+
' if (dist > radius) { '#10+
' gl_FragColor = texture2D(img0, uv); '#10+
' return; '#10+
' } '#10+
' //input '#10+
' vec2 m = vec2(0.5,0.25); '#10+
' //light '#10+
' vec3 lightLoc = vec3(m.x, m.y, radius), '#10+
' lightLocDelta = normalize(vec3(offset - center,0.) - lightLoc), '#10+
' lightDir = vec3(lightLocDelta.xy, -sqrt(1. - pow(lightLocDelta.x,2.) - pow(lightLocDelta.y,2.))),'#10+
' normal = normalize(vec3(center, sqrt(pow(radius,2.) - pow(center.x,2.) - pow(center.y,2.)))), '#10+
' bounce = reflect(lightDir, normal); '#10+
' //diffuse light '#10+
' float brightness = clamp(bounce.z, 0., 1.); '#10+
' //dark effect '#10+
' bounce = reflect(vec3(lightDir.xy, -lightDir.z), normal); '#10+
' float brightness2 = 1.7 - clamp(pow(bounce.z, 3.), 0., 1.); '#10+
' gl_FragColor = texture2D(img0, normal.xy * (zoom/normal.z) + vec2(0.50+offset.x, 0.5+offset.y)) '#10+
' * vec4(clamp(abs(normal), 0.5, 1.), 1.) '#10+
' //falloff brightness with distance '#10+
' * clamp(brightness2 - (0.05/normal.z), 0., 1.) '#10+
' //add specular '#10+
' + vec4(pow(brightness, 10.)); '#10+
//' gl_FragColor = vec4(tx1*tx0, 1.0); '#10+
'}',
'varying vec2 texCoord;', '130');
FTime := UniformSingle['time']; // float uniform
Fintensity := UniformSingle['intensity'];
FMode := UniformInteger['c_mode'];
FImage0 := UniformInteger['img0'];
FImage1 := UniformInteger['img1'];
FCanvasSize := UniformPointF['canvasSize'];
FImage0V := 0;
FImage1V := 1;
FTimer := 0;
FintensityV := 0.1;
FModeV := 0;
end;
procedure TBGLBilateralFilter.StartUse;
begin
inherited StartUse;
FTime.Update;
FTime.Value:=FTimer;
FImage0.Update;
FImage0.Value:=FImage0V;
FImage1.Update;
FImage1.Value:=FImage1V;
FIntensity.Update;
Fintensity.Value:= FintensityV;
FMode.Update;
FMode.Value:= FModeV;
end;
function TBGLBilateralFilter.Render(ATexture,BTexture: IBGLTexture): IBGLTexture;
var previousBuf,buf: TBGLCustomFrameBuffer;
previousShader: TBGLCustomShader;
begin
previousBuf := Canvas.ActiveFrameBuffer;
buf := Canvas.CreateFrameBuffer(ATexture.Width, ATexture.Height);
Canvas.ActiveFrameBuffer := buf;
// in case comment the two next line, if you not want to see black rectangle !!
//-----------------------------------------------------------------
Canvas.Fill(BGRAPixelTransparent);
Canvas.FillRect(0, 0, ATexture.Width, ATexture.Height, CSSBlack);
//-----------------------------------------------------------------
previousShader := Canvas.Lighting.ActiveShader;
Canvas.Lighting.ActiveShader := self;
BTexture.Bind(1);
ATexture.Draw(0, 0);
BTexture.Draw(0+64*sin(s_sin), -420);
Canvas.Lighting.ActiveShader := previousShader;
Canvas.ActiveFrameBuffer := previousBuf;
result := buf.MakeTextureAndFree;
// canvas renderer
//previousBuf := Canvas.ActiveFrameBuffer;
//buf := Canvas.CreateFrameBuffer(ATexture.Width, ATexture.Height);
//Canvas.ActiveFrameBuffer := buf;
//Canvas.Fill(BGRAPixelTransparent);
//RenderOnCanvas;
//Canvas.ActiveFrameBuffer := previousBuf;
//result := buf.MakeTextureAndFree;
s_sin := s_sin +0.02;
end;
procedure TBGLBilateralFilter.RenderOnCanvas;
var
previousShader: TBGLCustomShader;
begin
previousShader := Canvas.Lighting.ActiveShader;
Canvas.Lighting.ActiveShader := self;
CanvasSize := PointF(800,600);
Canvas.FillRect(0, 0, 800,600, CSSBlack);
Canvas.Lighting.ActiveShader := previousShader;
end;
end.