Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: OpenGL:llä useampi Viewport?

Sivun loppuun

Kray [13.03.2008 18:07:03]

#

Mietin, että miten OpenGL:llä saa tehtyä useamman viewportin. Esimerkkinä jaetun ruudun moninpeli, ja että vaikka yksi niistä on 2d ja toinen 3d. Mites se toteutetaan...? Jos se vaikuttaa, käytän SDL:ää ja AntTweakBaria. Niinkö, että piirrän joka viewportin erikseen...?

Metabolix [13.03.2008 18:33:49]

#

Tietenkin piirrät jokaisen esikseen.

glViewport(0, 0, 320, 480);
piirra_p1();
glViewport(320, 0, 320, 480);
piirra_p2();

Kray [15.03.2008 16:13:34]

#

Ok. Sitten, voisiko joku neuvoa, miten lasken polygonin normaalin. Siis sen, mikä annetaan glNormal3f -funktiolla ennen verteksejä. Löysin ohjeita googlella, mutta en oikein käsittänyt. Vaikka joku valmis funktio, jonka avulla myös ymmärtäisin laskutavan.
Edit: Ja sitten jos joku vielä neuvoisi miten lasketaan oma normaali jokaiselle verteksille.

Metabolix [15.03.2008 16:52:12]

#

Polygoni => kolmio ABC, ja kärjet ovat tietenkin kolmiulotteisen avaruuden pisteitä eli paikkavektoreita.
n = u×v = (B-A)×(C-B)
Ja ristitulon kaava:
(ux, uy, uz)×(vx, vy, vz) = (uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx)

Polygonin on syytä olla kokonaan samassa tasossa, ja siksipä kolmiot ovat polygoneista parhaita, koska ne ovat aina yhdessä tasossa.

Kray [15.03.2008 17:07:39]

#

Mutta entäs kun polygoni ei olekaan kolmio? Valitsenko vain mielivaltaiset 3 pistettä? Miten tuo tehdään, piirretäänkö erikseen:

glNormal3f(calcnorm(v[0].x,v[1].x,v[2].x), calcnorm(v[0].y,v[1].y,v[2].y), calcnorm(v[0].z,v[1].z,v[2].z));

Ja tuo calcnorm olisi:

float calcnorm(float a,float b,float c){
	return (b-a)*(c-b);
}

Ristituloa en vielä kommentoi, se myöhemmälle.

Edit: Täältä löytyy kuva tilanteesta. Laivan tapainen pitäisi tulla, nuo ei-mustat ovat ainoita joiden väri muuttuu pyöriessä, muut pysyvät mustina. Lisäksi vaihtuvat vaihtuvat väärin päin, tummentuvat mitä lähempänä kameraa.

Metabolix [15.03.2008 17:35:26]

#

Tuo risti ensimmäisessä laskutoimituksessa tarkoittaa juuri ristituloa, kyseessä on vektorilasku. Koodisi menee siis aika pahasti metsään.

Kannattaa ensin laskea nuo apuvektorit u ja v ja vasta niillä ristitulo.

Jos polygoni on tasossa, sillä on koko alueella myös sama normaali — ja määritelmän mukaan polygonien pitäisi olla tasossa. Suosittelen niinkin luovaa ratkaisua kuin polygonien muuttamista kolmioiksi, se nimittäin helpottaa elämää monessa suhteessa.

Normaali tulee vielä normalisoida (muuttaa yhden yksikön mittaiseksi eli jakaa omalla pituudellaan), ellet halua jättää tätä OpenGL:n huoleksi (glEnable(GL_NORMALIZE)).

Valaistuksenhan ei pitäisi varsinaisesti toimia syvyyden mukaan vaan valon tulokulman, ja myöskään pelkät normaalit eivät pane valaistusta toimimaan.

Funktioina voit luntata ristitulon 3D-vektoriluokasta.

Kray [15.03.2008 18:25:28]

#

No miten minä ne polygonit kolmioiksi oikein muutan???
Edit: tai no, mietin hiukan, ja tulin siihen tulokseen, että käyn läpi jokaisen mahdollisen kolmion, lasken niille omat normaalit ja ne, joiden normaalit eroavat, piirrän erikseen. Kuulostaako toimivalta?

