3D software rendering med C++

Tags:    c++
Skrevet af Bruger #1474 @ 11.08.2008

TGA Klasse

Vores lille program vil blive en simpel konsol. Konsol programmer har ingen grafisk brugerflade og kan derfor kun vise tekst. Det gør heldigvis ikke noget, for vi vil gemme de renderede pixels i et billedformat og til sidst gemme billedet til harddisken. Billedet kan derefter vises i et billedbehandligsprogram. Det er ganske almindelig at bruge konsol programmer når man skriver renderings software, da det giver den mulighed at man kan forstørre billedet for at studere nærmere detaljer. Vi vil derfor skrive en klasse der kan både gemme og indlæse et kendt billedformat. Indlæsningen vil blive brugt til senere brug, når vi vil lægge tekstur (texture) oven på vores 3D objekter. Der findes mange forskellige billedformater som sagtens kan anvendes til dette formål. Jeg har valgt at bruge TGA formatet, fordi dette er en ukomprimeret format der kan behandles på alle platforme.

TGA klassen minder meget om mange andre klasser af samme art. Den kan som sagt indlæse en TGA fil fra harddisken og skrive til en TGA fil. Der er dog et par små funktioner der er tilføjet. Alle pixels i et digitalt billede bliver normalt gemt i bytes. Det vil sige, at farve skalaen for et billede vil ligge fraogmed 0 tilogmed 255. Når vi beregner farver i forbindelse med en rendering, vil vi gerne bruge en større og mere nøjagtig skala. Vi vil også gerne havde muligheden for at gå ud over den almindelige farve skala. Det færdige resultat vil dog tilsidst blive gemt i bytes. Da komponenterne X, Y, Z og W i vores vektor klasse er af float typen: double, hvorfor så ikke bruge samme type for vores farver! Vi vil derfor gerne kunne tilskrive og aflæse pixels i TGA klassen, som om de var af float typen: double. Vi kunne sagtens gøre dette udenfor klassen, men for at undgå at havde samme kode skrevet mange forskellige steder, skriver vi det ind i vores klasse.

Der er en anden funktion i TGA klassen der er forskellig fra en almindelig billede klasse. Vi har muligheden for at aflæse en pixel ved hjælp af UVW eller tekstur (texture) koordinater. Normalt vil man aflæse en pixel ved at angive indekset for den ønskede pixel eller ved at angine X og Y koordinaterne. Vi vil få brug for denne funktion når vi vil skal til at rendere tekstur (texture) på vores geometri. Funktionen hedder GetPixel og er en overload til de mere almindelige funktioner af samme navn. TGA Klassen vil komme til at se således ud:

Fold kodeboks ind/udKode 


Som du sikkert har bemærket bruger TGA klassen en anden klasse ved navn: RGBA. Denne klasse er en simpel klasse med komponenterne: R, G, B og A. De er alle af float typen: double. Denne klasse vil repræsentere vores farver. Der er en lille funktion i klassen der hedder: Clamp. Den har to parameterer: Min og Max. Med denne funktion kan farve komponenterne afskæres til et minimum og maksimum værdi. I de fleste tilfælde vil vi bruge en skala mellem 0 og 1. Her er klassen:

Fold kodeboks ind/udKode 


Geometri Klasse

Denne klasse vil holde alt data for vores geometri eller 3D objekter. Som nævnt tidligere er 3D objekter som regel bygget op af trekanter. Det vil vores 3D objekter derfor også være. Men der er forskellige måder at definere trekanter på. Det kommer helt an på, hvordan man ønsker at komprimere dataerne. Denne artkel vil ikke bruge komprimeringsmodeller. For at gøre denne artikel så simpel som muligt vil vi kun anvende ukomprimeret data. Klassen indeholder en array af vertex'er, hvoraf tre vertex'er vil definere en trekant.

En vertex indeholder følgende data:
1) Geometrisk koordinater
2) En farve
3) Tekstur (texture) koordinater
4) En normal

Den geometriske koordinat bliver repræsenteret af en vektor.
Farven er repræsenteret af en RGBA klasse.
Tekstur (texture) koordinaterne vil blive repræsenteret af en ny klasse som indeholder komponenterne U,V og W. Tidligere nævnte jeg at vi ikke vil komme til at bruge W komponenten men for en ordens skyld bør vi nok tilføje den. Denne klasse har ikke nogle nævneværdige funktioner og vil derfor komme til at se således ud:

Fold kodeboks ind/udKode 


Nomalen for vores vertex vil vi komme til at bruge når vi skal beregne lysfaldet for vores trekanter. Den bruger kun tre komponenter X, Yog Z. Af nemheds skyld vil vi derfor definer den som en almindelig vektor trods vi aldrig kommer til at bruge W komponenten. Det vil være nemmest at lave en lille vertex klasse der indeholder disse klasser:

