Einzelnen Beitrag anzeigen

Benutzerbild von milos
milos

Registriert seit: 14. Jul 2008
Ort: Bern (CH)
508 Beiträge
 
Delphi 11 Alexandria
 
#1

Z-Buffering (Mal wieder ^^)

  Alt 24. Apr 2016, 02:32
Hallo,

ich habe ja vor einigen Monaten nach Hilfe gefragt um Backface-Culling und Z-Buffering in mein Isometrie-Zeichenprogramm einzubinden.
Leider hatte ich aufgrund von Zwischenfällen die mir einige Zeit und Nerv geraubt hat keine Zeit mich weiter um das Programm zu kümmern und konnte in der Zeit nur Backface-Culling implementieren und habe das Z-Buffering stehen gelassen.

Vor einigen Tagen kam mir jedoch eine Idee wie ich das (theoretisch) einfach lösen kann und momentan sieht es schon ziemlich gut aus, habe jedoch einen kleinen Fehler drinnen.
Und zwar probiere ich es gerade so, dass ich jedes Polygon einzeln auf ein Bitmap zeichnen lasse in der Position die es später auch haben soll. (Dazu sind noch Bilder angefügt, da Bilder bekannterweise mehr als 1000 Worte sagen )

Nun habe ich pro Polygon ein Bitmap bei der ich dann Pixel für Pixel nach gezeichneten Stellen suche und, falls ein gezeichneter Pixel vorhanden ist, dessen Position im 3D-Raum zurückrechne um dann berechnen zu können wie "nah" der Pixel von der Kamera ist. Am Schluss kombiniere ich einfach alle Bitmaps zu einem und lasse anhand der Pixeltiefe entscheiden ob es zu sehen ist oder nicht. Ich glaube genau da liegt der Fehle, beim bestimmen welche Tiefe der Pixel hat. (Im Bild im Anhang kann man den Fehler gut erkennen wenn man reinscrollt und beide Ergebnisse mal ein vergleicht. Beim vom Programm erzeugten sieht verläuft zum beispiel die Linie vom 2. Polygon nicht parallel zu den anderen vom 1. Polygon, das kann ja schon mal nicht stimmen )


Um die Koordinate wieder zurück zu rechnen benutze ich folgende Methode: (Das ganze Projekt ist auch im Anhang)
Delphi-Quellcode:
TPolygon3D = array of TVector3D;

function CalculateCoordinate(APolygon3D : TPolygon3D; APoint : TPointF) : TVector3D;
var
  APolygon : TPolygon;
  LLength,c : integer;
  LDistance, LMaxDistance : double;
  LPercent : double;

  LX,LY,LZ : double;
begin
  APolygon := Polygon3Dto2D(APolygon3D);
  LLength := Length(APolygon);


  LMaxDistance := 0;
  
  for c := 0 to LLength-1 do
  begin
    LDistance := APoint.Distance(APolygon[c]);
    LMaxDistance := LMaxDistance + LDistance;
  end;

  LX := 0;
  LY := 0;
  LZ := 0;
  
  for c := 0 to LLength-1 do
  begin
    LDistance := APoint.Distance(APolygon[c]);
    LPercent := (1 / LMaxDistance) * LDistance;
    LX := LX + (APolygon3D[c].X * LPercent);
    LY := LY + (APolygon3D[c].Y * LPercent);
    LZ := LZ + (APolygon3D[c].Z * LPercent);
  end;

  Result := Vector3D(LX,LY,LZ);
end;
um einen 3D Isometrischen Punkt ins 2D zu berechnen das hier:
Delphi-Quellcode:
function Polar2D(AX,AY,AAngle,ADistance : Double) : TPointF;
begin
  Result.x := (Cos(DegToRad(AAngle)) * ADistance) + AX;
  Result.y := (Sin(DegToRad(AAngle)) * ADistance) + AY;
end;

function IsoToScreen(AVector : TVector3D) : TPointF;
var
  LPoint : TPointF;
begin
  LPoint := PointF(0,0);

  LPoint := Polar2D(LPoint.X, LPoint.Y, 30, AVector.X);
  LPoint := Polar2D(LPoint.X, LPoint.Y, 150, AVector.Y);
  LPoint := Polar2D(LPoint.X, LPoint.Y, 270, AVector.Z);

  Result := LPoint;
