Tervehdys kaikille,
Olen tässä pohtinut, että mikä hyöty glDrawElement -funktio kutsulla on OpenGL:ssä, jos kerran UV koordinaateille tulee väkisinkin oma indeksointi?
Eli kuten alla olevasta datasta näkee UV koordinaatteja ei saa tehtyä millään verteksi kohtaista dataa, koska esim.
Vertex 1 viittaa polygon 0 UV koordinaatteihin UV 1.000000 0.000000
Vertex 1 viittaa polyhon 2 taas UV koordinaatteihin UV 0.000000 1.000000
...eli periaatteessa voisin pullauttaa tuon listan perusteella uuden indeksoinnin( Loop index ), mutta silloin verteksi kohtainen indeksoini menetetään ja samoja verteksi koordinaattesa tulee useampi rivi.
Näin ollen indeksien käytöllä ei saavuteta mitään hyötyä.
Polygon 0 Loop index 0 (Vertex 0) - UV 0.000000 0.000000 Loop index 1 (Vertex 1) - UV 1.000000 0.000000 Loop index 2 (Vertex 2) - UV 1.000000 1.000000 Loop index 3 (Vertex 3) - UV 0.000000 1.000000 Polygon 1 Loop index 4 (Vertex 4) - UV 0.000000 0.000000 Loop index 5 (Vertex 7) - UV 1.000000 0.000000 Loop index 6 (Vertex 6) - UV 1.000000 1.000000 Loop index 7 (Vertex 5) - UV 0.000000 1.000000 Polygon 2 Loop index 8 (Vertex 0) - UV 0.000000 0.000000 Loop index 9 (Vertex 4) - UV 1.000000 0.000000 Loop index 10 (Vertex 5) - UV 1.000000 1.000000 Loop index 11 (Vertex 1) - UV 0.000000 1.000000 Polygon 3 Loop index 12 (Vertex 1) - UV 0.000000 0.000000 Loop index 13 (Vertex 5) - UV 1.000000 0.000000 Loop index 14 (Vertex 6) - UV 1.000000 1.000000 Loop index 15 (Vertex 2) - UV 0.000000 1.000000 Polygon 4 Loop index 16 (Vertex 2) - UV 0.000000 0.000000 Loop index 17 (Vertex 6) - UV 1.000000 0.000000 Loop index 18 (Vertex 7) - UV 1.000000 1.000000 Loop index 19 (Vertex 3) - UV 0.000000 1.000000 Polygon 5 Loop index 20 (Vertex 4) - UV 0.000000 0.000000 Loop index 21 (Vertex 0) - UV 1.000000 0.000000 Loop index 22 (Vertex 3) - UV 1.000000 1.000000 Loop index 23 (Vertex 7) - UV 0.000000 1.000000
Yleisesti ottaen näyttikselle ladattu data on sellaisessa järjestyksessä ja siten määritelty, että indekseillä voidaan viitata yhden verteksin kaikkeen dataan, mukaanlukien verteksikohtaiset tekstuurikoordinaatit. Eli UV-koordinaatit ovat yleensä verteksikohtaisia.
En ole ihan varma, että mitä nyt sitten haet takaa, mutta ohessa kuitenkin minimaalinen esimerkki, josta pitäisi käydä ilmi, miten UV-koordinaatit voidaan parittaa verteksin X, Y ja Z -komponenttien kanssa. Virheitä lienee parisataa.
struct Vertex { float x, y, z; // Verteksi float u, v; // UV }; Vertex vertices[3]; vertice[0].x =-1.0; // Vasen vertice[0].y = 0.0; vertice[0].z = 0.0; vertice[0].u =-1.0; vertice[0].v = 0.0; vertice[1].x = 0.0; // Ylä vertice[1].y = 1.0; vertice[1].z = 0.0; vertice[1].u = 0.0; vertice[1].v = 1.0; vertice[2].x = 1.0; // Oikea vertice[2].y = 0.0; vertice[2].z = 0.0; vertice[2].u = 1.0; vertice[2].v = 0.0; [..] int indices[3]; // Luodaan indeksit vertekseihin indices[0] = 0; indices[1] = 1; indices[2] = 2; [..] glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*3, &vertices[0].x, GL_STATIC_DRAW); // Kopioidaan verteksidata (toivottavasti) GPU:n puolelle [..] glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(MyVertex), 0); // Määritellään verteksiominaisuuslistan ensimmäiseksi elementiksi verteksidatan x, y ja z -komponentit. glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(MyVertex), 12); // Ja toiseksi elementiksi u ja v - komponentit. [..] glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0); // Piirretään, olettaen että indeksit jne jne jne olisivat bindattuina ja muuta sen sellaista.
Indeksien hyöty tulee tosin ilmi vasta hiukan monimutkaisemmissa esimerkeissä, joissa polygonit jakavat verteksejä. Käytännössä on loppupeleissä monien 3d-mallien kanssa melko harvinaista, että mikä vain yksittäinen verteksi kuuluisi vain yhteen polygoniin.
Toki tuon käyttö on ihan selvää pässin lihaa. Ehkä kysymykseni oli hieman epäselvä. Nimittäin olen tekemässä Blenderille omaa exportteria omaan 3D moottorille, mutta törmäsin ongelmaan, että en saa juurikin tuota paritusta tehtyä tuolle Blenderin datalle.
Eli kuten jo sanoin
Vertex 1 viittaa polygon 0 UV koordinaatteihin UV 1.000000 0.000000
Vertex 1 viittaa polyhon 2 taas UV koordinaatteihin UV 0.000000 1.000000
...ongelmahan tuossa on se, että jos paritus tehdään niin ensin
Verteksi 1 -> UV on U: 1.0 V: 0.0000
Polygon 2 Verteksi 1 UV kuitenkin asetetaan uudestaan 0.0 ja 1.0.
...kuten wikipediassakin sanotaan: http://en.wikipedia.org/wiki/UV_mapping
"UV coordinates are applied per face,[2] not per vertex. This means a shared vertex can have different UV coordinates in each of its triangles, so adjacent triangles can be cut apart and positioned on different areas of the texture map."
Tuollahan se kysymys vähän selviinty juu. :-)
OpenGL:ssä tekstuurikoordinaattien takia toisinaan joutuukin verteksejä duplikoimaan ja OpenGL:n matalamman tason toteutus eroaa tässä Blenderin sisäisestä toteutuksesta.
Verteksien jakamisen hyöty tulee parhaiten vastaan esim seuraavanlaisessa tilanteessa:
v3 /|\ / | \ / | \ /_ _|_ _\ v1 v2 v4
Jos tässä kahden kolmion muodostamalle polygonille halutaan mapata tekstuuri, joka on vaikkapa ihan vain tasainen, jatkuva neliö, voidaan verteksit v2 ja v3 jakaa tekstuurikoordinaatteineen.
Jos taas sattuukin niin, että tähän haluttaisiin mapata tekstuuri, jossa kummallekin kolmiolle tarkoitetut osat ovat "eri paikoissa" kuvassa, silloin v2:n ja v3:n jakaminen onkin vaikeampaa ja saatetaan joutua duplikoimaan ne verteksit. Sisäänrakennettujen vertekstimääreiden kanssa ei pysty määrittelemään UV-koordinaatteja erikseen per-taso.
Periaatteessa voisit toki OpenGL:nkin kanssa kikkailla jaetut verteksikoordinaatit ja eriävät UV-koordinaatit esimerkiksi laittamalla oikeat käytettävät verteksidatat (paikka, UV, normaali) tekstuureihin ja lähettämällä OpenGL:n verteksimääreinä indeksit näihin tekstuureihin, mutta todennäköisesti se olisi lähinnä raskaampi renderöidä ja jossain määrin sekava toteuttaa.
Tämä avarsi mieltäni. Kiitos tästä. :)
Käytännössä GL_TRIANGLE_STRIP on nopeampi piirtää kuin GL_TRIANGLES, ja se vie myös vähemmän muistia. Koko juttu perustuu siihen että kolmiot jakaa verteksejä keskenään. Mutta mallin teko siihen muotoon on hankalampaa, joten laiska ratkaisu on piirtää joka kolmio erikseen.
Hei!
Kerran kyseessä on Blenderille tehty exportteri, ajattelin jakaa itse tekemäni exportterin, jos siitä olisi apua ongelmaan. Olen käyttänyt vastaavia muutamissa projekteissa. Kyseinen taitaa jopa viedä luustot, mutta en muista, toimivatko ne. Exportteri yhdistää samanlaiset kärjet toisiinsa. Käytännössä siis vain muodostetaan eri kärkien koordinaattiyhdistelmistä tuplet ja näitä listaan. Jos listassa on jo kyseinen tuple, palautetaan sen indeksi, muuten luodaan listaan uusi alkio ja palautetaan sen indeksi (ks. getSimilar). Data tallennetaan itse luomaani binäärimuotoon, jota on helppo lukea C++:lla.
import bpy from struct import pack def triangulateMesh(mesh): import bmesh bm = bmesh.new() bm.from_mesh(mesh) bmesh.ops.triangulate(bm, faces=bm.faces) bm.to_mesh(mesh) bm.free() def debug(m): # print(m) pass context = bpy.context scene = context.scene objects = context.selected_objects hasMesh = False # Data to be saved datVertices = [] datVetexGroups = None datFaces = [] datBones = [] def getSimilar(datVertex): if datVertex not in datVertices: datVertices.append(datVertex) return datVertices.index(datVertex) # Go through every selected object for object in objects: if object.type == 'MESH': if hasMesh: print("Exported object can contain only one mesh") break # TODO Break debug("MESH") debug('\tName:' + object.name) # Get some variables mesh = object.to_mesh(scene, True, 'PREVIEW', calc_tessface=False) triangulateMesh(mesh) faces = mesh.polygons[:] vertices = mesh.vertices[:] uvLayer = mesh.uv_layers.active datVertexGroups = object.vertex_groups debug('\tVertex groups:') for g in object.vertex_groups: debug('\t\t' + g.name + ' (' + str(g.index) + ')') debug('\tVertices:') debug('\t\tCount: ' + str(len(vertices))) for v in vertices: debug('\t\t\t' + str(v.co)) debug('\tUvs:') debug('\t\tLength: ' + str(len(mesh.uv_layers.active.data))) for f in mesh.uv_layers.active.data: debug('\t\t\t' + str(f.uv)) debug('\tFaces:') debug('\t\tLength: ' + str(len(faces))) for face in faces: debug('\t\tFace:') newIndices = [] for i in face.loop_indices: l = mesh.loops[i] v = vertices[l.vertex_index] pos = v.co norm = v.normal uv = uvLayer.data[l.index].uv debug('\t\t\tVertex:') debug('\t\t\t\tPosition: ' + str(pos)) debug('\t\t\t\tNormal: ' + str(norm)) debug('\t\t\t\tUv: ' + str(uv)) debug('\t\t\tGroups:') groups = [(g.group, g.weight) for g in v.groups] debug('\t\t\t\t' + str(groups)) if len(groups) > 2: raise Exception('One vertex should have maximum two groups attached') while len(groups) < 2: groups.append([0, 0]) newIndices.append(getSimilar((pos, norm, uv, groups))) # Flip the order of vertices newIndices[0], newIndices[1] = newIndices[1], newIndices[0] datFaces.append(newIndices) hasMesh = True elif object.type == 'ARMATURE': print("ARMATURE") root = object.data.bones['ROOT'] def goThrough(bone, parentIndex): datBones.append((parentIndex, bone)) parentIndex = len(datBones) - 1 # Index of just added bone for child in bone.children: goThrough(child, parentIndex) goThrough(root, 2**16-1) else: print("Unknown type: " + object.type) # Save the file datFile = open('C:/Users/Henrik/Documents/visual studio 2013/Projects/Games/HappyLand/HappyLand/models/out.dat', 'wb') datGroupsToBones = {} for index, (parentI, bone) in enumerate(datBones): if bone.name in datVertexGroups: print(bone.name, datVertexGroups[bone.name]) datGroupsToBones[datVertexGroups[bone.name].index] = index datFile.write(pack('HHH', len(datVertices), len(datFaces), len(datBones))) for datVertex in datVertices: pos = datVertex[0] uv = datVertex[2] norm = datVertex[1] groups = datVertex[3] datFile.write(pack('=fff', pos.x, pos.z, pos.y)) datFile.write(pack('=ff', uv.x, uv.y)) datFile.write(pack('=fff', norm.x, norm.z, norm.y)) datFile.write(pack('=BB', datGroupsToBones[groups[0][0]], datGroupsToBones[groups[1][0]])) datFile.write(pack('=ff', groups[0][1], groups[1][1])) for datFace in datFaces: datFile.write(pack('HHH', datFace[0], datFace[1], datFace[2])) for datBone in datBones: parentIndex = datBone[0] bone = datBone[1] name = bone.name.encode('ascii') head = bone.head_local tail = bone.tail - bone.head datFile.write(pack('=H', len(name)) + name) datFile.write(pack('=H', parentIndex)) datFile.write(pack('=fff', head.x, head.z, head.y)) datFile.write(pack('=fff', tail.x, tail.z, tail.y)) datFile.close()
Aihe on jo aika vanha, joten et voi enää vastata siihen.