3D software rendering med C++

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

Lys klasse

Indtil nu har vi bare aflæst geometriske farver og renderet en simpel Gauraud shading. Vi vil gøre vores rendering meget mere realistisk ved at tilføje en lyskilde. Den mest enkle lyskilde er en PointLight. Hvis du forestiller dig en nøgen pære der lyser lige kraftig i alle retninger, så vil du havde en god ide om, hvad der menes med PointLight. Den indeholder en position som kan repræsenteres af en vektor. Vi vil også få brug for en farve til vores lyskilde. Faktisk får vi brug for mere end blot én farve. Vi skal i alt bruge tre farver som hedder følgende:

1) Ambient
2) Diffuse
3) Specular

Ambient farven er egenlig snyd. Den findes ikke i den virklige verden men er noget man i sin tid har bestemt der skulle indføres for at opnå en mere realistisk effekt. I den virklige verden vil det oftest aldrig blive helt mørkt. Der vil som regl altid være en lille smule lys der ganske svagt bliver reflekteret rundt. Selv en mørk nat er stort set aldrig helt mørkt. Stjernene og ikke mindst månen vil reflektere lys og vil oplyse ganske svagt. Den virtuelle verden er meget mere simpelt anlagt. Enten er der lys eller også er der ikke noget lys. For at ungå at denne kunstige og urealistisk belysning har man derfor valgt at indføre ambient lyset.

Diffuse farven kan ganske enkelt opfattes som lystes egenlige grundfarve. Det er derfor den mest dominerende farve af dem alle.

Specular farven kan få overflader til at se blanke ud. Kun når lyskilden står i en reflekterende vinkel mod observatøren, som i vores tilfælde er vores kamera eller viewport, vil specular farven være synlig.

Jeg har valgt at lave en speciel farve klasse for vores lyskilde. Den ser således ud:

Fold kodeboks ind/udKode 


Lad os se, hvordan hele vores lys klasse kommer til at se ud:

Fold kodeboks ind/udKode 


For at beregne lysfaldet for en trekant skal vi bruge vertex klassens normal. Vi har én normal defineret for hver vertex. En normal er som regl altid vinkelret mod trekantens flade. Eller med andre ord, så vil normalen stikke ud fra trekantens flade med en vinkel på 90 grader.



For at beregne lysfladen vil man skulle finde vinklen mellem lyskilde og normal. Denne vinkel kan let beregnes ved at tage et prik produkt af normalen og lysretningen. Det vil give et cosinus vinkel. Denne funktion er allerede implementeret i vektor klassen og hedder: Dot, fordi prik produkt på engelsk hedder: dot product. Længden af normalen skal være en enhed lang. Funktionen: Normalize, som også ligger i vektor klassen, vil normalisere en vektor. Det vil sige at den sætter længden på en vektor til præcis 1 enhed. Hvis man ikke husker at normalisere en normal vil man ofte få en uønsket resultat.

Der findes et hav af forskellige materialer og lyskilder i den virklige verden der alle opføre sig på deres helt egen måde. Plastik reflektér lys på en anden måde end f.eks metal. Selv indenfor forskellige metal og plastik arter reflektér lys forskelligt. Når det kommer til stykket så er der ingen der har en korrekt formel på, hvordan lys bliver reflekteret af de forskellige materialer og overflader i forbindelse med computer genereret grafik. Vi kan højst sige at noget ligner eller ikke ligner. Lad os kigge lidt på de mest klassiske reflektions modeller.

Lambert Shading

Lambert reflektions model er grundstenen til langt de fleste reflektions modeller af lys. Den benytter kun ambient og diffuse komponenterne. De fleste reflektions modeller er kun fokuseret omkring specular farven. Her er den matematisk formel for Lambert:



Umiddelbart kan denne formel se lidt skræmmende ud, hvis man ikke er så matematisk stærk, men den er faktisk ikke så slem at oversætte til kode. Vi vil lave en funktion i vores renderings klasse som vi kalder for Lambert:

Fold kodeboks ind/udKode 


Som du kan se returner funktionen en lysfarve klasse. Denne funktion vil blive kaldt fra ShadePixel funktion så lad os tilpasse den funktion.

Fold kodeboks ind/udKode 


Lad os tilføje lyskilden til vores lille scene.

Fold kodeboks ind/udKode 


Glem ikke at definer lyskilden: PointLight, i renderings klassen! Siden prototypen er blevet udvidet med en ekstra parameter, skal vores nye lyskilde tilføjes til kommandoen i funtionen: RenderImage. Renderingen skulle nu gerne se således ud:




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