Metabolix [15.03.2008 18:44:28]

#

No millähän olet tehnyt laivasi? Yleensä mallinnusohjelmat osaavat tehdä muunnoksen. Jos haluat itse vaikkapa tiedostoa lukiessa muuttaa ne, niin tietenkin polygonista 01234567 tulee esimerkiksi kolmiot 012, 023, 034, 045, 056 ja 067. Jakotapoja on tietysti muitakin, esimerkiksi 017, 712, 726, 623, 635, 534.

Liukulukujen epätarkkuuden takia saatat menetelmälläsi saada jokaiselle kolmiolle hieman eri normaalin. Muutenkin esimerkiksi kahdeksankulmiosta voi valita 56 erilaista kolmiota, mutta kuten yllä nähtiin, leikkelemällä siitä tulee vain kuusi. Kannattaa siis tehdä systemaattinen muunnos.

Muunnos kannattaa joka tapauksessa tehdä, koska jokseenkin kaikissa laskuissa (grafiikka, törmäystarkistukset) on helpompi käsitellä kolmioita kuin monimutkaisempia kuvioita.

Kray [16.03.2008 11:01:45]

#

Havaitsinko sarkasmia? "Laivani" minä tein wings3d:llä kun ei ollut muuta tekemistä, konvertoin sen omaan formaattini objista. Käytän sitä testaukseen kun se on sopivan monimutkainen testaukseen. Tarkoituksena olisi tehdä oman formaattini osaava mallinnusohjelma erikoistarpeisiini, ei wings3d:n korvaajaksi (teksturointi ja erikoisempia asia, "kuvista mallintaminen"). Joo, sain tuon nyt toimimaan kolmioilla. Törmäystarkistusta en ainakaan nyt tarvitse. Voisitko neuvoa, miten verteksikohtaiset pisteet saa laskettua? Silloin ei käsittääkseni tarvitse muuttaa niitä kolmioiksi.
Edit: Jippii, sain verteksikohtaiset normaalit toimimaan! Tässä muille saman kanssa pähkäileville koodi, Saattaa olla huono ymmärtää kun se on ihan vain suoraan napattu koodista!
Tällä lasketaan normaalit:

MVertex vertex;
	for(a=0; a<Model::nVertex; ++a){
		vertex.x=0;vertex.y=0;vertex.z=0;
		for(b=0; b<Model::nPolygon; ++b){
			for(c=0; c<Model::PolygonArray[b].nVertex; ++c){
				if(Model::PolygonArray[b].VertexArray[c]==a){
					vertex.x+=Model::VertexArray[PolygonArray[b].VertexArray[c]].x;
					vertex.y+=Model::VertexArray[PolygonArray[b].VertexArray[c]].y;
					vertex.z+=Model::VertexArray[PolygonArray[b].VertexArray[c]].z;
				}
			}
		}
		Model::NormalArray[a]=vertex;
	}

Edit2: Tässä Täältä napattu pseudo-koodi:

for each vertex
   set a temporary vector to (0, 0, 0)
   for each polygon
      if one of the polygon's vertices is equal to the current vertex,
      add that polygon's normal to the temporary vector
   next
   normalize the temporary vector
   (no need to divide by the total polygons)
   this is now the vertex normal
next

Metabolix [16.03.2008 14:11:57]

#

Ei tainnut ihan oikein mennä tuo koodi? Ohjeessasi käsketään lisätä verteksin normaaliin jokaisen sitä koskevan polygonin normaali. Nyt lisäät verteksin normaaliin vain sitä verteksiä itseään. Jos siis verteksi v kuuluu polygoneihin P1, P2 ja P3, lopputuloksesi on
n = 3 * v, kun sen pitäisi olla
n = P1.n + P2.n + P3.n.

Joudut siis tuossakin tapauksessa ensin laskemaan polygoneille normaaleja. Ristitulolta et voi välttyä. Oman tapasi toiminta on hyvin näennäistä, kuten voisit kenties todeta lisäämällä latausvaiheessa mallisi vertekseihin jonkin luvun niin, että se siirtyy selkeästi pois origon läheisyydestä.

