Inden for computergrafik har jagten på realisme altid været en drivkraft. I 1990'erne skete der en revolutionerende udvikling, der markant forbedrede muligheden for at skabe detaljerede og visuelt rige 3D-modeller uden at overvælde systemet med en enorm mængde geometriske grundelementer som linjer, knudepunkter og flader. Denne udvikling var teksturmapping. Forestil dig, hvor meget mere interessant en simpel model af en væg bliver, når den ser ud som mursten eller træ i stedet for bare en glat, ensfarvet overflade. Teksturmapping er teknikken, der gør dette muligt ved at 'male' et billede direkte på overfladen af en 3D-model.
Teksturmapping handler grundlæggende om at tage et 2D-billede – en samling af pixels – og projicere det eller 'mappe' det over på en 3D-models overflade. Selvom navnet antyder 'teksturer' som stof, mursten eller græs, kan billedet i princippet være hvad som helst; et foto af en person, et logo, eller endda et billede genereret af programmet selv. Oftest er det dog et billede, der indlæses fra en almindelig billedfil som f.eks. en JPEG.

For at bruge teksturmapping konceptuelt, er der to hovedtrin:
- Definér en tekstur: Dette er typisk en rektangulær samling af pixels, ofte kaldet texels (en forkortelse for teksturelementer). Texels er i bund og grund pixels, men i kontekst af den billedarray, der bruges til teksturmapping.
- Specifér et par af teksturkoordinater (typisk benævnt s og t, eller u og v) for hvert vertex (hjørne) af din geometri.
Grafiksystemet bruger derefter disse informationer til at 'male' teksturen på polygonen under rendering.
Hvordan virker teksturmapping?
Teksturmapping er en rasteroperation. Det betyder, at det foregår på pixelniveau, i modsætning til mange andre 3D-operationer, der arbejder med geometrien (punkter, linjer, flader). Selvom vi anvender teksturer på 2D-overflader i vores 3D-model, skal grafiksystemet finde ud af, hvordan pixels skal modificeres under rasterisering (også kendt som scan-konvertering).
Processen foregår som en del af rasteriseringen. Når grafikkortet render en polygon (ofte kaldet et fragment), sker følgende trin konceptuelt:
- Grafikkortet bestemmer pixelkoordinaterne for hvert hjørne af polygonen.
- Det bestemmer polygonens kantpixels ved hjælp af en linjetegningsalgoritme (som f.eks. Bresenham's algoritme).
- Det bestemmer farven på kantpixels på en enkelt række ved hjælp af lineær interpolering fra vertexfarverne.
- Det 'går' derefter ned ad rækken og farver hver pixel ved hjælp af lineær interpolering fra de to kantpixels.
For at implementere teksturmapping under denne rasteriseringsproces skal grafikkortet yderligere:
- Beregne en teksturkoordinat for hver pixel under rasteriseringsprocessen, typisk ved hjælp af bi-lineær interpolering. Dette trin er kritisk, da det afgør, hvilken del af teksturbilledet der skal bruges til den pågældende pixel på skærmen.
- Slå teksturkoordinaterne op i arrayet af texels. Dette kan gøres ved enten at bruge den nærmeste texel (point sampling) eller ved at interpolere farven fra de fire nærmeste texels (bilineær filtrering) for at opnå et glattere resultat.
- Endelig skal farven fra teksturen enten bruges som den endelige farve for pixelen, eller kombineres med den oprindelige farve på pixelen (f.eks. ved at multiplicere farverne, hvilket bruges til at simulere overfladematerialer).
Teksturrummet og koordinater
Teksturer kan være 1D eller 2D, selvom 2D er langt det mest almindelige. Teksturparametrene (s,t eller u,v) er konventionelt defineret i området [0,1] i hver dimension. Dette teksturrum repræsenterer hele teksturbilledet. Bemærk, at hvis din teksturarray ikke er kvadratisk, og din polygon heller ikke er det, kan du risikere at skulle håndtere ændringer i billedformatet, hvilket kan føre til forvrængning.
Din tekstur er altid en array og derfor altid rektangulær. At mappe en tekstur til rektangulære overflader er relativt ligetil. At mappe den til andre former, som f.eks. en kugle eller en cylinder, kræver mere komplekse mapping-metoder og kan lettere forårsage synlig forvrængning.
Vi associerer hvert vertex på vores polygon med et sæt teksturparametre (s,t), ligesom vi associerer det med en normalvektor, en farve osv. Grafik-API'er og biblioteker som Three.js har specifikke strukturer til at repræsentere teksturkoordinaterne for hvert vertex i en trekantet flade (da alle polygoner i 3D-grafik i sidste ende opdeles i trekanter).
Hvordan relaterer teksturkoordinaterne sig til det 2D-array af texels? Det standardmæssige forhold er, at koordinaten (0,0) typisk svarer til det ene hjørne af teksturbilledet (ofte øverste venstre), mens (1,1) svarer til det modsatte hjørne (ofte nederste højre). For et teksturbillede med en given bredde (W) og højde (H), vil en texel ved array-indekset [række][kolonne] svare til teksturkoordinaterne (kolonne / (W-1), række / (H-1)), når man tænker på array-indekser fra (0,0) til (H-1, W-1). Dog er konventionen for (s,t) eller (u,v) i grafiksystemer ofte baseret på et flydende tal i [0,1] området, hvor (0,0) er det ene hjørne og (1,1) det andet, uafhængigt af billedets dimensioner. Standard i mange systemer er, at s-koordinaten løber langs teksturens 'bredde' og t-koordinaten langs teksturens 'højde'.
Selvom du ofte vil bruge hele teksturen, så alle dine teksturkoordinater er mellem 0 og 1, er dette ikke nødvendigt. Du kan vælge kun at bruge en del af teksturbilledet ved at specificere maksimale s- og t-koordinater, der er mindre end 1. Dette er nyttigt, hvis din faktiske billeddata kun fylder en del af den samlede teksturarray (f.eks. hvis teksturarrayen skal have dimensioner, der er potenser af to, mens dit billede ikke er det).
Teksturparametrene kan også være større end 1. Når dette sker, kan du bruge indstillinger til at få teksturen til at gentage sig. Hvis en s-koordinat er f.eks. 1.5, og gentagelse er slået til, vil grafiksystemet typisk bruge texelen ved s-koordinaten 0.5 (1.5 modulo 1). Dette er en effektiv måde at dække store overflader med et lille teksturbillede.
Indstilling af teksturkoordinater
Geometriobjekter i 3D-grafikbiblioteker definerer teksturkoordinater for hvert vertex. Som nævnt ønsker man ikke altid at bruge standard (0,1) teksturkoordinaterne. For eksempel, hvis du kun vil mappe en del af et billede til en overflade, skal du justere disse koordinater.
I mange systemer, herunder Three.js, gemmes teksturkoordinaterne i en egenskab på geometriobjektet. Navngivningen kan variere (s,t eller u,v). Denne egenskab indeholder typisk et array, der afspejler geometrien. For en simpel trekantet flade vil der være tre sæt (u,v) koordinater, ét for hvert vertex. Disse (u,v) koordinater er par af flydende tal, der angiver, hvilket punkt i 2D teksturrummet der svarer til det pågældende vertex i 3D-rummet.
Det kan være lidt ulogisk at modificere disse koordinater manuelt, især fordi standardopførslen i nogle biblioteker kan være at 'flippe' den vertikale teksturparameter (typisk v- eller t-koordinaten). Hvor man intuitivt ville forvente, at (0,0) er nederste venstre og (1,1) er øverste højre i teksturrummet, kan et bibliotek med flippet Y-akse have (0,1) som øverste venstre og (1,0) som nederste venstre. Dette skal man være opmærksom på, når man manuelt indstiller koordinaterne for at sikre, at billedet mappes korrekt og uden at være vendt på hovedet.
Indlæsning af billeder som teksturer
Når teksturen er et billede, der skal indlæses fra en ekstern fil, opstår der en udfordring: indlæsningen tager tid. Selvom det kun er få millisekunder, er det en evighed i forhold til den hastighed, hvormed din kode kører. Hvis du forsøger at rendre scenen umiddelbart efter at have anmodet om billeddata, vil billedet sandsynligvis endnu ikke være indlæst, og overfladen vil fremstå tom eller med en standardfarve.
Løsningen på dette er at bruge en event handler eller en 'callback'-funktion. Dette er en standardmetode i programmering til at håndtere asynkrone operationer. I stedet for at vente på, at billedet er indlæst, fortsætter programmet med at køre. Når billeddataene endelig er ankommet (den 'event' vi venter på), kaldes den funktion, du har specificeret som 'callback'. Denne funktion indeholder så den kode, der skal udføres, når billedet er klar – typisk at oprette teksturen, knytte den til et materiale og rendre scenen (eller den del af scenen, der afhænger af teksturen).
Ved at bruge callbacks sikrer man, at renderingen først sker, når teksturdataene er tilgængelige. Dette er afgørende for at få eksternt indlæste billeder til at fungere korrekt som teksturer i din 3D-grafikapplikation.
Her er en simpel sammenligning af rendering med og uden teksturer:
| Egenskab | Uden Teksturer | Med Teksturer |
|---|---|---|
| Detaljeringsgrad | Lav (afhænger kun af geometri og farve) | Høj (billeddetaljer tilføjes) |
| Geometrisk kompleksitet | Høj (kræver mange polygoner for detaljer) | Lav (detaljer kommer fra billedet, ikke geometrien) |
| Filstørrelse / Data | Primært geometri og farvedata | Geometri, farvedata PLUS billeddata (teksturer) |
| Renderingstid | Afhænger primært af geometrisk kompleksitet | Afhænger af geometrisk kompleksitet OG teksturbehandling (opslag, filtrering) |
| Visuelt udseende | Glatte, simple overflader | Realistiske, detaljerede overflader |
Ofte Stillede Spørgsmål
Hvad er teksturmapping?
Teksturmapping er en computergrafikteknik, der anvender et 2D-billede (en tekstur) på overfladen af en 3D-model for at give den visuelle detalje og realisme uden at øge den geometriske kompleksitet markant.
Hvorfor bruger man teksturmapping?
Man bruger teksturmapping for effektivt at tilføje overfladedetaljer som farve, mønstre og variation til 3D-modeller. Det er langt mere effektivt at 'male' en tekstur på en simpel flade end at modellere hver lille detalje med geometri.
Hvad er s og t koordinater?
s og t (eller u og v) er teksturkoordinater. De er et par af værdier, typisk mellem 0 og 1, der angiver, hvilket punkt i 2D teksturbilledet der skal mappes til et specifikt punkt på 3D-modellens overflade.
Hvordan håndteres asynkron billedindlæsning til teksturer?
Når billeder indlæses fra filer, sker det asynkront. Man bruger event handlers eller callback-funktioner, der først udføres, når billedet er fuldt indlæst. Dette sikrer, at teksturen er klar, før den bruges til rendering.
Opsummering
Vi har set, hvordan teksturmapping revolutionerede computergrafik ved at muliggøre tilføjelsen af rige overfladedetaljer til 3D-modeller på en yderst effektiv måde. Kernen i teknikken er at mappe et 2D-billede (bestående af texels) til en 3D-overflade ved hjælp af teksturkoordinater (s,t eller u,v) for hvert vertex. Processen foregår under rasterisering, hvor grafikkortet bruger interpolering til at bestemme farven for hver pixel på skærmen baseret på den tilsvarende texel. Vi har også berørt vigtige aspekter som teksturrummet, indstilling af koordinater, og hvordan man håndterer asynkron indlæsning af billedfiler for at sikre, at teksturer er klar, når de skal bruges. Teksturmapping er en fundamental teknik, der ligger til grund for meget af den visuelle realisme, vi ser i moderne 3D-grafik, fra spil til film og simuleringer.
Hvis du vil læse andre artikler, der ligner Teksturmapping: Detaljer uden kompleksitet, kan du besøge kategorien Grafik.
