Hier ist ein gutes Beispiel, wie man einen Bumpmap Effekt auf ein Bitmap rechnen lassen kann.
Um bei der Kompilierung nicht einen Crash zu verursachen, solltet ihr entweder vorher ein Bitmap in das Image laden oder den Timer deaktivieren und beim Laden des Bitmaps wieder aktivieren.
Der Effekt ist sehr schön und kann sehr gut modifiziert werden.
Der Timer sollte auf 39 fps gestellt werden.
unit Bumpmapping;
interface
uses Windows, Graphics;
procedure Bump_Init(SourceBitMap: TBitmap; r: Single = 3; g: Single = 3.6;
b: Single = 4);
procedure Bump_Flush();
procedure Bump_Do(Target: TBitmap; XLight, YLight: Integer);
procedure Bump_SetSource(SourceBitMap: TBitmap);
procedure Bump_SetColor(r, g, b: Single);
implementation
type
PBitmap = ^TBitmap;
TLine = array[0..MaxInt div SizeOf(TRGBQUAD) - 1] of TRGBQUAD;
PLine = ^TLine;
var
ColorArray: array of TRGBQuad; //Array für die Farbtabelle beim Bumpmapping
SourceArray: array of Byte; //Quell Muster
TargetBMP: TBitmap; //ZielBitmap
Black: TRGBQuad; //Schwart
White: TRGBQuad; //Weiß
procedure Bump_SetSource(SourceBitMap: TBitmap);
var
iX, iY: Integer;
z: Integer;
sLine: PLine;
iDot: Integer;
begin
//QuellArray erzeugen
SourceBitmap.PixelFormat := pf32Bit;
SetLength(SourceArray, SourceBitMap.Height * SourceBitMap.Width);
for iY := 0 to SourceBitMap.Height - 1 do
begin
//Scanline holen
sLine := SourceBitMap.ScanLine[iY];
for iX := 0 to SourceBitMap.Width - 1 do
begin
//Koordinaten errechnene
z := iY * SourceBitMap.Width + iX;
//Grauwert bestimmen
idot := sLine[iX].rgbRed;
idot := idot + sLine[iX].rgbGreen;
idot := idot + sLine[iX].rgbBlue;
iDot := (iDot div 3);
//Und eintragen
SourceArray[z] := iDot;
end;
end;
end;
procedure Bump_SetColor(r, g, b: Single);
var
iIndex: Integer;
c: Byte;
begin
if (r > 4) then r := 4;
if (r < 0) then r := 0;
if (g > 4) then g := 4;
if (g < 0) then g := 0;
if (b > 4) then b := 4;
if (b < 0) then b := 0;
SetLength(ColorArray, 255);
FillMemory(ColorArray, 255 * SizeOf(TRGBQuad), 0);
for iIndex := 0 to 127 do
begin
c := 63 - iIndex div 2;
ColorArray[iIndex].rgbRed := round(c * r);
ColorArray[iIndex].rgbGreen := round(c * g);
ColorArray[iIndex].rgbBlue := round(c * b);
end;
Black.rgbRed := 0;
Black.rgbBlue := 0;
Black.rgbGreen := 0;
White.rgbRed := 255;
White.rgbBlue := 255;
White.rgbGreen := 255;
end;
procedure Bump_Do(Target: TBitmap; XLight, YLight: Integer);
var
iX, iY: Integer;
sLine: PLine;
iR1, iT1: Integer;
iR, iT: Integer;
z: Integer;
begin
//Alle Zeile (bis auf oben und unten)
for iY := 1 to TargetBMP.Height - 2 do
begin
//Scanline holen
sLine := TargetBMP.ScanLine[iY];
//Startposition im Quell-Array
z := iY * TargetBMP.Width;
//Vorberechnung zur Beleuchtung
iT1 := (iY - YLight);
//Und alle Pixel durchwursten
for iX := 1 to TargetBMP.Width - 2 do
begin
//Position im Array aktualisieren
Inc(z);
//Steigung in unserem Punkt bestimmen
iT := iT1 - (SourceArray[z + TargetBMP.Width] -
SourceArray[z - TargetBMP.Width]);
iR := (iX - XLight) - (SourceArray[z + 1] - SourceArray[z - 1]);
//Absolut machen
if (iR < 0) then iR := -iR;
if (iT < 0) then iT := -iT;
//Wie sieht die Steigung aus ?
iR1 := iR + iT;
if (iR1 < 129) then
begin
//Hohe steigung, Farbe holen
sLine[iX] := ColorArray[iR1];
end
else
begin
//Ansonsten schwarz
sLine[iX] := Black;
end;
end;
end;
//Ergebnis übergeben
Target.Assign(TargetBMP);
end;
procedure Bump_Init(SourceBitMap: TBitmap; r: Single = 3; g: Single = 3.6;
b: Single = 4);
begin
//Zielbitmap erzeugen
TargetBMP := TBitmap.Create;
with TargetBMP do
begin
Height := SourceBitMap.Height;
Width := SourceBitMap.Width;
PixelFormat := pf32Bit;
end;
//Farbtabellen initialisieren
Bump_SetColor(r, g, b);
//Und aus dem Quellbitmap ein Array machen
Bump_SetSource(SourceBitmap);
end;
procedure Bump_Flush();
begin
//Speicher freimachen
TargetBMP.Free;
SetLength(ColorArray, 0);
end;
end.
Unit1 :
uses Bumpmapping
var
Form1: TForm1;
XPos: Single;
YPos: Single;
//
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Enabled := false;
DoubleBuffered := true;
Bump_Init(Image1.Picture.Bitmap, 2,3,4);
end;
// Der Timer muss deaktiviert werden und beim laden wieder aktiviert werden 39 (fps)
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Interval:=40;
//Image1.Stretch:=TRUE !!!!
//Position des Lichtpunktes ändern
XPos := XPos + 0.02;
YPos := YPos + 0.01;
//Auf 2Pi begrenzen
if (XPos > 2 * PI) then XPos := XPos - 2 * PI;
if (YPos > 2 * PI) then YPos := YPos - 2 * PI;
//Und ausgeben
with Image1.Picture do
Bump_Do(Bitmap,
trunc(Sin(XPos) * (Bitmap.Width shr 1) + (Bitmap.Width shr 1)),
trunc(Sin(YPos) * (Bitmap.Height shr 1) + (Bitmap.Height shr 1)))
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Bump_Flush();
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then begin
try
Timer1.Enabled := true;
Image1.Picture.Bitmap.LoadFromFile(OpenDialog1.FileName);
Bump_Init(Image1.Picture.Bitmap, 2,3,4);
finally
end;
end;
end;
Keine Kommentare:
Kommentar veröffentlichen