Raycasting - problem

0

Witam!

Aktualnie męczy mnie program, którego zrobienia podjąłem się 3( [!!!] ) raz... Po raz kolejny nie wyszło.

Służy on do raycastingu (patrz: ray casting), czyli renderowania 3D. Oczywiście jest to medoda niedoskonała - tym sposobem można stworzyć swego rodzaju labirynt ;-) . Gdy za pierwszym razem tworzyłem tę aplikację nie podchodziłem do tego poważnie, ostatnio zająłem się tym dla zabicia czasu, jenak za trzecim razem (czyli teraz) chcę się z tym ostatecznie rozprawić. Prosiłbym Ciebie, wejdź na Google graphic i wpisz "Wolfenstein 3D". Nie chcę tworzyć czegoś takiego (nie, nie... żadnych tekstur :-P ). Mój cel jest taki, by co pewną odległość rysować pionową kreskę, tak długą, jak odległość od punktu w którym znajduje się "kamera" do ściany patrząc pod pewnym kątem.

user image

To program wykonany przez mojego kolegę w programie do nauki algorytmiki Scratch. Niestety, ma swoje wady - przez ograniczenia programu Scrath, obraz renderuje się bardzo wolno. Spytał się mnie, czy umiałbym coś takiego zrobić. Odpowiedziałem, że tak, no i... nie udało się [wstyd] . W każdym razie, zastosował on zasadę, że im dalej, tam bardziej zmienia się kolor. W moim kodzie tego nie ma. Jeżeli pomożecie mi rozwiązać aktualny problem, to to będę Wam niezwykle wdzięczny (kolorki dodam potem [green] ).

user image

To wynik działania mojego programu. Występuje tu jakiś dziwny bug. W każdym razie jest źle, bo nie występuje tu efekt rybiego oka - normalnie proste ściany stają się krzywe (patrz pierwszy rysunek).

user image

A oto mapka.

Oto kod Unit'u 1:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Math;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    procedure Przypisz;
    { Private declarations }
  public
    { Public declarations }
  end;

type
  XYFullRec = record
  X,Y:extended;
  end;

var
  XY: TPoint;
  XYFull: XYFullRec;
  Wysokosc: array[0..90] of integer;
  Sciana: array[1..640,1..480]of boolean;
  Form1: TForm1;
  Mapa: TPicture;

const
  Zasieg: integer=128;

implementation

{$R *.dfm}

procedure TForm1.Przypisz;
begin
XYFull.X:=XY.X;
XYFull.Y:=XY.y;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
I,J: integer;
begin
Form1.BorderStyle:=bssingle;
XY.X:=310;
XY.Y:=355;
Przypisz;
Form1.Top:=0;
Form1.Left:=0;
Form1.ClientHeight:=480;
Form1.ClientWidth:=640;
Mapa:=TPicture.Create;
Mapa.Bitmap.LoadFromFile('C:\delphifiles\mapa.bmp');
for I:=1 to 640 do
for J:=1 to 480 do
if Mapa.Bitmap.Canvas.Pixels[I,J]=clblack then
Sciana[I,J]:=true else Sciana[I,J]:=false;
for I:=0 to 90 do
Wysokosc[I]:=0;
//zabiezpieczenia ramki
for I:=1 to 480 do
for J:=1 to 5 do
Sciana[J,I]:=true;
for I:=1 to 480 do
for J:=635 to 640 do
Sciana[J,I]:=true;
for I:=1 to 640 do
for J:=1 to 5 do
Sciana[I,J]:=true;
for I:=1 to 640 do
for J:=475 to 480 do
Sciana[I,J]:=true;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
Lacznie: XYFullRec;
Aktualnie: integer;
Kat: integer;
I: integer;
begin
begin
Kat:=-45;
Przypisz;
for I:=0 to 90 do
begin
Lacznie.X:=0;
Lacznie.Y:=0;
aktualnie:=1;
Kat:=Kat+1;
repeat
begin
Lacznie.X:=Lacznie.X+Sin(degtorad(Kat))*aktualnie;
Lacznie.Y:=Lacznie.Y-(sqr(aktualnie)-Sin(degtorad(Kat))*aktualnie);
Aktualnie:=Aktualnie+1;
end;
until (Sciana[trunc(XYFull.X+Lacznie.X),trunc(XYFull.Y+Lacznie.Y)]=true) or ((Aktualnie-2)=Zasieg);
Wysokosc[I]:=Zasieg-(Aktualnie-2);
end;
Form1.Canvas.Pen.Width:=1;
Form1.Canvas.Pen.Color:=clgray;
Form1.Canvas.Brush.Color:=clgray;
Form1.Canvas.Rectangle(0,240,640,480);
Form1.Canvas.Pen.Color:=clred;
Form1.Canvas.Pen.Width:=5;
for I:=0 to 90 do
begin
Form1.Canvas.MoveTo(i*5,240-Wysokosc[I]);
Form1.Canvas.LineTo(i*5,240+Wysokosc[I]);
end;
Form1.Canvas.Brush.Color:=clwhite;
Form1.Canvas.Pen.Color:=Form1.Canvas.Brush.Color;
Form1.Canvas.Rectangle(450,0,640,480);
end;
end;

