sobota, 18 września 2010

[WPF 3D] Malujemy scenę 3D pędzlem [PL]

W poprzedniej części serii poświęconej tematyce WPF 3D, opisane zostały rodzaje materiałów, jakimi można pokrywać figury i bryły w tworzonej scenie 3D, pominięte ciszą zostały natomiast pędzle wykorzystywane do przygotowania odpowiedniego materiału. W tym wpisie wiadomości dotyczące pokrycia figur zostaną uzupełnione i omówione zostaną pędzle, które są elementem wykorzystanego materiału.
Niniejszy wpis poza prezentacją różnych i niezależnych od konkretnego wykorzystania treści stanowi również opis pewnych zagadnień związanych z projektem Mesh Diagram 3D.Informacje dotyczące projektu oznaczone są etykietą MeshDiagram3D.
Wykorzystanie pędzla jest konieczne do przygotowania materiału dla wybranej figury lub bryły w scenie. W odróżnieniu jednak od opisywanych wcześniej elementów i wykorzystywanych klas wchodzących w skład WPF 3D (czyli przestrzeni nazw: System.Windows.Media.Media3D), pędzle są obiektami klas wchodzących w skład pakietu WPF do wyświetlania „zwykłej”, dwuwymiarowej grafiki. W związku z tym, dla tych, którzy już znają pochodne klasy System.Windows.Media.Brush nie będą one niczym nowym, natomiast dla tych, którzy jeszcze ich nie znają, polecam przeczytanie dokumentacji dostępnej pod adresem: http://msdn.microsoft.com/en-us/library/aa970904.aspx.
W tym wpisie wskażę jedynie pewne efekty, jakie można uzyskać poprzez wykorzystanie pędzli.
Zacznijmy od najprostszego ....

SolidColorBrush

SolidColorBrush jest wyjątkowo prostym pędzlem i pozwala na pomalowanie bryły lub figury pewnym, wybranym kolorem. Zobaczmy prosty przykład, wykorzystując:
Material material = new DiffuseMaterial(new SolidColorBrush( Colors.Blue ) );
otrzymujemy:

LinearGradientBrush i RadialGradientBrush (GradientBrush)

LinearGradientBrush i RadialGradientBrush, to pędzle trochę bardziej zaawansowane. Dziedziczą po pędzlu GradientBrush i pozwalają pokryć obiekt gradientem. Poza definicją samego pędzla należy pamiętać o ustawieniu właściwości TextureCoordinates dla krytej pędzlem siatki/figury (MeshGeometry3D).
Jako przykład przyjrzyjmy się scenie podobnej do powyższej, ale pokrytej z wykorzystaniem pędzla LinearGradientBrush, a więc kod:
mesh.TextureCoordinates = ( (PointCollection)new PointCollectionConverter().ConvertFromString( "0,0 0,1 1,1 1,0" ) );
LinearGradientBrush myBrush = new LinearGradientBrush();
myBrush.GradientStops.Add( new GradientStop( Colors.Yellow, 0.0 ) );
myBrush.GradientStops.Add( new GradientStop( Colors.Red,0.5) );
myBrush.GradientStops.Add( new GradientStop( Colors.Blue, 1 ) );
Material material = new DiffuseMaterial( myBrush );
Da nam w efekcie:

TextureCoordinates

W tym miejscu należy jeszcze wspomnieć o teksturowaniu, czyli zgodnie z Wikipedią:
Teksturowanie - technika stosowana w grafice trójwymiarowej, której celem jest przedstawienie szczegółów powierzchni obiektów przestrzennych za pomocą obrazów bitmapowych (tekstur) lub funkcji matematycznych (tekstur proceduralnych). Mapowanie tekstury określa, w jaki sposób powiązać piksele lub wartości funkcji z powierzchnią obiektu. Tekstury niosą informacje o barwie powierzchni, jak również innych parametrach generowanego obrazu, związanych np. z modelem oświetlenia: barwa światła odbitego, rozproszonego, stopień przezroczystości, współczynnik załamania światła itp.”
Na siatkę (w tym przypadku MeshGeometry3D), tworzącą bryłę scenie 3D nakładamy więc teksturę. Aby tekstura nałożona była właściwie (a nawet aby w ogóle było ją widać), należy ustawić odpowiednie jej koordynaty we właściwości TextureCoordinates siatki. W ramach tej właściwości ustawiamy współrzędne tekstury (listę punktów) określających, jak wybrany materiał jest mapowany na wierzchołki trójkątów, które tworzą siatkę.

