Sunteți pe pagina 1din 18

Introducere

Textarea unui obiect este procesul de mapare a unei imagini 2D (sau, uneori, 3D)
pe un model 3D, astfel încât să puteți obține detalii mult mai detaliate în model
fără a utiliza o mulțime de vârfuri. Fără texturi, singura modalitate de a obține
nivelul de detaliere care poate fi obținută prin folosirea cartografierii texturilor ar fi
specificarea unui vârf colorat diferit pentru fiecare pixel al ecranului. După cum vă
puteți imagina, adăugarea detaliilor unui model în acest mod poate fi foarte
ineficientă.
Lumina este necesară pentru a seta starea de spirit pentru o scenă. De asemenea,
iluminarea furnizează un indiciu despre direcția și forma unui obiect. Cu utilizarea
corectă a iluminatului putem face o scenă întunecată și sumbră sau luminoasă și
veselă.
Materialele sunt folosite pentru a determina modul în care lumina interacționează
cu suprafața modelului. Un model poate apărea foarte strălucitor sau foarte
plicticos în funcție de proprietățile materiale ale modelului.
În acest articol, voi descrie modul de aplicare a texturării, iluminării și materialelor
utilizând OpenGL și GLSL.

Pentru acest articol, mă voi baza pe mai multe biblioteci ale părților terțe pentru a
simplifica programarea.
 Gratuit OpenGL Utility Toolkit (FreeGLUT 2.8.1) : FreeGLUT este o
alternativă open-source la biblioteca OpenGL Utility Toolkit (GLUT). Scopul
librăriei GLUT este de a furniza funcționalitate fereastră, tastatură și mouse în
mod independent de platformă.
 OpenGL Extension Wrangler (GLEW 1.10.0) : Biblioteca OpenGL Extension
Wrangler oferă accesibilitatea la nucleul OpenGL și extensiile care nu sunt
disponibile în fișierele și bibliotecile de antet OpenGL furnizate de
compilatorul dvs.
 Biblioteca OpenGL Math (GLM 0.9.5) : Biblioteca matematică OpenGL este
scrisă pentru a fi în concordanță cu primitivele matematice furnizate de
limbajul de umbrire GLSL. Acest lucru face ca funcțiile de scriere a
matematicii în C ++ să fie foarte asemănătoare cu funcțiile de matematică pe
care le veți vedea în codul shader GLSL.
 SOIL : Biblioteca simplă OpenGL Image este cea mai ușoară cale pe care o
cunosc în prezent pentru a încărca texturi în OpenGL. Cu o singură metodă,
puteți încărca texturi și obține un obiect textura OpenGL. Puteți obține cea
mai recentă versiune de SOIL aici: http://www.lonesock.net/soil.html
Încărcarea texturilor
Deoarece există atât de multe formate de imagine diferite, nu voi intra în detaliu
despre cum să încărcați texturile în memorie. În schimb, mă voi lipi de biblioteca
simplă de imagini OpenGL ( SOIL ) pentru încărcarea texturilor în memoria
grafică. SOIL este o librărie liberă de imagini open source care poate încărca
imagini din mai multe formate de imagine. Dacă utilizați context OpenGL 3.0 sau
mai avansat compatibil, asigurați-vă că descărcați versiunea SOIL pe care o
furnizez în secțiunea Dependență de mai sus, altfel veți întâlni probleme atunci
când SOIL încearcă să utilizeze o funcție OpenGL 1.x care nu este disponibilă în
OpenGL 3.0 și peste.
Pentru a încărca o textură utilizând SOIL necesită un cod foarte mic:

GLuint textureObject = 0;
textureObject = SOIL_load_OGL_texture( "path/to/file.JPG",
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS );

Înainte de a începe cu crearea unei aplicații pentru a interacționa cu shaderele, să


definim mai întâi un shader de vârfuri și un shader de fragmente pe care le putem
folosi în aplicația noastră.

