Render sempurna Pixel ke target rendert dengan quad layar penuh

9

Saya mengalami masalah dalam merender banyak nilai ke target rendert. Nilai tidak pernah berakhir dalam kisaran persis yang saya inginkan. Pada dasarnya saya menggunakan quad layar penuh dan shader piksel untuk membuat tekstur target rendert saya dan kemudian bermaksud untuk menggunakan koordinat tekstur sebagai dasar untuk beberapa perhitungan dalam shader. Koordinat tekstur rentang quad mulai dari (0,0) di kiri atas (1,1) di sudut kanan bawah ... masalahnya adalah, setelah interpolasi, nilai-nilai ini tidak sampai pada pixel shader karena itu .

Contoh: Saya membuat tekstur 4x4 dan shader dalam hal ini hanya menampilkan koordinat tekstur (u, v) di saluran merah dan hijau:

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

Yang ingin saya hilangkan adalah tekstur di mana piksel di kiri atas adalah RGB (0,0,0) dan dengan demikian hitam, piksel di kanan atas adalah RGB (255,0,0) dan dengan demikian cerah merah dan piksel di kiri bawah adalah RGB (0,255,0) - hijau cerah.

Namun, sebaliknya saya mendapatkan ini di sini di sebelah kanan:

(render quad lurus, tidak ada koreksi)
render lurus quad, tidak ada koreksi yang diterapkan

Pixel kiri atas berwarna hitam, tapi saya hanya mendapatkan warna merah dan hijau yang relatif gelap di sudut lainnya. Nilai RGB mereka adalah (191,0,0) dan (0,191,0). Saya sangat curiga ini ada hubungannya dengan lokasi pengambilan sampel quad: piksel kiri atas dengan benar mengambil sampel sudut kiri atas quad dan mendapatkan (0,0) sebagai koordinat UV, tetapi piksel sudut lainnya tidak mengambil sampel dari sudut-sudut lain dari quad. Saya telah menggambarkan ini di gambar kiri dengan kotak biru yang mewakili quad dan titik putih koordinat pengambilan sampel atas.

Sekarang saya tahu tentang setengah piksel offset yang harus Anda terapkan pada koordinat Anda saat merender layar quads yang disejajarkan dengan Direct3D9. Mari kita lihat hasil apa yang saya dapatkan dari ini:

(quad diberikan dengan offset setengah piksel DX9)
quad dirender dengan offset setengah piksel

Merah dan hijau menjadi lebih terang tetapi masih belum benar: 223 adalah maksimum yang saya dapatkan pada saluran warna merah atau hijau. Tapi sekarang, saya bahkan tidak memiliki hitam pekat lagi, tetapi sebaliknya abu-abu gelap, kekuningan dengan RGB (32,32,0)!

Yang sebenarnya saya butuhkan adalah rendering semacam ini:

(target membuat, mengurangi ukuran quad)
hitam pekat, hijau dan merah dengan interpolasi yang benar

Sepertinya saya harus bergerak ke kanan dan batas bawah quad saya tepat satu piksel ke atas dan ke kiri, dibandingkan dengan gambar pertama. Maka kolom kanan dan baris paling bawah dari piksel semua harus mendapatkan koordinat UV dengan benar langsung dari batas quad:

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)),
};

Namun ini tidak berhasil dan menyebabkan piksel bagian bawah dan kanan tidak render sama sekali. Saya kira pusat piksel ini tidak tercakup oleh quad lagi dan dengan demikian tidak akan diproses oleh shader. Jika saya mengubah perhitungan pixelSize dengan jumlah kecil untuk membuat quad jumlah kecil lebih besar itu berfungsi ... setidaknya pada tekstur 4x4. Itu tidak bekerja pada tekstur yang lebih kecil dan saya khawatir secara halus mendistorsi distribusi nilai uv pada tekstur yang lebih besar juga:

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

(Saya memodifikasi perhitungan pixelSize sebesar 0,001f - untuk tekstur yang lebih kecil, misalnya tabel pencarian 1D, ini tidak berfungsi dan saya harus meningkatkannya ke 0,01f atau sesuatu yang lebih besar)

Tentu saja ini adalah contoh sepele dan saya bisa melakukan perhitungan ini lebih mudah pada CPU tanpa harus khawatir tentang pemetaan UVs ke pusat pixel ... masih, harus ada cara untuk benar-benar membuat penuh, lengkap [0, 1] jangkauan ke piksel pada target rendert !?

Mario
sumber
Bukan berarti itu bisa membantu, tapi saya mengalami masalah yang sama persis.
IUsedToBeAPygmy
1
Jangan lupa untuk menonaktifkan sampling linier juga.
r2d2rigo
Itu tidak terlihat seperti sistem koordinat yang Anda gunakan cocok dengan piksel layar — jika ya, maka sepertinya Anda hanya menggambar 2 × 2 piksel di sini. Masalah pertama yang saya selesaikan adalah mendapatkan skala proyek dan modelview diskalakan sedemikian rupa sehingga +1 di ruang koordinat adalah pergeseran 1 piksel pada layar.
Slipp D. Thompson

Jawaban:

4

Masalah Anda adalah bahwa UV dirancang untuk menjadi koordinat tekstur. Koordinat 0,0 adalah sudut kiri atas piksel kiri atas dalam tekstur, yang bukan tempat Anda ingin membaca tekstur. Untuk 2D Anda ingin membaca tekstur di tengah piksel itu.

Jika matematika saya benar, apa yang perlu Anda lakukan untuk mengerjakannya adalah:

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

Itu Anda kurangi offset yang sesuai untuk mendapatkan pixel kiri atas pada 0,0 dan kemudian menerapkan skala untuk mendapatkan kanan bawah benar.

Hal yang sama juga dapat dicapai dengan memperluas koordinat UV untuk geometri di luar kisaran 0-1.

Adam
sumber
Untuk menguraikan kalimat terakhir: Anda bisa menerapkan transformasi ini ke UVs dalam buffer vertex untuk empat sudut quad, kemudian dalam pixel shader lakukan return float4(texCoord.rg, 0, 1);.
Nathan Reed
Saya mencoba formula yang disarankan di atas dan itu tidak berhasil: dalam rendertarget sampel 4x4 saya dari atas saya mendapatkan 0, 42, 127 dan 212 di saluran R dan G, dari kiri ke kanan atau atas ke bawah. Namun, saya ingin nilai dari 0 hingga 255 dalam langkah-langkah yang berjarak sama (untuk tekstur 4x4 yaitu 0, 85, 170 dan 255). Saya juga mencoba mengubah koordinat UV, tetapi belum menemukan offset yang tepat.
Mario
0

Sepertinya saya harus bergerak ke kanan dan batas bawah quad saya tepat satu piksel ke atas dan ke kiri, dibandingkan dengan gambar pertama.

Hampir, tetapi hanya setengah dari pixel. Cukup kurangi 0,5 dari simpul quad Anda. Misalnya Anda ingin 256x256 quad dengan tekstur yang sama, berikut adalah poin Anda:

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

Atau, Anda dapat menambahkan setengah dari texel dalam pixel shader (texCoord.rg + 0.5 * (1.0 / texSize))

Setengah piksel offset adalah fakta yang diketahui di DX9. Ini dan artikel ini dapat membantu Anda juga.

alariq
sumber
0

Ini tentu saja disebabkan oleh pengambilan sampel linier. Jika Anda melihat texels di kanan dan di bawah pixel kiri-atas hitam penuh, Anda akan melihat bahwa mereka memiliki 63 di saluran R dan / atau G, dan 2 di antaranya memiliki 2 di B. Sekarang lihat di Anda mendapatkan lumpur-gelap-kuning; itu 31 di R dan G dan 1 di B. Itu pasti hasil dari rata-rata texels lebih dari kelompok 2x2, jadi solusinya adalah mengatur filter tekstur Anda ke titik.

Maximus Minimus
sumber
-2

Itu hanya karena Anda menggunakan texCoords dari output vertex yang diinterpolasi oleh hard drive setelah memproses vertex shader.

Jason Huang
sumber