Doskonałe renderowanie w pikselach do rendertargetu z czterokrotnie pełnoekranowym ekranem

9

Mam problem z renderowaniem wiązki wartości w rendertarget. Wartości nigdy nie kończą się w dokładnie takim zakresie, jaki chcę. Zasadniczo używam kwadratu pełnoekranowego i modułu cieniującego piksele do renderowania na mojej tekstu rendertarget, a następnie zamierzam użyć współrzędnych tekstury jako podstawy do niektórych obliczeń w module cieniującym. Współrzędne tekstury dla zakresu quada od (0,0) w lewym górnym rogu do (1,1) w prawym dolnym rogu ... problem polega na tym, że po interpolacji te wartości nie docierają do modułu cieniującego jako takiego .

Przykład: renderuję teksturę 4x4, a moduł cieniujący w tym przypadku po prostu wyświetla współrzędne tekstury (u, v) w kanałach czerwonym i zielonym:

return float4(texCoord.rg, 0, 1);

Chcę z niego uzyskać teksturę, w której piksel w lewym górnym rogu to RGB (0,0,0), a zatem czarny, piksel w prawym górnym rogu to RGB (255,0,0), a zatem jasny czerwony, a piksel w lewym dolnym rogu to RGB (0,255,0) - jasna zieleń.

Zamiast tego widzę to tutaj po prawej stronie:

(renderowanie prostego kwadratu, bez korekty)
proste renderowanie kwadratu, bez korekty

Lewy górny piksel jest czarny, ale w pozostałych rogach dostaję stosunkowo ciemnoczerwony i ciemnozielony kolor. Ich wartości RGB to (191,0,0) i (0,191,0). Podejrzewam, że ma to związek z lokalizacjami próbkowania kwadratu: lewy górny piksel poprawnie próbkuje lewy górny róg kwadratu i otrzymuje (0,0) jako współrzędne UV, ale pozostałe piksele narożne nie próbkują z pozostałe rogi quada. Zilustrowałem to na lewym obrazie z niebieskim polem reprezentującym kwadrat, a białymi kropkami górne współrzędne próbkowania.

Teraz wiem o przesunięciu o pół piksela, które powinieneś zastosować do swoich współrzędnych podczas renderowania quad wyrównanych do ekranu w Direct3D9. Zobaczmy, jaki wynik mam z tego:

(quad renderowany z przesunięciem pół piksela DX9)
quad renderowany z przesunięciem o pół piksela

Czerwone i zielone stały się jaśniejsze, ale nadal nie są poprawne: 223 to maksimum, jakie dostaję na kanale koloru czerwonego lub zielonego. Ale teraz nie mam już nawet czystej czerni, ale zamiast tego ciemnożółty szary z RGB (32,32,0)!

Tak naprawdę potrzebuję tego rodzaju renderowania:

(docelowy rendering, zmniejszony rozmiar quada)
czysta czerń, zieleń i czerwień z poprawną interpolacją

Wygląda na to, że muszę przesunąć prawą i dolną ramkę mojego quada dokładnie o jeden piksel w górę i w lewo, w porównaniu do pierwszej cyfry. Następnie prawa kolumna i dolny rząd pikseli powinny poprawnie uzyskać współrzędne UV od granicy kwadratu:

VertexPositionTexture[] quad = new VertexPositionTexture[]
{
    new VertexPositionTexture(new Vector3(-1.0f,  1.0f, 1f), new Vector2(0,0)),
    new VertexPositionTexture(new Vector3(1.0f - pixelSize.X,  1.0f, 1f), new Vector2(1,0)),
    new VertexPositionTexture(new Vector3(-1.0f, -1.0f + pixelSize.Y, 1f), new Vector2(0,1)),
    new VertexPositionTexture(new Vector3(1.0f - pixelSize.X, -1.0f + pixelSize.Y, 1f), new Vector2(1,1)),
};

Jednak to nie całkiem zadziałało i spowodowało, że dolne i prawe piksele w ogóle się nie renderowały. Podejrzewam, że centra tych pikseli nie są już objęte quadem i dlatego nie będą przetwarzane przez moduł cieniujący. Jeśli zmienię obliczenie pixelSize o niewielką ilość, aby quad był nieco większy, to trochę to działa ... przynajmniej na fakturze 4x4. Nie działa na mniejszych teksturach i obawiam się, że subtelnie zaburza równomierny rozkład wartości UV na większych teksturach:

Vector2 pixelSize = new Vector2((2f / textureWidth) - 0.001f, (2f / textureHeight) - 0.001f);