Mai intai vom defini shaderul de vertex care va fi folosit pentru a transforma
nodurile geometriei noastre in spatiu clip astfel incat acestea sa poata fi consumate
de rasterizator.
Funcțiile de iluminare vor fi definite în shader-ul fragmentului mai târziu. Un lucru
important este să ne amintim că pentru a realiza o iluminare adecvată, toate
vectorii și pozițiile trebuie să fie în același "spațiu". Spațiul în care doriți să
efectuați iluminarea este alegerea dvs., dar ar trebui să fie consecventă. Pentru
această demonstrație, am ales să calculam iluminatul în spațiul mondial, dar este
posibil și realizarea iluminării în spațiu-obiect, spațiu luminos sau spațiu de
vizualizare.
Pentru a realiza iluminarea în spațiul mondial, trebuie să transformați poziția
vârfului și vertexul normal într-un spațiu mondial în shaderul de vârf și să treci
atributele de spațiu mondial la shader-ul fragmentului. În plus, trebuie să vă
asigurați că vă asigurați poziția ochiului (poziția camerei) și poziția luminii în
spațiul mondial în shader-ul fragmentului.
Mai întâi, să aruncăm o privire la shaderul de vârfuri:
1 #version 330 core

3 layout(location=0) in vec3 in_position;

4 layout(location=2) in vec3 in_normal;

5 layout(location=8) in vec2 in_texcoord;

7 out vec4 v2f_positionW; // Position in world space.

8 out vec4 v2f_normalW; // Surface normal in world space.

9 out vec2 v2f_texcoord;

10
11 // Model, View, Projection matrix.

12 uniform mat4 ModelViewProjectionMatrix;

13 uniform mat4 ModelMatrix;

14

15 void main()

16 {

17 gl_Position = ModelViewProjectionMatrix * vec4(in_position, 1);

18

19 v2f_positionW = ModelMatrix * vec4(in_position, 1);

20 v2f_normalW = ModelMatrix * vec4(in_normal, 0);

21 v2f_texcoord = in_texcoord;

22 }

Coordonarea texturii nu este utilizată pentru calculele de iluminare, astfel încât este pur și simplu trecută ca fiind la
shader-ul fragmentului.

FRAGMENT SHADER
Shaderul fragmentului este puțin mai complicat. Acceptă atributele vârfului de la
shaderul de vârf în plus față de variabilele uniforme care sunt folosite pentru a
defini proprietățile luminoase și materiale care vor fi folosite pentru a lumina
corect obiectul nostru.
Mai întâi, să aruncăm o privire la variabilele de intrare.
1 #version 330 core

3 in vec4 v2f_positionW; // Position in world space.

4 in vec4 v2f_normalW; // Surface normal in world space.

5 in vec2 v2f_texcoord;

7 uniform vec4 EyePosW; // Eye position in world space.

8 uniform vec4 LightPosW; // Light's position in world space.

9 uniform vec4 LightColor; // Light's diffuse and specular contribution.

10
11 uniform vec4 MaterialEmissive;

12 uniform vec4 MaterialDiffuse;

13 uniform vec4 MaterialSpecular;

14 uniform float MaterialShininess;

15

16 uniform vec4 Ambient; // Global ambient contribution.

17

18 uniform sampler2D diffuseSampler;

19

layout (location=0) out vec4 out_color;

Primele variabile de intrare de pe liniile 3-5 sunt transferate de la shader-ul de vârf


la shader-ul fragmentului.
Pe linia 7, poziția ochiului spațial din lume este atribuită variabilei
uniforme EyePosW de către aplicație. Aceasta este pur și simplu poziția camerei în
spațiul mondial.
Pentru lumini de punct, trebuie să cunoaștem poziția spațială a lumii sursei de
lumină. Această valoare este stocată în variabila uniformă LightPosW .
Pentru simplitate, vom presupune că contribuția difuză și contribuția speculară a
sursei de lumină sunt aceleași și vom stoca aceste valori într-o singură variabilă
uniformă numită LightColor .
Pe liniile 11-14, proprietățile materialului sunt definite. Pentru acest shader, vom
defini proprietățile materialului emisiv , difuz , specular și strălucitor .
Pentru simplitate, definim doar un singur termen ambiental global. Pentru propriile
shadere de iluminat, puteți alege să separați valorile ambientale globale, per lumină
și materiale.
Pe linia 18, un singur eșantionator uniform este folosit pentru a prelua un texel din
textura primară care este asociată cu obiectul redat.
Pe linia 20, definim singurul parametru de ieșire de la shaderul nostru. Culoarea
finală a fragmentelor care este mapată la primul buffer de culoare în cadrul
framebuffer activ (există doar un buffer tampon de culoare în cadrul framebuffer
implicit, astfel încât să putem omite specificația layout-ului în acest caz).
Acum, să vedem cum implementăm ecuația de iluminare pentru fiecare fragment.
22 void main()

23 {

24 // Compute the emissive term.

25 vec4 Emissive = MaterialEmissive;

26

27 // Compute the diffuse term.


28 vec4 N = normalize( v2f_normalW );

29 vec4 L = normalize( LightPosW - v2f_positionW );

30 float NdotL = max( dot( N, L ), 0 );

31 vec4 Diffuse = NdotL * LightColor * MaterialDiffuse;

32

33 // Compute the specular term.

34 vec4 V = normalize( EyePosW - v2f_positionW );

35 vec4 H = normalize( L + V );

36 vec4 R = reflect( -L, N );

37 float RdotV = max( dot( R, V ), 0 );

38 float NdotH = max( dot( N, H ), 0 );

39 vec4 Specular = pow( RdotV, MaterialShininess ) * LightColor * MaterialSpecular;

40

41 out_color = ( Emissive + Ambient + Diffuse + Specular ) * texture( diffuseSampler, v2f_texcoord );

42 }

Pe linia 25, contribuția de emisie a materialelor este atribuită pur și


simplu variabilei locale Emissive . Deoarece termenul emisiv pur și simplu adaugă
iluminarea atunci când altfel nu există nici un fel de iluminare, termenul emisiv nu
este afectat de poziția luminii sau de poziția spectatorului.
Apoi, contribuția difuză este calculată. Contribuția difuză este mai strălucitoare
atunci când suprafața normală N și vectorul de lumină L îndreaptă în aceeași
direcție (paralelă) și în mod constant coboară, deoarece unghiul dintre acești doi
vectori crește. Aceasta este determinată de unghiul cosinus ( NdotL ) între acești
doi vectori. Dacă unghiul dintre ele este 0, unghiul cosinusului este 1 (cel mai
strălucitor). Pe măsură ce unghiul dintre ele crește, unghiul cosinusului scade până
când cei doi vectori sunt ortogonali, caz în care unghiul cosinus dintre ei devine 0
și nu ar trebui să apară nicio contribuție de iluminare. Dacă N și Lsunt orientate în
direcții opuse, atunci unghiul cosinus devine negativ, dar deoarece nu vrem să
avem culori negative, pur și simplu strângem unghiul cosinusului la 0
folosind funcția max .
În secțiunea următoare, termenul specular este calculat. În acest program de
fragmente, computez contribuția speculară utilizând modelul de iluminare
Phong și folosind modelul Blinn-Phong . Pentru a vedea diferența dintre aceste
două modele de iluminare, înlocuiți RdotV pe linia 39 cu NdotH .
Pe linia 41, culoarea finală a fragmentului este calculată prin însumarea
contribuțiilor de iluminare și modularea acesteia prin culoarea texturii
obiectului. Culoarea texturii este extrasă din textură utilizând funcția
de textură specială . Primul parametru pentru această funcție este proba care este
asociată cu unitatea de textură pe care textura obiectului este legată. Al doilea
parametru este coordonatele texturii care sunt transmise direct de la shaderul de
vârfuri.
Acest shader calculează numai contribuția la iluminare a unei singure surse de
lumină. Dacă doriți să definiți mai multe lumini, puteți să creați pur și simplu o
buclă în jurul calculelor de iluminare difuze și speculare și să schimbați variabilele
uniforme LightPosW și LightColor ca array. Valorile
finale Diffuse și Specularvor fi suma
tuturor contribuțiilor difuze și speculare pentru toate luminile din scenă.
Apoi, vom vedea cum putem crea o aplicație care poate face o scenă folosind
aceste shadere.
Inițializați GLUT și GLEW
Similar cu articolul precedent intitulat [Introducere în OpenGL și GLSL] , voi
folosi FreeGLUT și GLEW pentru a inițializa contextul și extensiile
OpenGL. Consultați articolul respectiv pentru detalii complete despre aceste
funcții.
Încărcarea texturilor
Vom folosi biblioteca SOIL pentru a încărca texturile folosite de această
demonstrație. Vom specifica și câteva proprietăți de textura după încărcarea
texturii.
Dacă utilizați Visual Studio 2012 și un context compatibil înainte, asigurați-vă că
utilizați versiunea mea SOIL și nu cea de pe site-ul SOIL.
main.cpp

233 GLuint LoadTexture( const std::string& file )

234 {

235 GLuint textureID = SOIL_load_OGL_texture( file.c_str(), SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOI

236 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );

237 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

238 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );

239 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

240 glBindTexture( GL_TEXTURE_2D, 0 );

241

242 return textureID;

243 }

Metoda SOIL_load_OGL_texture poate fi utilizată pentru a încărca o textură și a


genera un obiect textura OpenGL care poate fi apoi utilizat pentru a textura
obiectele din scena noastră.
De asemenea, dorim să specificăm modul de filtrare a texturii
la GL_LINEAR_MIPMAP_LINEAR pentru GL_TEXTURE_MIN_FILTER
GL_LINEAR pentru GL_TEXTURE_MAG_FILTER . Am
setat GL_TEXTURE_WRAP_ * la GL_REPEAT pentru a evita cusăturile care
apar în obiectul nostru.
Pe linia 240, ID-ul de obiect textura implicit de 0 este legat de țintă de
textură GL_TEXTURE_2D și astfel nelegând orice obiect textura legat anterior.

Crearea unei sfere

Deoarece toate obiectele din scenă (pământul, luna și soarele) pot fi reprezentate de
o sferă, vom genera o singură sferă utilizând un obiect Vertex Array și
îl vom folosi pentru a reda fiecare obiect din scenă.
Voi crea o sferă procedurală care conține coordonatele texturii și normele
vertexului necesare pentru a face obiectele cu iluminare și textura.
Voi descrie această funcție în trei părți:
1. Configurarea atributelor vertex
2. Configurarea tamponului index
3. Configurarea obiectului Array de vârf

ATRIBUTELE VERTEXELOR
Mai întâi vom configura atributele de vârf pentru sferă. Fiecare vârf al sferei va
avea trei atribute: poziția , coordonatele normale și textura . Sfera va fi centrată
la origine (0, 0, 0) și va avea o anumită rază. Numărul de segmente de-a lungul
axei principale a sferei este determinat de parametrul stivei, iar numărul de
segmente în jurul circumferinței fiecărui coș este determinat
de parametrul secțiunilor .
O vizualizare în sârmă a sferei noastre poate arăta ca imaginea de mai jos.
Axa principală a sferei merge de la un pol la celălalt. Stivele traversează axa principală, iar felii se deplasează în
jurul stivei.
main.cpp

245 GLuint SolidSphere( float radius, int slices, int stacks )