VisualBrush

Kolejnym ciekawym pędzlem jest VisualBrush, który pozwala na „malowanie” elementami GUI wchodzącymi w skład WPF-a, np. TexBlock'iem, który można użyć do wyprowadzania teksu:
TextBlock textblock = new TextBlock( new Run( "maciek" ) );
textblock.Foreground = new SolidColorBrush( Colors.Green );
textblock.FontFamily = new FontFamily( "Arial" );
Material material = new DiffuseMaterial( new VisualBrush( textblock ) );
Powyższy kod, pozwoli na uzyskanie następującego efektu:
Aby dowiedzieć się więcej na temat wyprowadzania tekstu, polecam zapoznać się z artykułem: „Tworzenie napisów dla scen 3D tworzonych przy pomocy WPF

ImageBrush

ImageBrush jest chyba najciekawszym pędzlem do wykorzystania w scenie 3D. Pozwala na nanoszenie na obiekty w scenie dowolnych obrazków (tekstur), np.:
ImageBrush imageBrush = new ImageBrush();
imageBrush.ImageSource = new BitmapImage(
        new Uri( @".\maciej-progtech-wpf-3d-light-sun.jpg", UriKind.Relative ) );
Material material = new DiffuseMaterial( imageBrush );
W efekcie otrzymamy:

Kafelkowanie

Tutaj należy jeszcze wspomnieć o kafelkowaniu, czyli składaniu materiału z wielu obrazków. W tym celu należy ustawić właściwości: TileMode (jakie kafelkowanie), Viewbox (jaki fragment tekstury wyświetlamy?), VieboxUnits (w jakich jednostkach ustawiamy fragment tekstury, który wyświetlamy), Viewport (jaki fragment ma zajmować wyświetlana tekstura?), ViewportUnits ( w jakich jednostkach ustawiamy fragment, który ma zajmować wyświetlana tekstura).
W poniższym przykładzie wykorzystałem pędzel z poprzedniego przykładu, ale zmieniłem ustawienia „kafelkowania”:
imageBrush.TileMode = TileMode.Tile;
imageBrush.Viewport = ( (Rect)new RectConverter().ConvertFromString( "0,0,0.5,0.5" ) );
Dało to w efekcie:
Na koniec chciałbym jeszcze dodać, że warto zajrzeć na MSDN'a i przeczytać również „MSDN: Painting with Images, Drawings, and Visuals
Promuj

7 komentarzy:

  1. Mam pytanie a propos ImageBrush.
    Otóż nie bardzo czaję kwestię ścieżki do grafiki.
    Testowo stworzyłem sobie coś takiego:
    ImageBrush brush = new ImageBrush(new BitmapImage(new Uri(BaseUriHelper.GetBaseUri(this), "Images/test.png")));
    label1.Background = brush;
    I powyższy kod działa.
    Niestety, gdy ładuję ten sam pędzel w poniższy sposób:
    Material material = new DiffuseMaterial(brush);
    scena nie jest renderowana.
    Czy możesz mi podsunąć pomysł na rozwiązanie problemu?
    Z góry dzięki.
    Pozdrawiam

    OdpowiedzUsuń
  2. Czesc, mianowicie nie bardzo wiem jak mam ustawic texturecoordinates by obrazek byl rowno na obu trojkatach (tak jak u ciebie przy imagebrush), moj aktualny model wylgada tak http://i.imgur.com/uwkbYU6.jpg, chcialbym by te paski były równo.

    OdpowiedzUsuń
  3. Świetny tekst! Od roku nie czytałam tak wartościowego artykułu.

    OdpowiedzUsuń

Posty powiązane / Related posts