Obecnie próbuję zamaskować niektóre duszki. Zamiast wyjaśniać to słowami, stworzyłem kilka przykładowych zdjęć:
Obszar do maskowania (w kolorze białym)
Teraz czerwony duszek, który należy przyciąć.
Wynik końcowy.
Teraz wiem, że w XNA możesz zrobić dwie rzeczy, aby to osiągnąć:
- Użyj bufora szablonów.
- Użyj modułu Pixel Shader.
Próbowałem zrobić moduł cieniujący piksele, który zasadniczo to zrobił:
float4 main(float2 texCoord : TEXCOORD0) : COLOR0
{
float4 tex = tex2D(BaseTexture, texCoord);
float4 bitMask = tex2D(MaskTexture, texCoord);
if (bitMask.a > 0)
{
return float4(tex.r, tex.g, tex.b, tex.a);
}
else
{
return float4(0, 0, 0, 0);
}
}
To wydaje się przycinać obrazy (choć nie jest poprawne, gdy obraz zaczyna się poruszać), ale moim problemem jest to, że obrazy ciągle się poruszają (nie są statyczne), więc to przycinanie musi być dynamiczne.
Czy mogę zmienić kod modułu cieniującego, aby uwzględnić jego pozycję?
Ewentualnie czytałem o używaniu bufora szablonów, ale większość próbek wydaje się zależeć od użycia rendertargetu, czego tak naprawdę nie chcę robić. (Używam już 3 lub 4 do końca gry, a dodanie kolejnej na górze wydaje się przesadą)
Jedyny samouczek, który nie używa Rendertargets, to jeden z bloga Shawna Hargreavesa tutaj. Problem polega na tym, że dotyczy XNA 3.1 i wydaje się, że nie przekłada się to dobrze na XNA 4.0.
Wydaje mi się, że cieniowanie pikseli jest właściwą drogą, ale nie jestem pewien, jak prawidłowo ustawić pozycję. Wydaje mi się, że musiałbym zmienić współrzędne na ekranie (np. 500, 500), aby były w zakresie od 0 do 1 dla współrzędnych modułu cieniującego. Moim jedynym problemem jest próba ustalenia, jak poprawnie używać przekształconych współrzędnych.
Z góry dziękuję za wszelką pomoc!
Odpowiedzi:
Możesz użyć metody cieniowania pikseli, ale jest ona niekompletna.
Będziesz także musiał dodać kilka parametrów, które informują moduł cieniujący pikseli, gdzie chcesz umieścić szablon.
W kodzie C # przekazujesz zmienne do modułu cieniującego piksele przed
SpriteBatch.Draw
wywołaniem w następujący sposób:źródło
maskCoord
obliczeniach jeden z X powinien być literą Y. Zredagowałem moją wstępną odpowiedź z poprawką i uwzględniłem ustawienia klamry UV, których będziesz potrzebowaćMaskTexture sampler
.Pierwszym krokiem jest poinformowanie karty graficznej, że potrzebujemy bufora szablonu. Aby to zrobić podczas tworzenia GraphicsDeviceManager, ustawiamy PreferredDepthStencilFormat na DepthFormat.Depth24Stencil8, aby w rzeczywistości istniał szablon do pisania.
AlphaTestEffect służy do ustawiania układu współrzędnych i filtrowania pikseli za pomocą alfa, które przejdą test alfa. Nie zamierzamy ustawiać żadnych filtrów ani ustawiać układu współrzędnych na port widoku.
Następnie musimy skonfigurować dwa DepthStencilStates. Te stany określają, kiedy SpriteBatch renderuje na szablonie, a kiedy SpriteBatch renderuje na BackBuffer. Interesują nas przede wszystkim dwie zmienne StencilFunction i StencilPass.
Dla pierwszego DepthStencilState ustawiamy StencilFunction na CompareFunction. Powoduje to, że StencilTest się powiódł, a gdy StencilTest SpriteBatch renderuje ten piksel. StencilPass jest ustawiony na StencilOperation. Zamień, co oznacza, że gdy test StencilTest zakończy się powodzeniem, piksel zostanie zapisany w StencilBuffer o wartości ReferenceStencil.
Podsumowując, StencilTest zawsze przechodzi, obraz jest rysowany na ekranie normalnie, a dla pikseli rysowanych na ekranie wartość 1 jest przechowywana w StencilBuffer.
Drugi DepthStencilState jest nieco bardziej skomplikowany. Tym razem chcemy rysować na ekranie tylko wtedy, gdy wartość w StencilBuffer wynosi. Aby to osiągnąć, ustawiamy StencilFunction na CompareFunction.LessEqual i ReferenceStencil na 1. Oznacza to, że gdy wartość w buforze szablonu wynosi 1, test StencilTest się powiedzie. Ustawienie StencilPass na StencilOperation. Zachowaj powoduje, że StencilBuffer nie aktualizuje się. To pozwala nam rysować wiele razy przy użyciu tej samej maski.
Podsumowując, StencilTest przechodzi tylko wtedy, gdy StencilBuffer ma mniej niż 1 (piksele alfa z maski) i nie wpływa na StencilBuffer.
Teraz, gdy mamy skonfigurowane nasze DepthStencilStates. Możemy rysować za pomocą maski. Po prostu narysuj maskę za pomocą pierwszego DepthStencilState. To wpłynie zarówno na BackBuffer, jak i StencilBuffer. Teraz, gdy bufor szablonu ma wartość 0, w której maska ma przezroczystość, a 1, w której zawiera kolor, możemy użyć StencilBuffer do maskowania późniejszych obrazów.
Drugi SpriteBatch używa drugiego DepthStencilStates. Bez względu na to, co narysujesz, tylko piksele, w których StencilBuffer jest ustawiony na 1, przejdą test szablonu i zostaną narysowane na ekranie.
Poniżej znajduje się całość kodu w metodzie Draw, nie zapomnij ustawić PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8 w konstruktorze gry.
źródło
W powyższej odpowiedzi zdarzyły się dziwne rzeczy, kiedy zrobiłem dokładnie to, co zostało wyjaśnione.
Jedyne, czego potrzebowałem, to jedno i drugie
DepthStencilStates
.I losowania bez
AlphaTestEffect
.I wszystko było w porządku, zgodnie z oczekiwaniami.
źródło