end.

Dodam jeszcze, że:

Form1.Color:=clblack;

Jeżeli ktoś chciałby pomóc mi na osobności, chętnie umówię się na Skypa (ewentualnie GG).
Pliki projektu do wglądu za prośbą.

Za pomoc serdecznie dziękuję. :-)

PS: Tylko nie mówcie "Są lepsze sposoby" lub "A po co w Delphi, lepiej w C++". Ja chcę zrobić to metodą raycastingu i właśnie w Delphi (też dlatego, że object pascal najlepiej znam)

0
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Math;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    procedure Przypisz;
    { Private declarations }
  public
    { Public declarations }
  end;

type
  XYFullRec = record
  X,Y:extended;
  end;

var
  XY: TPoint;
  XYFull: XYFullRec;
  Wysokosc: array[0..90] of integer;
  Sciana: array[1..640,1..480]of boolean;
  Form1: TForm1;
  Mapa: TPicture;

const
  Zasieg: integer = 128;

implementation

{$R *.dfm}

procedure TForm1.Przypisz;
begin
XYFull.X := XY.X;
XYFull.Y := XY.y;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
I,J: integer;
begin
Form1.BorderStyle := bssingle;
XY.X := 310;
XY.Y := 355;
Przypisz;
Form1.Top := 0;
Form1.Left := 0;
Form1.ClientHeight := 480;
Form1.ClientWidth := 640;
Mapa := TPicture.Create;
Mapa.Bitmap.LoadFromFile('C:\delphifiles\mapa.bmp');
  for I := 1 to 640 do
    for J := 1 to 480 do
      if Mapa.Bitmap.Canvas.Pixels[I,J] = clblack then
        Sciana[I,J] := true else Sciana[I,J] := false;
for I := 0 to 90 do
  Wysokosc[I] := 0;
//zabiezpieczenia ramki
for I := 1 to 480 do
  for J := 1 to 5 do
    Sciana[J,I] := true;
for I := 1 to 480 do
  for J := 635 to 640 do
    Sciana[J,I] := true;
for I := 1 to 640 do
  for J := 1 to 5 do
    Sciana[I,J] := true;
for I := 1 to 640 do
  for J := 475 to 480 do
    Sciana[I,J] := true;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  Lacznie: XYFullRec;
  Aktualnie: integer;
  Kat: integer;
  I: integer;
begin
Kat := -45;
Przypisz;
for I := 0 to 90 do
  begin
  Lacznie.X := 0;
  Lacznie.Y := 0;
  aktualnie := 1;
  Kat := Kat + 1;
  repeat
  begin
    Lacznie.X := Lacznie.X + Sin(degtorad(Kat)) * aktualnie;
    Lacznie.Y := Lacznie.Y - (sqr(aktualnie) - Sin(degtorad(Kat)) * aktualnie);
    Aktualnie := Aktualnie + 1;
  end;
  until
    (Sciana[trunc(XYFull.X + Lacznie.X),trunc(XYFull.Y + Lacznie.Y)] = true) or ((Aktualnie - 2) = Zasieg);
  Wysokosc[I] := Zasieg - (Aktualnie - 2);
end;
Form1.Canvas.Pen.Width := 1;
Form1.Canvas.Pen.Color := clgray;
Form1.Canvas.Brush.Color := clgray;
Form1.Canvas.Rectangle(0,240,640,480);
Form1.Canvas.Pen.Color := clred;
Form1.Canvas.Pen.Width := 5;
for I := 0 to 90 do
begin
  Form1.Canvas.MoveTo(i * 5,240 - Wysokosc[I]);
  Form1.Canvas.LineTo(i * 5,240 + Wysokosc[I]);