Kray [16.03.2008 14:16:35]

#

Hmm... Selvä... No, miten minä oikein lasken sen polygonin normaalit jos se ei ole kolmio??? Huomauttaisin, että tuo tapa mitä minä käytin toimi aivan kuin piti!

Metabolix [16.03.2008 14:25:35]

#

No yksi mahdollisuus on tietysti laskea normaaliksi tuollaisen mahdollisen kolmiojaon normaalien keskiarvo.

Voisit ihan oikeasti todeta sen toimivuuden mainitsemallani tavalla, väitänpä, että tulee aika erikoisesti valaistu paatti.

Kray [16.03.2008 14:39:10]

#

Äh... Siis juuri se että esimerkiksi neliö 0123 lasketaan kahdeksi kolmioksi? Siis 012 ja 023? Hetkinen... Silloin kolmioita muodostuu... Onko oikein: kolmioita on verteksien määrä-2. Koska neliöstä tulee kaksi, viisikulmiosta 01234 012 023 ja 034, kuusikulmiosta 012345 012 023 034 045. Eli vain teen ne näin ja sitten niiden keskiarvo? Okei. Muuten, miten se normalisointi tapahtuu? Olen jättänyt sen opengl:n huoleksi, en ymmärtänyt siitä vektoriluokasta...
Edit: Ei se niin erikoisesti ollut valaistu, samalla tavalla oli kuin wings3d:ssä.

Metabolix [16.03.2008 15:19:06]

#

kray kirjoitti:

Edit: Ei se niin erikoisesti ollut valaistu, samalla tavalla oli kuin wings3d:ssä.

Siis jos siirrät mallin verteksit kauas origosta? Nimenomaan verteksit, ei glTranslatea siis vaan ihan plussia, esimerkiksi kaikille verteksi.x += 50. Tarkistapa vielä. Tällöin nimittäin mikä tahansa laskemasi "normaali" osoittaisi lähinnä positiiviselle x-akselille riippumatta siitä, millä puolella mallia se on.

Kyllä, kulmat-2.

Ai niin, se normalisointi. Jaat vain normaalin komponentit sen pituudella:

// Pituus Pythagoraan lauseen avulla
pituus = sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
v.x /= pituus; v.y /= pituus; v.z /= pituus;

Kray [16.03.2008 15:24:10]

#

En ihan tuota tainnut tarkoittaa ... :I Mutta okei, alan virittelemään tuota kolmiojuttua.

Metabolix [16.03.2008 15:58:15]

#

Sanotaan nyt vielä sekin, että verteksinormaalit eivät todellakaan ole ratkaisu kaikkeen. Esimerkiksi kuution valaiseminen verteksinormaalien mukaan ei ole kovin kaunista, koska kuution ei ole tarkoituskaan olla pyöreä. Verteksinormaalit ovat sileyden (pyöreyden) toteuttamista varten, kulmikkaissa tilanteissa on parempi käyttää pintojen normaaleita. Tietenkin on sellainenkin mahdollisuus, että kulmikkaassa kohdassa kuvio on epäjatkuva, eli esimerkiksi kartion kärjessä on paljon verteksejä, joilla on omat normaalinsa, sen sijaan, että ainoan kärkiverteksin normaali osoittaisi kärjen suuntaan.

JoinTuanJanohon [16.03.2008 16:31:55]

#

...siis, tarkemmin noi normaalit kannattaa laskea jokaisen kolmen kolmio-polygonin kärjille niin, että kärkipisteiden normaali on niiden kolmen keskiarvona. (Normaalit oletetaan aina yhden mittaisiksi yksikkövektoreiksi, jotka osoittavat origosta johonkin suuntaan. Normaaleille ei siis koskaan saa suorittaa siirtoa, mutta kiertoa niille voi antaa riippuen kappaleen sen hetkisistä kiertokulmista.)

Kray [17.03.2008 16:32:08]

#