end;
Das erstellen der "PixelMatrix"
Delphi-Quellcode:
procedure TPixelBuffer.CreateMatrix(APolygon: TPolygon3D; AFillColor : TAlphaColor);
var
  LBitmap : TBitmap;
  LData : TBitmapData;
  x: Integer;
  y: Integer;
  LX,LY : integer;
  LRect : TRect;
  LCam : TVector3d;
begin
  LBitmap := TBitmap.Create(500,500);
  LBitmap.Canvas.BeginScene();
  LBitmap.Canvas.Fill.Color := AFillColor;
  LBitmap.Canvas.FillPolygon(Polygon3dTo2d(APolygon),1);
  LBitmap.Canvas.EndScene;

  LBitmap.SaveToFile(Random(10).ToString + '.png');

  LBitmap.Map(TMapAccess.Read,LData);
  LRect := PolygonRect(Polygon3Dto2D(APolygon));

  for x := LRect.Left to LRect.Right do
  begin
    for y := LRect.Top to LRect.Bottom do
    begin
      if LData.GetPixel(X,Y) <> $00000000 then
      begin
        LX := X;
        LY := Y;
        Pixels[LX,LY].Color := LData.GetPixel(LX,LY);
        Pixels[LX,LY].Vector := CalculateCoordinate(APolygon,PointF(LX,LY));
        LCam := Vector3D(Pixels[LX,LY].Vector.X-Pixels[LX,LY].Vector.Z,Pixels[LX,LY].Vector.Y-Pixels[LX,LY].Vector.Z,0);
        Pixels[LX,LY].Depth := LCam.Distance(Pixels[LX,LY].Vector); // Hier wird die Tiefe berechnet
      end;
    end;
  end;

  LBitmap.Free;
end;
Und das Rendern:
Delphi-Quellcode:
procedure TRenderer.Render(APolygons: array of TPolygon3D);
var


  c: Integer;
  LBuffer : TPixelBuffer;

  Colors : array of TAlphaColor;

  LBitmap : TBitmap;
  LData : TBitmapData;
  x: Integer;
  y: Integer;

begin
  Colors := [$FFFF0000,$FFFF00FF,$FF00FF00,$FF0000FF];

  for c := 0 to Length(Apolygons)-1 do
  begin
    LBuffer := TPixelBuffer.Create(500,500);
    LBuffer.CreateMatrix(APolygons[c],Colors[c]);
    Buffers.Add(LBuffer);
  end;

  LBitmap := TBitmap.Create(500,500);
  LBitmap.Map(TMapAccess.Write, LData);

  LBuffer := TPixelBuffer.Create(500,500);
  for x := 0 to 499 do // Hier wird noch mal die finale Matrix erstellt
    begin
      for y := 0 to 499 do
      begin
        for c := 0 to Buffers.Count-1 do
        begin
          if (Buffers[c].Pixels[x,y].Depth < Lbuffer.Pixels[x,y].Depth) then // Überprüfung ob der Pixel gezeichnet werden soll oder ob er überdeckt ist
          begin
              Lbuffer.Pixels[x,y].Depth := Buffers[c].Pixels[x,y].Depth;
              Lbuffer.Pixels[x,y].Height := Buffers[c].Pixels[x,y].Height;
              LBuffer.Pixels[x,y].Color := Buffers[c].Pixels[x,y].Color;
          end;
        end;
      end;
    end;

  for x := 0 to 499 do
  begin
      for y := 0 to 499 do
      begin
        LData.SetPixel(X,Y,LBuffer.Pixels[X,Y].Color);
      end;
  end;

  LBitmap.SaveToFile('./test.png');
end;
Im anhang sieht man das es auch schon fast funktioniert, jedoch glaube ich das die Tiefenberechnung fehlerhaft ist oder das vielleicht die CalculateCoordinate auch nicht wirklich gut gelöst ist :/
Ich hoffe ich habe mich so ausgedrückt das man mein Anliegen gut versteht, ist schon ziemlich spät und muss nun schlafen sonst raubt mir Delphi wieder mal die ganze Nacht... und den Tag darauf sowieso!

Edit: Ach ja der code wurde auf die schnelle geschrieben als "proof of concept", im Zeichenprogramm hat der nichts zu suchen

Freundliche Grüsse
Miniaturansicht angehängter Grafiken
zbuffer.png  
Angehängte Dateien
Dateityp: zip zbuffer.zip (56,1 KB, 5x aufgerufen)
Milos

Geändert von milos (24. Apr 2016 um 02:36 Uhr)
  Mit Zitat antworten Zitat