246 {

247 using namespace glm;

248 using namespace std;

249

250 const float pi = 3.1415926535897932384626433832795f;

251 const float _2pi = 2.0f * pi;

252

253 vector<vec3> positions;

254 vector<vec3> normals;

255 vector<vec2> textureCoords;


256

257 for( int i = 0; i <= stacks; ++i )

258 {

259 // V texture coordinate.

260 float V = i / (float)stacks;

261 float phi = V * pi;

262

263 for ( int j = 0; j <= slices; ++j )

264 {

265 // U texture coordinate.

266 float U = j / (float)slices;

267 float theta = U * _2pi;

268

269 float X = cos(theta) * sin(phi);

270 float Y = cos(phi);

271 float Z = sin(theta) * sin(phi);

272

273 positions.push_back( vec3( X, Y, Z) * radius );

274 normals.push_back( vec3(X, Y, Z) );

275 textureCoords.push_back( vec2(U, V) );

276 }

277 }

În această funcție, axa Y locală a sferei este axa principală. Dacă doriți să utilizați
o axă diferită ca axă principală, schimbați pur și simplu componenta Y cu
componenta axei principale (de exemplu componenta Z).

TAMPON DE INDICE
Acum, avem atributele vertex pentru sfera noastră, dar nu putem să renderăm pur și
simplu vertexele în această ordine (dacă nu dorim doar să renderăm puncte)
deoarece trebuie să trecem geometria sferică la GPU folosind triunghiuri, trebuie
să construim un buffer tampon care să definească ordinea în care trebuie trimise
nodurile la GPU. De asemenea, trebuie să ne asigurăm că ordonarea vârfurilor din
fiecare triunghi este înfășurată corect folosind o ordine de înfășurare în sensul
invers acelor de ceasornic, astfel încât partea exterioară a sferei să nu fie distrusă
atunci când se utilizează sacrificarea din spate.
main.cpp

279 // Now generate the index buffer

280 vector<GLuint> indicies;

281

282 for( int i = 0; i < slices * stacks + slices; ++i )

283 {

284 indicies.push_back( i );

285 indicies.push_back( i + slices + 1 );

286 indicies.push_back( i + slices );

287

288 indicies.push_back( i + slices + 1 );

289 indicies.push_back( i );

290 indicies.push_back( i + 1 );

291 }

Fiecare față a sferei este formată din două triunghiuri. Un triunghi superior și un triunghi inferior. Fiecare iterație
prin această buclă va crea 2 triunghiuri pentru fiecare față a sferei noastre. Dacă
schimbați axa principală așa cum este sugerat în pasul anterior, asigurați-vă că
schimbați ordinea în care sunt împinse vârfurile în tamponul index pentru a se
potrivi. De exemplu, dacă realizați axa Z axa principală, schimbați indicii 2 și 3
pentru fiecare triunghi în această metodă.
Ultimul pas este de a crea obiectul Vertex Array care încapsulează atributele
vertexului sferei și tamponul index.

OBIECTUL VÂRFULUI MATRICEI


În această fază, vom crea un obiect Vertex Array (VAO) și vom lega cele trei
atribute de vertex și tampon index la VAO pentru redarea ușoară a sferei noastre.
main.cpp

293 GLuint vao;

294 glGenVertexArrays( 1, &vao );

295 glBindVertexArray( vao );

296

297 GLuint vbos[4];

298 glGenBuffers( 4, vbos );

299

300 glBindBuffer( GL_ARRAY_BUFFER, vbos[0] );


301 glBufferData( GL_ARRAY_BUFFER, positions.size() * sizeof(vec3), positions.data(), GL_STATIC_DRAW );

302 glVertexAttribPointer( POSITION_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

303 glEnableVertexAttribArray( POSITION_ATTRIBUTE );

304

305 glBindBuffer( GL_ARRAY_BUFFER, vbos[1] );

306 glBufferData( GL_ARRAY_BUFFER, normals.size() * sizeof(vec3), normals.data(), GL_STATIC_DRAW );

307 glVertexAttribPointer( NORMAL_ATTRIBUTE, 3, GL_FLOAT, GL_TRUE, 0, BUFFER_OFFSET(0) );

308 glEnableVertexAttribArray( NORMAL_ATTRIBUTE );

309

310 glBindBuffer( GL_ARRAY_BUFFER, vbos[2] );

311 glBufferData( GL_ARRAY_BUFFER, textureCoords.size() * sizeof(vec2), textureCoords.data(), GL_STATIC_DR

312 glVertexAttribPointer( TEXCOORD0_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

313 glEnableVertexAttribArray( TEXCOORD0_ATTRIBUTE );

314

315 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vbos[3] );

316 glBufferData( GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(GLuint), indicies.data(), GL_STATIC_

317

318 glBindVertexArray( 0 );

319 glBindBuffer( GL_ARRAY_BUFFER, 0 );

320 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );

321

322 return vao;

323 }

Principal

Punctul principal de intrare pentru aplicația noastră va iniția OpenGL, va încărca


resursele de care avem nevoie și vom interoga locațiile uniforme în programul
shader.
{

326 g_PreviousTicks = std::clock();


327 g_A = g_W = g_S = g_D = g_Q = g_E = 0;

328

329 g_InitialCameraPosition = glm::vec3( 0, 0, 100 );

330 g_Camera.SetPosition( g_InitialCameraPosition );

331 g_Camera.SetRotation( g_InitialCameraRotation );

332

333 InitGL(argc, argv);

334 InitGLEW();

335

337 g_EarthTexture = LoadTexture( "../data/Textures/earth.dds" );

338 g_MoonTexture = LoadTexture( "../data/Textures/moon.dds" );

339

340 GLuint vertexShader = LoadShader( GL_VERTEX_SHADER, "../data/shaders/simpleShader.vert" );

341 GLuint fragmentShader = LoadShader( GL_FRAGMENT_SHADER, "../data/shaders/simpleShader.frag" );

342

343 std::vector<GLuint> shaders;

344 shaders.push_back(vertexShader);

345 shaders.push_back(fragmentShader);

346

347 g_SimpleShaderProgram = CreateShaderProgram( shaders );

348 assert( g_SimpleShaderProgram );

349

350 // Retrieve the location of the color uniform variable in the simple shader program.

351 g_uniformColor = glGetUniformLocation( g_SimpleShaderProgram, "color" );

Pe liniile 337 și 338, cele două texturi care vor fi folosite pentru a face pământul și
luna în scena noastră sunt
353 vertexShader = LoadShader( GL_VERTEX_SHADER, "../data/shaders/texturedDiffuse.vert" );

354 fragmentShader = LoadShader( GL_FRAGMENT_SHADER, "../data/shaders/texturedDiffuse.frag" );

355

356 shaders.clear();

357
358 shaders.push_back(vertexShader);

359 shaders.push_back(fragmentShader);

360 g_TexturedDiffuseShaderProgram = CreateShaderProgram( shaders );

361 assert( g_TexturedDiffuseShaderProgram );

362

363 g_uniformMVP = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "ModelViewProjectionMatrix" );

364 g_uniformModelMatrix = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "ModelMatrix" );

365 g_uniformEyePosW = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "EyePosW" );