Nyt taitaa olla jokin pielessä. Se teidän ohjeilla tehty viritelmä näyttää tältä. Minun oma ylempänä kertomani koodi taas tekee näin. Minun omani tekee sen kyllä paremmin (ainakin omasta mielestäni, toisessa laivan yläosa on pielesssä. Käytin tätä koodia:

MVertex vertex;
    MVertex vertices[3];
    MVertex* normals;//[Model::PolygonArray[a].nVertex-2]
    float len;
    fclose(fpt);
    for(a=0; a<Model::nPolygon; ++a){
    	normals = (MVertex*) malloc(sizeof(MVertex) * (Model::PolygonArray[a].nVertex-2));
    	for(b=0; b<Model::PolygonArray[a].nVertex-2; ++b){
			vertices[0]=Model::VertexArray[Model::PolygonArray[a].VertexArray[0]];
			vertices[1]=Model::VertexArray[Model::PolygonArray[a].VertexArray[b+1]];
			vertices[2]=Model::VertexArray[Model::PolygonArray[a].VertexArray[b+2]];
			normals[b]=CalcNormal(vertices[0],vertices[1],vertices[2]);
		}
		vertex=CalcVertexAverage(normals,Model::PolygonArray[a].nVertex-2);
		len=sqrt(vertex.x * vertex.x + vertex.y * vertex.y + vertex.z * vertex.z);
		vertex.x /= len; vertex.y /= len; vertex.z /= len;
		Model::PolygonArray[a].Normal=vertex;
		free(normals);
	}

	for(a=0; a<Model::nVertex; ++a){
		vertex.x=0;vertex.y=0;vertex.z=0;
		for(b=0; b<Model::nPolygon; ++b){
			for(c=0; c<Model::PolygonArray[b].nVertex; ++c){
				if(Model::PolygonArray[b].VertexArray[c]==a){
					vertex.x+=Model::PolygonArray[b].Normal.x;
					vertex.y+=Model::PolygonArray[b].Normal.y;
					vertex.z+=Model::PolygonArray[b].Normal.z;

				}
			}
		}

		len = sqrt(vertex.x * vertex.x + vertex.y * vertex.y + vertex.z * vertex.z);
		vertex.x /= len; vertex.y /= len; vertex.z /= len;
		Model::NormalArray[a]=vertex;
	}

Metabolix [17.03.2008 18:20:40]

#

Jos haluat ihmetellä erilaisia lopputuloksia, niin laitan tähän toistaiseksi muutaman linkin:
1) pelkät polygoninormaalit,
2) matemaattisesti lasketut verteksinormaalit, kun kerran kuvio muutenkin on matemaattisesti luotu,
3) jälkimmäisellä tavalla lasketut normaalit, koskevien polygonien normaalien summa aivan oikein, ja
4) ensimmäisellä tavallasi lasketut normaalit, siis koskevien polygonien verteksien summa.

Ero lienee selvä, ja sama näkyy kyllä laivassasikin: miksi ihmeessä laivan takapää on musta ja etupää punainen, vaikka koko kylki osoittaa samaan suuntaan ja olisi siis oikeasti yksivärinen (ellet ole jo spottivaloja keksinyt)?

[Mod. huom: rikkonaiset linkit poistettu myöhemmin.]

Kray [17.03.2008 18:44:45]

#

...No, ihmettelen vielä, valoja taidan seuraavaksi väkertää, katsotaan sitten. Laivan takapää oli musta, koska se oli taaempana. Huomenna taitaa sitten ensimmäiset kysymykset valoja koskien tulla :).
Edit: Jos antaisit täyden koodin koskien noiden verteksinormaalien matemaattista laskentaa, niin siitä saattaisin oppia jotain...

Metabolix [17.03.2008 18:46:47]

#

kray kirjoitti:

Laivan takapää oli musta, koska se oli taaempana.

Kerrotko vielä, kuinka tuo etäisyys liittyy asiaan? Ja mihin oikein olet noita normaaleja käyttämässä jos et valoihin? O_o Se taitaa olla suunnilleen ainoa asia, johon OpenGL käyttää annettuja normaaleita.