Fold kodeboks ind/udKode 


Geometri klassen har et par nævneværdige funktioner: InterpolateVertexColors, InterpolateTextureCoordinates og InterpolateNormals. De alle har fire ens parametere. Den første parameter forventer en skæringsvektor og de sidste forventer tre vertex'er. Funktionen: InterpolateVertexColor, vil finde den farve som ligger tættest på skæreringspunktet ved at blande de tre vertex farver afhængigt af vektorens barycentriske koordinater. Resultatet vil blive returneret i en farve klasse. De to andre funktioner gør nøjagtig det samme dog bare med trekantens henholdsvis tekstur (texture) koordinater og normaler. Sidst i klassen er der en stribe funktioner der hver kan lave en geometrisk objekt så som en kasse og en cylinder. Af hensyn til denne artikels længde vil vi ikke komme nærmere indpå, hvordan algoritmerne i disse funktioner virker. Geometri klassen ser således ud:

Fold kodeboks ind/udKode 



Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (26)

User
Bruger #13520 @ 01.09.08 21:15
coolt
User
Bruger #12871 @ 13.09.08 13:32
Wow!
Det er ikke hverdag man falder over en så gennemført tutorial. Du forklarer tingene rigtig godt og detaljeret uden at du forventer at folk er indforstået med hvad det lige er du mener. Jeg har arbejdet med 3D før i 3D's MAX og har tit tænkt på hvordan man kunne programmere sådan noget selv. Så du skal virkelig have mange tak for at vise mig hvordan man evt. kunne gøre det.
Keep up the good work :D
5/5 point
P.S. Lige et lille spørgsmål... Hvor har du alt din viden fra?
User
Bruger #1474 @ 17.09.08 14:12
Mange tak for jeres kommentar.

Jeg har min viden fra forskellige bøger, websites og selvfølgeligt mit universitet. Jeg er netop blevet færdig som: Computer Spil Programmør på et Engelsk universitet. Da jeg altid har arbejdet med real-time rendering, har jeg i et stykke tid gået og fået lyst til at prøve software rendering. Håber at I kan drage nytte af min artikel.
User
Bruger #12364 @ 06.10.08 11:38
Genialt! Det var lige hvad jeg havde brug for!
User
Bruger #14855 @ 21.04.09 19:29
Søren du laver generalt DE BEDSTE artikler!
User
Bruger #11164 @ 07.05.09 01:31
Jeg har et forslag til din vektor klasse. Jeg benytter operator overloading til ting som at addere, subtrahere og skalarproduktet. Jeg synes det gør brugen af vektorer meget mere overskuelig :) Ellers thumbs up.
User
Bruger #1474 @ 07.05.09 12:56
Jeg plejer også at bruge operator overloading i mine vektor klasser men jeg ville skrive C++ koden så den let kunne sammenlignes med Delphi koden (Se den samme artikel for Delphi: http://www.udvikleren.dk/Delphi/Article.aspx/319/ ). Operator overloading er en forholdsvis ny feature i Delphi derfor har jeg ikke inkluderet den i Delphi versionen. I tilfælde af at en C++ programmør er interesseret i at lære Delphi (eller modsat) eller bare er interesseret i at se forskellen mellem de to sprog burde disse artikler være et godt udgangspunkt.


Mange tak for jeres kommentar. Hold jer endelig ikke tilbage hvis I har forslag, kritik eller ros :)


PS: Glem nu ikke rate mine artikler :P
User
Bruger #15008 @ 24.05.09 21:27
Dejligt at kunne læse noget på dansk ;-) Super godt skrevet.
User
Bruger #14855 @ 17.06.09 15:32
Hmm, god artikel, men ringe du har lavet PRÆCIS den samme artikel, bare med C++!
User
Bruger #14855 @ 17.06.09 15:33
Undskyld men Delphi!
User
Bruger #14855 @ 17.06.09 15:33
*Mener
User
Bruger #1474 @ 17.06.09 17:10
Jonas:: Hvorfor ser du det som et negativt ting at have en artikel der viser implementationen i flere sprog? Skal Delphi udviklere ikke havde chancen for at implementere denne artikel? :)

