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)
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)
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)
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 !?
Jawaban:
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.
sumber
return float4(texCoord.rg, 0, 1);
.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:
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.
sumber
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.
sumber
Itu hanya karena Anda menggunakan texCoords dari output vertex yang diinterpolasi oleh hard drive setelah memproses vertex shader.
sumber