Przejdź do treści

laboratorium rel8

czas czytania: 7 minnapisane przez: REL8

Zubli dostał nowe ciało. Czego nauczyliśmy się, animując maskotkę piksel po pikselu.

Trzy wersje Zubliego obok siebie: maskotka z brył three.js, pierwsza próba nowej postaci z brył oraz nowy Zubli z renderu i shadera

Zubli to twarz naszej recepcji AI dla klinik stomatologicznych. Pacjent spotyka go w rogu strony kliniki późnym wieczorem, gdy recepcja dawno zamknięta, i pyta o cenę, wolny termin albo o to, czy ból zęba może poczekać do jutra. Postać, z którą ludzie rozmawiają o 23:00, ma jedno zadanie: sprawić, żeby rozmowa w pierwszych dwóch sekundach wydawała się bezpieczna.

W tym tygodniu zbudowaliśmy ciało Zubliego od zera. Nie osobowość, nie logikę rozmowy, tylko ciało. Najciekawszy nie jest efekt, lecz zakręt, który musieliśmy wziąć w połowie drogi. Ta historia pokazuje też, jak wygląda dzień pracy nad DentalDesk.pl.

Po co klinice maskotka w czacie?

Bo pacjent nie pisze do „systemu”. Pisze do kogoś. Maskotka obniża próg pierwszej wiadomości, zwłaszcza u osób, które boją się dentysty albo wstydzą się zapytać o cenę. Zubli nie diagnozuje i nie udaje lekarza. Odpowiada tylko na podstawie danych zatwierdzonych przez klinikę, a rano recepcja dostaje gotową kartę zgłoszenia z zapisem rozmowy.

Próba pierwsza: robot z brył

Pierwszy Zubli był „prawdziwym” 3D. Postać składała się w przeglądarce z kilkudziesięciu prostych brył: zaokrąglonych pudełek, kul, kapsuł i torusów. Każda miała własny materiał, całość oświetlał mały studyjny zestaw świateł, a animacja działała klatka po klatce. Z tego podejścia wyszły trzy prototypy: wesoły żółty robot ze stokrotką, wyluzowany w dzianinowej bluzie i dentystyczny „Doc” w czepku.

Poruszały się pięknie. Na tle grafiki koncepcyjnej wyglądały jednak jak zabawki obok fotografii. Faktura dzianiny, lakierowane odblaski na czarnym wizjerze, szwy bluzy, sznurówki sneakersów: każdy taki detal współczesny model graficzny renderuje w sekundy, a ręczne modelowanie zajęłoby tydzień.

Po lewej prototyp zbudowany z brył, po prawej nowe ciało Zubliego z renderu

Próba druga: zostawić piksele, dodać ruch

Odwróciliśmy problem. Zamiast modelować detale, wzięliśmy gotowy obraz i nauczyliśmy go ruszać się. Odpowiadają za to trzy warstwy.

Klatki są źródłem prawdy. Wygenerowaliśmy tę samą postać w sześciu pozach, w identycznym kadrze i na płaskim szarym tle. Skrypt w Pythonie wycina tło (segmentacja), wymazuje świecące oczy (inpainting) i wyrównuje każdą klatkę na dwóch kotwicach: na punkcie między oczami i na linii stóp. Zmiana pozy jest odtąd płynnym przenikaniem, nie skokiem. Przy przenikaniu pojawiały się szare obwódki. Zniknęły po przejściu na alfę premultiplikowaną, czyli zapis, w którym kolor piksela jest z góry pomnożony przez jego krycie.

Zestaw sześciu wyrównanych klatek Zubliego w różnych pozach

Kukiełka, nie film. Każda klatka to tekstura na siatce z około pięciu tysięcy wierzchołków. Shader dzieli sylwetkę na miękko ważone strefy: głowa obraca się wokół szyi, tułów oddycha od stóp, rękawy zginają się w barkach, a cała postać kołysze się i spłaszcza przy lądowaniu. Ten sam pomysł napędza postacie Live2D w grach: deformuj obraz zamiast renderować model.

Oczy rysowane na żywo. Wizjer jest czystą czarną powierzchnią. Oczy rysujemy na osobnym, małym płótnie i składamy w shaderze fragmentów wewnątrz prostokąta wizjera, na tej samej siatce. Obracają się więc razem z głową. Daje to mruganie, spojrzenie podążające za kursorem, uśmiech, serduszka, a nawet linie CRT z pierwotnego renderu.

Cztery warianty oczu Zubliego: neutralne, wesołe, mrugnięcie i serduszka, rysowane w czasie rzeczywistym

Jeden filtr CSS z cieniem po cichu zabierał połowę klatek. Na zintegrowanej karcie graficznej było 29 klatek na sekundę, po usunięciu filtra 60.

Bryły 3D czy animowany render?

Robot z brył 3DAnimowany render
Jakość detaluograniczona, „zabawkowa”jak na grafice koncepcyjnej
Zakres ruchunieograniczonyduże gesty z klatek, mikroruch z matematyki
Nowa pozadni modelowaniajedna klatka przez pipeline
Nowa postaćnowy model od zerata sama ścieżka, inna klatka bazowa
Kolor pod markę klinikiprzemalowanie materiałówkolor oczu z palety kliniki
Wydajnośćzależna od liczby bryłzmierzone 60 klatek na sekundę na zintegrowanej grafice
Schemat procesu: render, wysegmentowana i wyrównana klatka z wymazanym wizjerem, animowana kukiełka

Jak to powstało

To była praca dwóch agentów. Claude zaprojektował pipeline, shader i runtime. GPT wygenerował klatki poz z jednego obrazu referencyjnego. Koordynował je herdr, nasz multiplekser terminali dla agentów kodujących. Przeglądarka bez interfejsu sprawdzała każdy gest zrzutami ekranu, zanim obejrzał je człowiek.

Czego nas ta budowa nauczyła

  • Mierzymy animacje na zintegrowanej grafice. Jeden filtr CSS z cieniem zabierał połowę klatek: było 29, miało być 60.
  • Sześć wyrównanych klatek zastępuje całą animację, jeśli siatka ma około pięciu tysięcy wierzchołków i ważone strefy.
  • Szare obwódki przy przenikaniu klatek to problem zwykłej alfy. Alfa premultiplikowana rozwiązała go w shaderze.
  • Oczy nie muszą być geometrią. Osobne płótno i złożenie w shaderze fragmentów dają mruganie i spojrzenie podążające za kursorem.
  • Dwa agenty koordynowane przez herdr dzielą pracę jak dwie osoby: Claude pisał pipeline i shader, GPT robił klatki.

Zamiast budować robota, który próbuje wyglądać jak obrazek, wzięliśmy obrazek i nauczyliśmy go ruszać się. Cały ruch mieści się w sześciu klatkach i pięciu tysiącach wierzchołków.