Banyak lampu dengan bayangan di three.js menyebabkan kesalahan Fragment shader

Asumsikan untuk memiliki pemandangan dengan jalan yang banyak lampu jalan (lebih dari 20), Anda memindahkan objek ke dekatnya dan Anda mengharapkan bayangan.

Sederhananya, lampunya

var light = new THREE.PointLight(0xffffff, 0.5, 6.0);

Hanya jalan yang mempunyai .receiveShadow = true dan hanya mobil yang mempunyai .castShadow = true (selain nanti lampunya)

contoh

Di three.js menambahkan .castShadow = true ke semua lampu menyebabkan kesalahan berikut

THREE.WebGLProgram: shader error:  0 gl.VALIDATE_STATUS false 
gl.getProgramInfoLog Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (16).

Untungnya dalam adegan jam kita hanya membutuhkan beberapa (maksimal 4) dari mereka untuk menghasilkan bayangan, karena sebagian besar cahaya berada di luar jangkauan.

Saya mencoba menggunakan 2 pendekatan

  1. Mengulangi semua lampu dan menyetel .castShadow = true atau .castShadow = false secara dinamis.

  2. Menambah dan menghilangkan lampu seluruhnya namun mengaturnya tanpa bayangan atau bayangan.

Dengan keduanya saya mendapat kesalahan yang sama.

Pendekatan lain apa yang bisa berhasil?

Memperbarui

@neeh membuat Fiddle untuk itu di sini (untuk menyebabkan kesalahan berubah var numLightRows = 8; ke angka yang lebih tinggi). Namun perhatikan kesalahannya, akan ada kesalahan lain dengan terlalu banyak lampu yang tidak disebabkan oleh masalah yang sama

Dia juga menunjukkan bahwa kita melihat di sini bahwa pointShadowMap dibuat meskipun tidak digunakan. Hal ini menjelaskan mengapa tidak ada perubahan dengan pendekatan yang “lebih cerdas”. Ini sekarang ada dalam kode GLSL.

Jadi kami dibatasi oleh GPU, yang dalam kasus saya memiliki 16 IMAGE_UNITS tetapi tidak berlaku untuk semua GPU (CPU saya sebenarnya berfungsi baik dengan lebih banyak). Anda dapat memeriksa sistem Anda dengan renderer.capabilities.maxTextures. Tapi seperti yang disebutkan, kita sebenarnya hanya membutuhkan 4.

Masalahnya masih ada.


person arc    schedule 18.03.2017    source sumber
comment
Saya tidak akan menggunakan scene.remove( light ) dan scene.add( light ) tetapi melintasi pemandangan untuk mencari lampu, menghilangkan bayangannya dan untuk yang dekat dengan mobil menyalakannya.   -  person gaitat    schedule 19.03.2017
comment
@gaitat Saya mencoba keduanya dan sayangnya kedua alternatif tersebut gagal   -  person arc    schedule 19.03.2017


Jawaban (1)


Masalah

Ya a baru peta bayangan akan dibuat untuk setiap cahaya yang memiliki castShadow = true (Sebenarnya tidak demikian, periksa masalah ini). Peta bayangan adalah gambar di mana bayangan dihitung untuk kemudian dicampur pada suatu permukaan.

gl.getProgramInfoLog Jumlah sampel shader fragmen melebihi MAX_TEXTURE_IMAGE_UNITS (16).

Artinya, perangkat Anda dapat mengirim tidak lebih dari 16 tekstur per panggilan gambar. Biasanya, mobil (jalan?) yang ingin Anda beri bayangan adalah 1 panggilan seri.

Untuk menggambar objek yang menerima bayangan, semua peta bayangan harus dipadukan bersama dengan peta difusi. Jadi ini memerlukan penggunaan unit tekstur N+1 untuk satu panggilan undian. (N adalah jumlah cahaya yang dapat menghasilkan bayangan.)

Jika Anda mempelajari shader Three.js, Anda akan menemukan ini :

#ifdef USE_SHADOWMAP

    #if NUM_DIR_LIGHTS > 0

        // Reserving NUM_DIR_LIGHTS texture units
        uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];
        varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];

    #endif

    ...

#endif

Periksa alat ini untuk melihat berapa banyak unit tekstur yang dapat ditangani browser Anda (Fragment shader > Unit Gambar Tekstur Maks).


Solusinya ?

Membuat dan menghapus lampu secara dinamis itu buruk karena memakan banyak memori (alokasi peta bayangan...).

Namun, seperti yang dikatakan gaitat, Anda dapat mengaktifkan bayangan hanya untuk lampu terdekat. Lakukan saja hal berikut di loop render Anda:

  1. Nonaktifkan semua bayangan: light.castShadow = false;
  2. Carilah lampu terdekat
  3. Aktifkan bayangan untuk N lampu terdekat: light.castShadow = true;

Peningkatan

Algoritme ini buruk karena mengalokasikan satu peta bayangan per cahaya. Selain memakan memori, rendering akan terhenti sedikit setiap kali Anda melintasi cahaya baru yang tidak memiliki peta bayangan yang dialokasikan...

Oleh karena itu, idenya adalah menggunakan kembali peta bayangan yang sama untuk cahaya terdekat. Anda dapat menangani peta bayangan seperti ini:

// create a new shadow map
var shadowMapCamera = new THREE.PerspectiveCamera(90, 1, 0.5, 500);
var shadow = new THREE.LightShadow(shadowMapCamera);

// use the shadow map on a light
light.shadow = shadow;
shadow.camera.position.copy(light.position);
light.castShadow = true;

Anda bisa mendapatkan jumlah maksimum unit tekstur dengan renderer.capabilities.maxTextures. Jadi Anda dapat menghitung jumlah peta bayangan yang akan dibuat berdasarkan itu tetapi ingatlah untuk menyisakan beberapa untuk peta yang lebih teratur seperti diffuseMap, normalMap...

masukkan deskripsi gambar di sini

Lihat biola ini untuk implementasi penuh (hanya 4 peta bayangan yang digunakan).

person neeh    schedule 19.03.2017
comment
Terima kasih pak, atas jawabannya tentu memberi saya sedikit wawasan. Sayangnya hal itu tidak menyelesaikan masalah. Menambah numLightRows menyebabkan kesalahan lagi. Tampaknya aneh, Anda dapat menambahkan lampu sebanyak yang Anda inginkan tanpa masalah, tetapi menambahkan satu lampu bayangan saja akan menyebabkan kegagalan. - person arc; 19.03.2017
comment
Aneh, saya tidak mengalami kesalahan ini, bahkan dengan 100 lampu dan maxTextures = 32. Sebenarnya saya mendapat kesalahan lain tapi itu karena saya memiliki terlalu banyak cahaya di adegan saya. Apakah Anda yakin itu kesalahan yang sama persis? - person neeh; 19.03.2017
comment
Ya, kesalahan yang sama hingga 29 lampu, setelah saya mendapatkan kesalahan yang sama seperti di notebook saya: Varyings over maximum register limit, tapi saya tahu ini adalah batas perangkat keras, jadi tidak ada yang bisa dilakukan di sana. Saya memiliki EVGA 970 yang menjadi perhatian. - person arc; 19.03.2017
comment
Jadi error Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (16) masih dimulai dengan 16 lampu? Itu berarti ada peta per-cahaya lain di suatu tempat. - person neeh; 19.03.2017
comment
Saya rasa saya mendapat ide lain yang bisa digunakan untuk sejumlah lampu, jika saya berhasil saya akan menulis jawaban baru. - person neeh; 19.03.2017
comment
Bisa jadi. Pada dasarnya, saya bisa menambahkan lampu sebanyak yang saya mau. Namun begitu seseorang mempunyai bayangan, ia akan jatuh. Tentu, senang saya mendapat bantuan di sini - person arc; 19.03.2017
comment
Ya, baru kusadari bahwa... Aku tidak mengerti kenapa. - person neeh; 19.03.2017
comment
Oh saya mengerti! Lihat saja kode yang saya tempel, renderer.shadowMap.enabled = true; aktifkan USE_SHADOWMAP lalu kita alokasikan NUM_POINTS_LIGHTS yang bervariasi! Jadi mesinnya bahkan tidak peduli berapa banyak peta bayangan yang kami kirimkan... Bagaimanapun, saya punya ide baru dan saya akan menghapus jawaban ini. - person neeh; 19.03.2017