end;
Form1.Canvas.Brush.Color := clwhite;
Form1.Canvas.Pen.Color := Form1.Canvas.Brush.Color;
Form1.Canvas.Rectangle(450,0,640,480);
end;

end.

Owszem, zapomniałem, ale na szczęście nie było tego dużo... ;-)

0

witam,
a może zaimportujesz sobie formatter'a, sam używam dla D7 i D5
naprawdę SUPER

pozdrawiam

EDIT:
nic nie ma ale zwiększa przejrzystość (to tylko wtrącenie) ;-)
natomiast co do tematu postu się nie wypowiadam bo nie znam tematu

0

Domyślam się, że chodzi o program do formatowania kodu... (co ma piernik do wiatraka [glowa] ) Ale nie o to mi chodzi... :-P

Fajnie by było, gdyby ktoś po prostu skusiłby się na sprawdzenie kodu i poprawienie ewentualnego błędu.

Dziękuję


Zapraszam tam
To jest mniej więcej to, co chcę zrobić (przy czym mój labirynt to bitmapa, a tam występują pojedyńcze pomieszczenia)

0

chlopcze a czytales moj artykul o kolizjach 3D w dziale delphi i co nie ma moze tam takiej funkcji jak IntersectedPolygon co? he??? zatkalo teraz to co da sie to zrobic co nie?

Gry 3D, Kolizja Sferyczna

ale i tak nie rozumiem twojego belkotu co ty chcesz osiagnac

0

Trzy kropki: link jest dobry, ale powstrzymaj trochę swój niewyparzony język.

0

Dzięki za linka. Jednak zanim wystawisz posta proponuję, żebyś przeczytał dokładnie wszystko jeszcze raz. Jeżeli czegoś nie rozumiesz, to po prostu poproś o dokładniejsze wytłumaczenia...

W tym artykule napisane jest, cytuję:

(...)czyli zaprogramować sferę odbijającą się od świata 3D w poprawny sposób(...)
Czy oznacza to, że moja koncepcja jest niepoprawna? Po powierzchownym przejrzeniu artukułu muszę stwierdzić, że nie o to mi chodzi. Jest kwestia odróżnienia raycastingu (prostrza wersja, nie używa osi Y, użyta w projekcie) od raytraceingu (normale renderowanie 3D). Radzę wpisać do wikipedii raytracing i raycasting.

Wiem, że wydaje się to banalną sprawą, lecz jeśli ktoś posiedzi nad tym kodem i pomyśli zwłaszcza nad procedurą OnPaint formularza. Chodzi mi zwłaszcza o część kodu do:

until (Sciana[trunc(XYFull.X+Lacznie.X),trunc(XYFull.Y+Lacznie.Y)]=true) or ((Aktualnie-2)=Zasieg);
Wysokosc[I]:=Zasieg-(Aktualnie-2);
end;

, reszta kodu chyba nie stanowi problemu (nie tworzy błędu).

user image

Niebieskie linie to przemieszczający się punkt o zmiennoprzecinkowych współrzędnych, wyliczanych dzięki funkcjom trygonometrycznym (zmiennoprzecinkowe dlatego gdyż np.:gdyby zaokrąglić 0.5 powstałoby 1, a to już duża różnica). Przemieszcza się on tak długo o 1, aż jego współrzędne po zaokrągleniu i ich wstawienu (tablica[X,Y])do tablicy (640X480, boolean) będą oznaczały scianę (TRUE). Gdy tego dokona zapisuje do tablicy integerów [0..90](tak, tak: 91 pozycji, nie pomyliłem się) odległość miedzy kamerą, a scianą pod tym kątem.Po tej operacji zmienia kąt o 1 i wykonuje to samo kolejnych 90 razy.

Sorry za taki trochę niezrozumiały język.

user image

Ta medoda nie jest dokładna, tworzy się efekt rybiego oka - brzeg sciany jest lekko zakrzywiony względem rzeczywistego przebiegu (tu czarna linia). Dlatego jestem pewien, że program nie działa, bo taki efekt nie powstaje.

<font size="2">Jeżeli ktoś ma pytania, to niech śmiało pisze...</span>

0

Dobra, sprawa zamknięta. Stworzylem ten program na innej zasadzie (bez funkcji trygonometrycznych) i działa :-) ! Pomoc już mi nie potrzebna

1 użytkowników online, w tym zalogowanych: 0, gości: 1