Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: glDrawElement ja UV Indices

kayttaja-3842 [15.01.2015 22:44:45]

#

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

Tzaeru [16.01.2015 08:09:00]

#

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.

kayttaja-3842 [16.01.2015 08:33:59]

#

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."

Tzaeru [16.01.2015 09:09:48]

#

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.

kayttaja-3842 [17.01.2015 17:18:24]

#

Tämä avarsi mieltäni. Kiitos tästä. :)

User137 [22.01.2015 19:14:53]

#

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.

Hennkka [22.01.2015 21:09:37]

#

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()

Vastaus

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

Tietoa sivustosta