(Zmodyfikowałem obliczenie pixelSize o 0,001f - w przypadku mniejszych tekstur, np. Tabel przeglądowych 1D, to nie działa i muszę go zwiększyć do 0,01f lub coś większego)

Oczywiście jest to trywialny przykład i mógłbym wykonać te obliczenia o wiele łatwiej na CPU bez martwienia się o mapowanie UV do centrów pikseli ... jednak musi istnieć sposób, aby faktycznie renderować pełne, pełne [0, 1] zakres do pikseli w rendertargecie !?

Mario
źródło
To nie jest żadna pomoc, ale mam dokładnie ten sam problem.
IUsedToBeAPygmy
1
Nie zapomnij również wyłączyć próbkowania liniowego.
r2d2rigo
Nie wygląda na to, że układ współrzędnych, z którego korzystasz, pasuje do pikseli ekranu - gdyby tak było, wygląda na to, że rysujesz tutaj tylko 2 × 2 piksele. Pierwszym problemem, który rozwiązałem, jest skalowanie projektu i matryc widoku modelu w taki sposób, że +1 w przestrzeni współrzędnych to przesunięcie o 1 piksel na ekranie.
Slipp D. Thompson,

Odpowiedzi:

4

Problem polega na tym, że promienie UV mają być współrzędnymi tekstury. Współrzędna 0,0 to lewy górny róg lewego górnego piksela w teksturze, który nie jest tam, gdzie zwykle chcesz czytać teksturę. W przypadku 2D chcesz odczytać teksturę w środku tego piksela.

Jeśli moja matematyka ma rację, to co musisz zrobić, aby obejść, to:

return float4((texCoord.rg - (0.5f/textureSize)) * (textureSize/(textureSize-1)), 0, 1);

Oznacza to, że odejmuje się odpowiednie przesunięcie, aby uzyskać lewy górny piksel na 0,0, a następnie zastosować skalę, aby uzyskać prawy dolny prawy.

To samo można również osiągnąć, rozszerzając współrzędne UV dla geometrii poza zakresem 0-1.

Adam
źródło
Aby rozwinąć ostatnie zdanie: możesz zastosować tę transformację do UV w buforze wierzchołków dla czterech rogów kwadratu, a następnie w shaderze pikseli return float4(texCoord.rg, 0, 1);.
Nathan Reed
Wypróbowałem powyższą formułę i to nie do końca zadziałało: w moim rendertargecie 4x4 z góry dostaję 0, 42, 127 i 212 w kanałach R i G, od lewej do prawej lub od góry do dołu. Chciałbym jednak wartości od 0 do 255 w równomiernie rozmieszczonych krokach (dla tekstury 4x4, która wynosiłaby 0, 85, 170 i 255). Próbowałem także zmienić współrzędne UV, ale nie znalazłem jeszcze właściwego przesunięcia.
Mario
0

Wygląda na to, że muszę przesunąć prawą i dolną ramkę mojego quada dokładnie o jeden piksel w górę i w lewo, w porównaniu do pierwszej cyfry.

Prawie, ale tylko połowa piksela. Po prostu odejmij 0,5 od quadów. Np. Chcesz quad 256x256 o tej samej teksturze, oto twoje punkty:

p1 = (-0.5, -0.5)
p2 = (-0.5, 255-0.5)
p3 = (255-0.5, 255-0.5)
p4 = (255-0.5, -0.5)

Lub zamiast tego możesz dodać połowę tekstu w module cieniującym piksele (texCoord.rg + 0,5 * (1,0 / texSize))

Przesunięcie o pół piksela jest znanym faktem w DX9. Ten i te artykuły również mogą ci pomóc.

alariq
źródło
0

Jest to z pewnością spowodowane liniowym próbkowaniem. Jeśli spojrzysz na tekstury po prawej i poniżej pełnego czarnego lewego górnego piksela, zobaczysz, że mają 63 w kanałach R i / lub G, a 2 z nich mają 2 w B. Teraz spójrz na masz mętno-ciemnożółty; jest to 31 w R i G oraz 1 w B. Jest to zdecydowanie wynik uśredniania tekstur w grupie 2x2, więc rozwiązaniem jest ustawienie filtra tekstur w taki sposób, aby wskazywał.

Maximus Minimus
źródło
-2

Po prostu dlatego, że używasz texCoords z wyjścia wierzchołka, które jest interpolowane przez dysk twardy po przetworzeniu modułu cieniującego wierzchołek.

Jason Huang
źródło