10
Tags:
delphi
Skrevet af
Bruger #1474
@ 11.08.2008
Lys klasseIndtil 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:
type
TLIGHTCOLOR = class
Ambient : TRGBA;
Diffuse : TRGBA;
Specular : TRGBA;
constructor Create;
destructor Free;
end;
implementation
constructor TLIGHTCOLOR.Create;
begin
//Lav en standard opsætning af farver
Ambient := TRGBA.Create( 0.025, 0.05, 0.1, 1.0 );
Diffuse := TRGBA.Create( 0.8, 0.8, 0.8, 1.0 );
Specular := TRGBA.Create( 1.0, 1.0, 1.0, 1.0 );
end;
destructor TLIGHTCOLOR.Free;
begin
Ambient.Free;
Diffuse.Free;
Specular.Free;
end;
Lad os se, hvordan hele vores lys klasse kommer til at se ud:
type
TLIGHT = class
Position : TVECTOR;
Color : TLIGHTCOLOR;
constructor Create; overload;
constructor Create( const X, Y, Z : Double ); overload;
destructor Free;
end;
implementation
constructor TLIGHT.Create;
begin
Color := TLIGHTCOLOR.Create;
Position := TVECTOR.Create;
end;
constructor TLIGHT.Create( const X, Y, Z : Double );
begin
Color := TLIGHTCOLOR.Create;
Position := TVECTOR.Create( X, Y, Z, 1 );
end;
destructor TLIGHT.Free;
begin
Color.Free;
Position.Free;
end;
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 ShadingLambert 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:
//Denne funktion vil beregne Lambert reflektions model
function TRENDER.Lambert( RayNear, RayFar, Ray : TVECTOR; V1, V2, V3 : TVERTEX; Geometry : TGeometry; Light : TLIGHT ) : TLIGHTCOLOR;
var
N, V, L : TVECTOR;
Diffuse : Double;
begin
//Interpolerer vertex normalerne for vores trekant
N := Geometry.InterpolateNormals( Ray, V1, V2, V3 );
N.Normalize;
//Find vores fragment
V := TVECTOR.Create;
V.FollowLine( RayNear, RayFar, Ray.Z );
//Find vores lys vektor og normalisér den
L := Light.Position.Sub( V );
L.Normalize;
//Find intensiteten ved at tage prik produktet af vores lys vektor og normal
Diffuse := L.Dot( N );
//Gem Diffuse farven og lad Specular være nul
Result := TLIGHTCOLOR.Create;
Result.Diffuse.SetValues( Diffuse * Light.Color.Diffuse.R, Diffuse * Light.Color.Diffuse.G, Diffuse * Light.Color.Diffuse.B );
Result.Specular.SetValues( 0, 0, 0 );
//Husk at afklippe vores farve værdier mellem 0 og 1
Result.Diffuse.Clamp( 0, 1 );
//Frigør middlertidige klasser fra hukommelsen!
N.Free;
V.Free;
L.Free;
end;
Som du kan se returner funktionen en lysfarve klasse. Denne funktion vil blive kaldt fra ShadePixel funktion så lad os tilpasse den funktion.
//Denne funktion vil blive kaldt for hver gang en fragment er blevet fundet!
function TRENDER.ShadePixel( RayNear, RayFar, Ray : TVECTOR; V1, V2, V3 : TVERTEX; Geometry : TGEOMETRY; Texture : TTGAFILE; Light : TLIGHT ) : TRGBA;
var
VertexColor, TextureColor : TRGBA;
LightColor : TLIGHTCOLOR;
Coord : TUVW;
begin
//Interpoler vertex farverne
VertexColor := Geometry.InterpolateVertexColors( Ray, V1, V2, V3 );
VertexColor.Clamp( 0, 1 );
//Interpoler tekstur koordinaterne
Coord := Geometry.InterpolateTextureCoordinates( Ray, V1, V2, V3 );
TextureColor := Texture.GetPixel( Coord, False );
//Beregn Lambert reflektions model
LightColor := Lambert( RayNear, RayFar, Ray, V1, V2, V3, Geometry, Light );
//Bland dem sammen og returner farven
Result := TRGBA.Create( Light.Color.Ambient.R * VertexColor.R * TextureColor.R + VertexColor.R * TextureColor.R * LightColor.Diffuse.R + LightColor.Specular.R,
Light.Color.Ambient.G * VertexColor.G * TextureColor.G + VertexColor.G * TextureColor.G * LightColor.Diffuse.G + LightColor.Specular.G,
Light.Color.Ambient.B * VertexColor.B * TextureColor.B + VertexColor.B * TextureColor.B * LightColor.Diffuse.B + LightColor.Specular.B, 1 );
//Husk at afklippe vores farve værdier mellem 0 og 1
Result.Clamp( 0, 1 );
//Frigør midlertidige klasser fra hukommelsen!
VertexColor.Free;
Coord.Free;
TextureColor.Free;
LightColor.Free;
end;
Lad os tilføje lyskilden til vores lille scene.
constructor TRENDER.Create;
begin
GeometryCount := 3;
//Lav en cylinder
GeometryList[ 0 ] := TGEOMETRY.Create;
GeometryList[ 0 ].CreateCylinder( 20, 1, 3, 1, -1, -1, -22 );
//Lav en kasse
GeometryList[ 1 ] := TGEOMETRY.Create;
GeometryList[ 1 ].CreateBox( 2, 2, 2, +3, -1.5, -30 );
//Lav en plan flade
GeometryList[ 2 ] := TGEOMETRY.Create;
GeometryList[ 2 ].CreatePlane( 13, 13, 0, -2.5, -27 );
//Indlæs teksturen for vores trekant
Texture := TTGAFILE.Create;
Texture.Load( 'Test.tga' );
//Lav en rødlig lyskilde
PointLight := TLIGHT.Create( 18, 10, 10 );
PointLight.Color.Diffuse.SetValues( 1, 0.75, 0.5 );
end;
Glem ikke at definer lyskilden i renderings klassen samt at frigøre den fra hukommelsen i klassens destruktion.
destructor TRENDER.Free;
var
I : Integer;
begin
//Frigør alle vores geometri klasser fra hukommelsen!
for I := 0 to GeometryCount - 1 do
GeometryList[ I ].Free;
//Frigør vores lysklasse fra hukommelsen!
PointLight.Free;
end;
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 (2)
Hmm, god artikel, men ringe du har lavet PRÆCIS den samme artikel, bare med C++!
Koden er jo ikke den samme!
Du skal være
logget ind for at skrive en kommentar.