Mange tak for rosen.
Jeg modtager gerne ris såvel som ros :)
User
Bruger #14855 @ 19.06.09 23:29
Mener bare at du har lavet 2 af de samme artikler og fået dobblet point for det, du kunne bare ha' lavet det hvor artikel navnet var: "3D software rendering med C++ og Delphi" :) Men synes det er ret mærkeligt :)
User
Bruger #1474 @ 20.06.09 19:10
Hvorfor skulle jeg blande to sprog sammen når de er delt i to adskilte teknologier på dette forum? I så fald hvilken udviklings teknologi skulle jeg så poste dem under? :) Er det fordi du mener at jeg har fået for mange points for de artikler? :)
User
Bruger #1474 @ 20.06.09 20:00
Hvorfor skulle jeg blande to sprog sammen når de er delt i to adskilte teknologier på dette forum? I så fald hvilken udviklings teknologi skulle jeg så poste dem under? :) Er det fordi du mener at jeg har fået for mange points for de artikler? :)
User
Bruger #14855 @ 21.06.09 21:00
Jep :)
User
Bruger #1474 @ 21.06.09 23:01
Jamen så må du jo snakke med de admins der har ansvar for artikeler her på forummet og tage problemstilling op hos dem. Det kan jeg næsten ikke gøre noget ved. Håber trods alt du kan bruge mine artikler til noget.
User
Bruger #14855 @ 22.06.09 14:39
Det kan jeg da SIKKERT! :) Artiklen er meget god :) Men synes bare det er urætfærdigt at man får Up for begge :)
User
Bruger #14103 @ 01.08.09 17:13
Jonas:

De der UP, er det ikke bare nogle tal, og så ikke mere i det?:)
Altså jeg laver altid Forumindlæg uden point..:)
User
Bruger #13937 @ 15.09.09 17:39
Hey. Virkelig gennemført beskrivelse, og tror bestemt at jeg skal ha undersøgt mere på emnet.

Anyway. Har et par spørgsmål:

Den første kodestump på s. 4, som starter således: "//Den officelle TGA fil header!". Skal den gemmes som TGA.h? For hvis det er tilfældet så prøver den jo på at include sig selv :S

Og kan det her på udvikleren lade sig gøre at hente de filer som bliver brugt/lavet i artiklerne? For det ville da være en fordel hvis man sidder ligesom mig og roder rundt i hvordan det hele skal sidde sammen så man har en virkende bund at starte på, og at man ved fejl evt. ved fejl ved at det ikke er koden, men noget setup :)

Og til sidst. Dette burdet ikke have noget problem med at compile på en ubuntu-installation?

mvh. martin :)
User
Bruger #1474 @ 15.09.09 20:21
Hej Martin,

Stukturen som jeg har kaldt for: FILEHEADER, er den officielle TGA fil header. Den skal ligge øverst i header filen TGA.h således den kan genkendes af TGA klassen. Du kan godt lægge den i en separat header fil, hvis du har lyst til det, men så skal den selvfølgelig inkluderes i TGA header filen inden TGA klassen defineres. Jeg vil nok anbefale at lægge den inden i selve TGA klassen da denne TGA fil format kun benyttes af TGA klassen! Med andre ord så er alt koden i den refererede tekst boks implementeringen af hele TGA klassen og kan skrives i en header fil eller en header med tilhørende cpp fil.

Angående kilde kode, du kan sende mig en udvikler post her på forummet med din mail adresse. Så vil jeg sende projektet til dig i en zip fil.

Alle andre er selvfølgelig også velkommen til at sende mig deres mail adresse.

Projektet skulle være platforms uafhængig. Der bliver ikke brugt nogle operative kommandoer eller API'er af nogen form, så ja, hvis din kompiler ellers kan kompile ganske almindelige console applikationer, så burde du også kunne kompilere dette projekt. Lad mig vide hvis du støder ind i nogle problemer.

Søren Klit Lambæk
User
Bruger #1927 @ 19.02.10 12:08
Rigtig god artikel.

Du har dog glemt at fortælle at man skal huske at ændre RenderImage til at bruge den nye ShadePixel()

Fold kodeboks ind/udKode 




Og Phong laver compile errors

Da du her
Fold kodeboks ind/udKode 


parser V1, V2 og V3 til InterpolateVertexColors funktionen. Den ta'r vectors, men V1, V2 og V3 er vertexes.
User
Bruger #1927 @ 19.02.10 12:10
Eller... Nu bliver jeg forvirret.

Fold kodeboks ind/udKode 
User
Bruger #1927 @ 19.02.10 12:12
Nej, okay. er omvendt. Funktionen forventer vertexes, men V1, V2 og V3 er vectors.

Hvordan den være?
User
Bruger #1927 @ 19.02.10 12:13
Denne side er da håbløst bugged.

Fold kodeboks ind/udKode 
User
Bruger #1474 @ 22.02.10 10:21
Hej Morten, jeg er ikke helt med paa hvad du mener. Baade InterpolateNormals() og InterpolateColors() tager vertexer (Vertices). Kan du ikke give mig noget mere sammenhaengene kode saa jeg kan se mere detaljeret hvad du goer. Tak :)
Du skal være logget ind for at skrive en kommentar.
t