366

367 // Light properties.

368 g_uniformLightPosW = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "LightPosW" );

369 g_uniformLightColor = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "LightColor" );

370

371 // Global ambient.

372 g_uniformAmbient = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "Ambient" );

373

374 // Material properties.

375 g_uniformMaterialEmissive = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "MaterialEmissive" );

376 g_uniformMaterialDiffuse = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "MaterialDiffuse" );

377 g_uniformMaterialSpecular = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "MaterialSpecular" );

378 g_uniformMaterialShininess = glGetUniformLocation( g_TexturedDiffuseShaderProgram, "MaterialShininess" );

379

380 glutMainLoop();

381 }

Pe liniile 353 și 354 se încarcă vârfurile și shaderele de fragmente care au fost


arătate mai devreme, iar pe linia 360 vom crea programul de shader care leagă
vârful și shaderele fragmentelor.
Pe liniile 363-378 sunt interogate variabilele uniforme definite în programul
shader.
Și pe linia 380, bucla principală este lansată.
Refaceți scenă
Pentru a face această scenă, vom desena 3 sfere. Prima sferă va reprezenta
soarele. Aceasta va fi o sferă albă neclară care se va roti în jurul valorii de
aproximativ 90.000 km în jurul centrului scenei. Poziția singurei lumini în scenă va
fi aceeași ca și obiectul care reprezintă soarele.
Pământul este așezat în centrul scenei. Pământul se rotește în jurul polilor, dar
pozițiile sale rămân fixe în centrul scenei (Pământul nu va fi tradus).
Obiectul final va fi luna. Luna pare să se rotească în jurul pământului, dar la o
distanță de 60.000 km de pământ.
În această scenă, facem acordul că 1 unitate este de aproximativ 1.000 km. Deși
unitățile folosite sunt complet arbitrare, este logic să se aleagă unitățile care sunt
utilizate în realitate fără a necesita un interval infinit de tampon de adâncime. De
exemplu, o unitate comună de măsurare este de 1 unitate este echivalentă cu 1
metru când se creează un shooter de prima persoană.
În prima parte a funcției de randare, vom crea o sferă VAO care poate fi folosită
pentru a face soarele, pământul și luna.
main.cpp

399 void DisplayGL()