Ja vastaus seuraavaan: niinpä sen vissiin pitääkin, anteeksi häiriö. ^^ Menee sekaisin, kun en ole koskaan itse yrittänytkään käyttää muuta kuin kolmioita (resurssien tuhlausta sellainen, mutkistaa asioita, kuten huomaat).

Siitä matemaattisesta laskennasta et todellakaan hyödy mitään, se ei ole mikään yleispätevä menetelmä vaan toimii silloin, kun koko objekti muutenkin luodaan laskemalla (kuten nyt tuollaiset yksinkertaiset asiat kuin pallo, sylinteri tai ääretön-merkki). Ei se ole derivointia kummempaa. ^^

Kray [17.03.2008 18:48:59]

#

Lisäys edelliseen viestiini: eikös sen vertices[0]:n pitäisi olla juuri 0? Katsos kun: 0123:sta muodostuu 012 023 eli 0 on kantapiste.
Edit: No, miksi siinä minun näyttämässäni kuvasta tuolla teidän järjestelmällä se tummuus vaihteli tasaisella kohdalla?
Edit2:...Minulla on pieni ajatuskatkos... Eli mites siis se täydellinen koodi tuohon tapaan jota yritin oikein menisi? Nyt en vain ymmärrä mikä on vikana... Huomauttaisin että laivan pyöriessä aina se taaempana oleva osa oli tumma, lähin oli kirkas!

Metabolix [17.03.2008 18:55:48]

#

Itsehän sinun pitäisi parhaiten tietää, mitä väreihin liittyvää koodia ohjelmasi sisältää. Sitä on turha muilta kysellä. Jos et käytä valoja, on turha odottaa, että normaalit tekisivät mitään mielekästä. Jostain ne värit kuitenkin tulevat, joten kerropa sinä, mistä.

Kray [17.03.2008 18:58:00]

#

glColor3f(1,0,0);
ja enabloituna: GL_LIGHTO ja GL_LIGHTNING.
Ja muu ei taida vaikuttaa.
Edit: Kyselin vain esimerkkiä polygonin ja verteksien normaalien laskemiseen, eli verteksimuuttujat sisältävät x,y ja z:n, ja polygonin taulukossa nämä. Nimittäin ajatus nyt ei pysy kasassa kun en nyt oikein tiedä mitä tapaa pitäisi käyttää.

Metabolix [17.03.2008 19:47:49]

#

Usko jo, että etäisyydellä ei ole merkitystä, ellet ole ajatellut saada aikaan pistevaloa. Ja jos et aseta valoille järkeviä parametreja, ota ne pois päältä; ei se valmiskaan valojärjestelmä ole mikään TeeMulleAitoPäivänvalo(), vaan sitä pitää säätää ja viritellä.

Tällaisessa ajattelua vaativassa asiassa kuin 3D-piirtäminen ei vain toimi lähestymistapa "laitetaanpa näin ja katsotaan, jos se auttaisi". Älä huolehdi mistään verteksien normaaleista, ennen kuin saat valot toimimaan edes pelkillä polygoneilla.

OpenGL:n valojärjestelmä on kohtuullisen rajoittunut, joten ei ole lainkaan hassumpi ajatus toteuttaa valoja itse. Se tosin vaatii hieman enemmän matemaattista ymmärtämistä kuin muutaman funktioparametrin asettaminen (kopiointia ei tietenkään tueta).

Oikea tapa polygonin normaalin laskemiseen on edelleenkin ristitulo, ja verteksien normaalit voi aivan hyvin laskea summaamalla ympäröivien polygonien normaalit. Kun ilmeisesti käytät nyt tuollaista osoitinjärjestelmää, niin tässä eräänlaista pseudokoodia. Ensin polygonien normaalit:

foreach polygoni
  polygoni.normaali = (0, 0, 0);
  k0 = polygoni.kulmat(0);
  k1 = polygoni.kulmat(1);
  i = 2;
  while (i < polygoni.kulmien_maara)
    k2 = polygoni.kulmat(i);
    polygoni.normaali += ristitulo((k1 - k0), (k2 - k1));
    k1 = k2;
  normalisoi(polygoni.normaali) // TAI ks. seuraava koodi

Sitten vertekseille normaalit:

foreach verteksi
  verteksi.normaali = (0, 0, 0);
foreach polygoni
  foreach kulma
    kulma.normaali += polygoni.normaali
  // normalisoi(polygoni.normaali) voisi olla vaihtoehtoisesti vasta tässä.
  // Tutustu ristitulon matemaattiseen merkitykseen, niin ehkä ymmärrät eron.
foreach verteksi
  normalisoi(verteksi.normaali);

Muista myös varoa nollalla jakoa (nollavektoreita) normalisointivaiheessa.

Kray [18.03.2008 07:20:17]

#

Se osoitinjärjestelmäni on koodivinkistäni luettavissa oleva luokaksi muutettu ja paranneltu koodi. Tarkoitus olisi tätä formaattiani kehitellä niin voisin kenties julkaista 2.0 -version .model:ista, tekstuureitakin varten on (paperilla) suunniteltu .image formaatti :). No, eli... Polygonien normaalit c:nä, kirjoitettu suoraan tähän:

int a,b;
MVertex points[3];
for(a=0; a<Model::nPolygon; ++a){
    Model::PolygonArray[a].Normal=0;//en nyt jaksanut kaikkia x y z
    points[0]=Model::VertexArray[Model::PolygonArray[a].VertexArray[0]];
    points[1]=Model::VertexArray[Model::PolygonArray[a].VertexArray[1]];
    for(b=2; b<Model::PolygonArray[a].nVertex; ++b){
        points[2]=Model::VertexArray[Model::PolygonArray[a].VertexArray[i]];
        Model::PolygonArray[a].Normal += ristitulo((points[1]-points[0]),(points[2]-points[01]));
        points[1]=points[2];
    }
    normalize(Model::PolygonArray[a].Normal);
}

Menikö oikein? Verteksit:(Myös tähän kirjoitettu)

int a,b;
for(a=0; a<Model::NormalArray; ++a){
    Model::NormalArray=(0,0,0);
}
for(a=0; a<Model::nPolygon; ++a){
    for(b=0; b<Model::PolygonArray[a].nVertex; ++b){
        Model::VertexArray[Model::PolygonArray[a].VertexArray[b]]+=Model::PolygonArray[a].Normal;
    }
}
for(a=0; a<Model::nVertex; ++a){
    normalize(Model::NormalArray[a]);
}

OK?

User137 [18.03.2008 10:37:55]

#

99.9999% malleista käyttää polygoneja jotka ovat tasaisia, joten jokaisen pinnalla olevan kolmion normaalin tulisi olla sama. Kuitenkin oletan että löytyy tapauksia joissa 3 vierekkäistä verteksiä voi muodostaa suoran... Joten lasketaan 3 verteksiä jotka mahdollisimman hyvin kattavat koko polygonin.

Esim. 6 kulmainen polygoni, verteksit 0,1,2,3,4,5
Jos valitaan verteksit 0,2,4 saataisiin melko tarkka keskiarvo polygonin normaalista, lähes poikkeuksetta. Nuo myös pystyy laskemaan (count = polygonin verteksien lukumäärä)
1) Ensimmäinen verteksi on 0
2) count/3
3) (count*2)/3

ps. Siksi "lähes poikkeuksetta" koska jos 0,1,2,3,4 muodostaa suoran, ei tulokseen voi luottaa vaikka kaikkien vierekkäisten kolmioiden keskiarvo olisi myös päin hevosia :p

Kray [18.03.2008 14:44:43]

#

Eli 012345 polygonista otetaan 0 2 5 ?

User137 [18.03.2008 19:06:36]

#

0 2 4 tuo lasku antais (6*2=12 / 3 = 4). Jokaisen valitun verteksin välille jää 1 valitsematon. Jos valitset 0 2 5 niin 0 ja 5 on vierekkäisiä, mikä saattaa antaa tulokseen epätarkkuutta.

Jokos normaalin laskeminen kolmiolle toimi?

Kray [18.03.2008 19:13:29]

#

Toimii tuo Metabolixin neuvoma jotenkuten, valoja vähän olen nyt opetellut. Tällä hetekellä tappelen aivan toisen asian kanssa.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta