Przyjmę, że treść pożądanych znaczników html jest danymi stron trzecich, takimi jak obraz lub element iframe, i nie można ich używać z webgl, więc musi to być znacznik html i nie może być sprite.
Istnieje książka kucharska do obliczeń na GPU. Powtórz każdą zmianę sceny. Niestety nie mogę tego zrobić dla Three.js (nie znam silnika).
Etap 1 Konstrukt widzialność obrazu z etykietkami
Tworzenie zbioru (a) wskaźnik bufora zawierającego wielkości Znaczniki, identyfikatory bramek (Int liczb od 0) i pozycji znacznika.
Tworzenie bufora renderowania i nowego programu WebGL, który będzie używany do renderowania do niego. Shadery tego programu będą renderować uproszczoną scenę, w tym "cienie tagów". Teraz uproszczony algorytm dla modułu cieniującego jest następujący: dla dowolnego obiektu renderuj biały kolor. Z wyjątkiem tagu, renderuj kolor na podstawie identyfikatora tagu.
Jeśli twój bieżący program ma mgły, przezroczyste obiekty, mapy wysokości lub pewną logikę proceduralną, może być również zawarty w modułach cieniowania (zależy, czy może on obejmować znacznik, czy nie).
Wynik może wyglądać tak (ale to nie jest ważne):
Jeśli kolor jest inna niż biały, istnieje znacznik. (Zakładając, że mam tylko 3 znaczniki, a następnie moje kolory # 000000, # 010000, # 020000, które wyglądają jak czarny, ale nie jest.)
Stage 2, Collect przejrzystości danych o znacznikach w obrazie
Potrzebujemy innego programu WebGL i renderbuffer. Wyrenderujemy punkty w buforze renderowania, każdy punkt ma jeden piksel i znajduje się obok siebie. Punkty reprezentują znaczniki. Będziemy więc potrzebować bufora tablicowego z pozycjami znaczników (i identyfikatorami znaczników, ale można to wydedukować w shaderze). Łączymy również tekstury z poprzedniego etapu.
Teraz kod wierzchołkowego modułu cieniującego wykona następujące czynności, w oparciu o atrybut identyfikatora znacznika, ustawi położenie punktu. Następnie oblicza przezroczystość z wyszukiwaniem tekstury, pseudokod:
attribute vec3 tagPosition;
attribute float tagId;
float calculateTransparency(vec2 tagSize, vec2 tagPosition) {
float transparency = 0;
for(0-tagSize.x+tagPosition.x) {
for(0-tagSize.y+tagPosition.y) {
if(textureLookup == tagId) transparency++; // notice that texture lookup is used only for area where tag could be
}
}
return transparency/totalSize;
}
vec2 tagSize2d = calculateSize(tagPosition);
float transparency = calculateTransparency(tagSize2d, tagPosition.xy);
Pozycja punktu i przezroczystość zostaną wprowadzone jako zmienne w stosunku do FS. FS będzie renderował niektóre kolory w oparciu o przezroczystość (na przykład biała dla pełnej widoczności, czarna dla niewidoczna i odcienie szarości dla częściowej widoczności).
Rezultatem tego etapu jest obraz, w którym każdy piksel przedstawia jeden znacznik, a kolor piksela oznacza przezroczystość znacznika. W zależności od tego, ile tagów posiadasz, niektóre piksele mogą nic nie oznaczać i mają wartość clearColor. Pozycja pikseli odpowiada identyfikatorowi tagu.
Stage 3, odczyt wartości z javascript
Aby odczytać dane z powrotem używać readPixels (lub może korzystać texImage2D?). Simple way to do it.
Następnie używa się forloop na podstawie identyfikatorów znaczników i zapisuje dane z tablicy maszynowej na przykład na komputerze stanu javascript. Teraz masz wartości przezroczystości w javascript i możesz zmieniać wartości CSS.
Pomysły
W etapie 1, zmniejszając rozmiar renderbuffer spowoduje znaczny wzrost wydajności (to obniża także tekstura wyszukiwań w etapie 2) z prawie zero kosztów.
Jeśli korzystasz z readPixels bezpośrednio po etapie 1 i próbujesz odczytać dane z ekranu za pomocą javascript, nawet jeśli używasz renderbuffer tylko o wielkości 320 * 200px, js musi wykonać tyle iteracji co rozdzielczość. Więc jeśli scena zmieni się co chwilę, to po prostu pusty forloop:
var time = Date.now();
for(var i=0;i<320*200*60;i++) { // 64000*60 times per second
}
console.log(Date.now() - time);
tooks ~ 4100ms na moim komputerze. Ale na etapie 2 musisz wykonać tylko tyle powtórzeń, ile tagów w widocznym obszarze. (Dla 50 znaczników może to być 3000 * 60).
Największy problem, jaki widzę, to złożoność implementacji.
Węzłem tej techniki jest readpixel i wyszukiwanie tekstur. Możesz rozważyć nie wywołanie etapu 3 z prędkością FPS, ale zamiast tego z wolniejszą predefiniowaną prędkością.
Czy zamiast elementu HTML można użyć elementu "THREE.Sprite"? – WestLangley
@WestLangley Potrzebuję dynamicznie generowanych i interaktywnych (klikalnych) elementów; czy można to zrobić całkiem dobrze z duszkami? – Frxstrem
Nie rozumiem, dlaczego nie. Ponownie użyj swoich duszków. Utwórz pulę z nich. Jeśli przekroczysz liczbę dostępną w puli, dodaj ją do puli. – WestLangley