400 {

401 int slices = 32;

402 int stacks = 32;

403 int numIndicies = ( slices * stacks + slices ) * 6;

404 if ( g_vaoSphere == 0 )

405 {

406 g_vaoSphere = SolidSphere( 1, slices, stacks );

407 }

Sfera VAO este creată dintr-o sferă cu 32 de felii și 32 de stive. Desigur, puteți
crea o sferă mai detaliată, cu mai multe felii și stive, dacă credeți că această sferă
nu are suficiente detalii, dar pentru scopurile mele acest lucru pare să fie
suficient. Dacă aș fi fost ambițios, aș avea funcția SolidSphere să returneze un
obiect de sferă care stochează numărarea indexului și ID-urile VBO-urilor interne
care sunt folosite pentru a stoca geometria sferei, dar care nu sunt necesare pentru
implementarea minimă aici. Vă încurajez să creați un obiect sferic pe cont propriu,
care încapsulează datele sferei.
Următoarea parte a funcției de randare va configura proprietățile uniforme în
shader și va face soarele, pământul și luna. Să tragem mai întâi soarele folosind
programul Shadow Shader (de vreme ce soarele nu este texturat sau aprins, nu este
nevoie să folosim programul Shurer TextureLit pentru asta).
main.cpp

409 const glm::vec4 white(1);

410 const glm::vec4 black(0);

411 const glm::vec4 ambient( 0.1f, 0.1f, 0.1f, 1.0f );


412

413 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

414

415 // Draw the sun

416 glBindVertexArray(g_vaoSphere);

417

418 glUseProgram( g_SimpleShaderProgram );

419 glm::mat4 modelMatrix = glm::rotate( glm::radians(g_fSunRotation), glm::vec3(0,-1,0) ) * glm::translate(glm::vec3(

420 glm::mat4 mvp = g_Camera.GetProjectionMatrix() * g_Camera.GetViewMatrix() * modelMatrix;

421 GLuint uniformMVP = glGetUniformLocation( g_SimpleShaderProgram, "MVP" );

422 glUniformMatrix4fv( uniformMVP, 1, GL_FALSE, glm::value_ptr(mvp) );

423 glUniform4fv(g_uniformColor, 1, glm::value_ptr(white) );

424

425 glDrawElements( GL_TRIANGLES, numIndicies, GL_UNSIGNED_INT, BUFFER_OFFSET(0) );

Pe liniile 409-411 sunt definite câteva variabile constante. Aceste valori vor fi
folosite pentru a seta variabilele uniforme în shadere.
Pe linia 413, buffer-ul de culoare și buffer-ul de adâncime al framebuffer-ului legat
în prezent sunt șterse.
Pe linia 416, VAO pentru obiectul sferic este legat. Acest obiect de sferă va fi
folosit pentru a face soarele, pământul și luna, astfel încât să nu mai trebui să îl
deblocăm din nou până la sfârșitul funcției de randare.
Soarele va fi redat folosind un shader de culoare simplu. Nu se va aplica lumina
sau textura la soare. Este pur și simplu o minge albă în spațiu. Pentru a face
soarele, legăm programul simplu de shader pe linia 418.
Pentru a face soarele, trebuie să setăm MVP și variabilele uniforme de culoarecare
sunt definite în programul shader. Pentru a stabili o uniformă de matrice,
folosim glUniformMatrix4fv și pentru a seta o variabilă uniformă a vectorului cu
4 componente utilizăm glUniform4fv .
Pe linia 419, matricea mondială pentru soare este calculată separat pentru că vom
folosi poziția lumii soarelui pentru a seta poziția sursei de lumină în etapele
următoare.
Pe linia 425, sfera reprezentând soarele este redată
folosind metoda glDrawElements .
Apoi vom trage pământul.
main.cpp

427 // Draw the earth.

428 glBindTexture( GL_TEXTURE_2D, g_EarthTexture );


429 glUseProgram( g_TexturedDiffuseShaderProgram );

430

431 // Set the light position to the position of the Sun.

432 glUniform4fv( g_uniformLightPosW, 1, glm::value_ptr(modelMatrix[3]) );

433 glUniform4fv( g_uniformLightColor, 1, glm::value_ptr(white) );

434 glUniform4fv( g_uniformAmbient, 1, glm::value_ptr(ambient) );

435

436 modelMatrix = glm::rotate( glm::radians(g_fEarthRotation), glm::vec3(0,1,0) ) * glm::scale(glm::vec3(12.756f) );

437 glm::vec4 eyePosW = glm::vec4( g_Camera.GetPosition(), 1 );

438 mvp = g_Camera.GetProjectionMatrix() * g_Camera.GetViewMatrix() * modelMatrix;

439

440 glUniformMatrix4fv( g_uniformMVP, 1, GL_FALSE, glm::value_ptr(mvp) );

441 glUniformMatrix4fv( g_uniformModelMatrix, 1, GL_FALSE, glm::value_ptr(modelMatrix) );

442 glUniform4fv( g_uniformEyePosW, 1, glm::value_ptr( eyePosW ) );

443

444 // Material properties.

445 glUniform4fv( g_uniformMaterialEmissive, 1, glm::value_ptr(black) );

446 glUniform4fv( g_uniformMaterialDiffuse, 1, glm::value_ptr(white) );

447 glUniform4fv( g_uniformMaterialSpecular, 1, glm::value_ptr(white) );

448 glUniform1f( g_uniformMaterialShininess, 50.0f );

449

450 glDrawElements( GL_TRIANGLES, numIndicies, GL_UNSIGNED_INT, BUFFER_OFFSET(0) );

Pentru a desena pământul, trebuie să legăm textura pământului de prima unitate de


textura activă. În mod implicit, unitatea de textura activă este unitatea de textura 0
și formatorul uniform de textură din programul Shader difuzat texturat are o
valoare implicită de 0, deci nu este necesar să setăm explicit valoarea probatorului
uniform în programul shader la 0. Este suficient să legați pur și simplu textura
la țintă textură GL_TEXTURE_2D .
Pe linia 429, am setat programul shader activ ca cel al programului de shadere
texturat și luminat pe care l-am compilat în funcția principală.
Pe linia 432-434 am setat variabilele uniforme în shader. Poziția luminii în spațiul
mondial este poziția soarelui pe care tocmai l-am redat, care este pur și simplu a
treia coloană a matricei model care a fost setată pe linia 419. Culoarea luminii este
setată pe alb pe linia 433, iar contribuția globală la aer este setat pe linia 434.
Pe liniile 436 se calculează matricea modelului pământului. Matricea modelului
este necesară ca parametru separat pentru programul shader, deoarece îl vom folosi
pentru a transforma poziția vârfului modelului și a vârfului normal în spațiul
mondial.
Pe linia 437, poziția ochilor în spațiul mondial a fost calculată din poziția mondială
a camerei. Funcția GetPosition a clasei camerei returnează un vector cu 3
componente, dar shader-ul nostru așteaptă un vector cu 4 componente, astfel încât
trebuie să aruncăm poziția camerei într-un vector cu 4 componente prin adăugarea
unui element 1 la componenta w astfel încât să acționeze ca un punct în spațiul 3D
și nu un vector de direcție.
Pe linia 438 se calculează matricea de vizualizare-proiectare. Această matrice este
utilizată pentru a transforma poziția vertexului direct în spațiul clip.
Pe liniile 440-442 sunt setate variabilele uniforme corecte din programul shader.
Pe liniile 445-448 sunt setate variabilele uniforme pentru proprietățile
materialelor. Veți observa că stofa materialului este setată la 50,0. Aceasta va
produce o lumină speculară destul de luminoasă pe suprafața pământului.
Pe linia 450, facem pământul folosind glDrawElements ca mai înainte.
Apoi tragem luna care este aproape identică cu cea a pământului, cu excepția unei
matrici diferite a modelului, a texturii și a unor proprietăți materiale.

452 // Draw the moon.

453 glBindTexture( GL_TEXTURE_2D, g_MoonTexture );

454

455 modelMatrix = glm::rotate( glm::radians(g_fSunRotation), glm::vec3(0,1,0) ) * glm::translate(glm::vec3(60, 0, 0) ) *

456 mvp = g_Camera.GetProjectionMatrix() * g_Camera.GetViewMatrix() * modelMatrix;

457

458 glUniformMatrix4fv( g_uniformMVP, 1, GL_FALSE, glm::value_ptr(mvp) );

459 glUniformMatrix4fv( g_uniformModelMatrix, 1, GL_FALSE, glm::value_ptr(modelMatrix) );

460

461 glUniform4fv( g_uniformMaterialEmissive, 1, glm::value_ptr(black) );

462 glUniform4fv( g_uniformMaterialDiffuse, 1, glm::value_ptr(white) );

463 glUniform4fv( g_uniformMaterialSpecular, 1, glm::value_ptr(white) );

464 glUniform1f( g_uniformMaterialShininess, 5.0f );

465

466 glDrawElements( GL_TRIANGLES, numIndicies, GL_UNSIGNED_INT, BUFFER_OFFSET(0) );

468 glBindVertexArray(0);

469 glUseProgram(0);
470 glBindTexture( GL_TEXTURE_2D, 0 );

471

472 glutSwapBuffers();

473 }

S-ar putea să vă placă și