Tervehdys kaikille,
Olen tässä työstänyt jonkin aikaa omaa 3D Engineä OpenGL:llä. Tuli tässä kuitenkin mieleen, että olisi kiva koittaa tähän projektiin GLSL Shadereitä.
En kuitenkaan ole aivan varma mihin kaikkeen Shadereitä tulisi käyttää. perus taitaa olla tietenkin pikseli kohtainen valon laskenta.
Olisin kuitenkin halukas tietämään mitä kaikkea GLSL:llä on käytännössä mahdollista/kannattavaa tehdä. Onnistuuko esim. kaiken näköiset effectit ( tuli, salama jne jne... ), vai kannattaako nämä tehdä CPU:lla?
Periaatteessa kaikki OpenGL:n grafiikka käyttää (sisäisestikin) shadereitä. glBegin/glEnd jne on vanhentunutta tekniikkaa joita ei enää saisi käyttää. Uudemmilla MacOSX systeemeillä vanhentuneiden tekniikoiden tuki on jo poistunut kokonaan OpenGL 3:n myötä. Joten nyt viimeistään GLSL:n kimppuun.
PC-koneissa ne toimii vielä OpenGL 4:nkin kanssa yhteensopivuus-komponentin kautta.
Itse en ole käyttänyt glBegin/glEndiä pitkään aikaan. Käytän tällä hetkellä glDrawElements -funktiota.
Ymmärtääkseni Shadereillä ei kuitenkaan ole tarkoitus renderöidä itse modelleja, vaan luoda lisä tehosteita 3D maailmaan?
Esimerkiksi nämä normal mappaukset ja per pixel lightingit, mitä käytetään nykyaikaisessa grafiikassa suunnilleen kaikilla texturoiduilla pinnoilla, täytyy tehdä shadereilla, koska ainakaan tietääkseni OpenGL ei tarjoa valmiita ratkaisuja näitä efektejä varten. Siis myös itse modellit olisi tarkoitus renderöidä shadereilla.
Muutenkin, jos käyttää shadereita, kannattaa niitä käyttää kaikkeen ja dumpata kokonaan OpenGL fixed function pipeline, vaikka haluaisi ainoastaan yksinkertaisen teksturoidun modellin. Mikäli vielä muistan oikein, tätä fixed function pipelinea ei edes enää tarjota uusimmissa OpenGL versioissa.
OpenGL ES:ään ei muistaakseni ole ikinä kuulunut tuota 'fixed function pipeline'ä, miten sen nyt haluaisikaan suomentaa.
Verteksejä ja elementtejä ei nyt varsinaisesti renderöidä shadereilla, vaan ne ovat osa sitä koko renderöintiketjua.
Ja juu, OpenGL ei nykyseillään juurikaan tarjoa valmiita ratkaisuja, vaikka niitä saattaakin löytyä tuettuina yhteensopivuusasioiden takia.
Verteksejä (ja elementtejä) voi luoda geometria-shadereilla, jotka tosin vaatinevat suhteellisen uuden näyttökortin. Yleensä kuitenkin verteksit ja elementit määritellään erikseen ja siirretään näyttökortin muistiin.
Voit GLSL:llä tehdä käytännössä kaikki visuaaliset efektit, ja näyttiksellä noin muuten laskea vaikka Fibonaccin lukujonoja. Pikselipohjainen valaistus ja bump-mapping ovat ehkä ne yleisimmin käytetyt kohteet; Käyttökohteet kuitenkin ovat hyvin monipuolisia. Erilaiset efektit saattavat kuitenkin tarvita erinäistä oheisdataa, esimerkiksi tuo normaalimappaus mielellään ottaa vaikkapa tekstuurimuodossa kartan normaaleista.
GLSL:llä voit leikkiä webissä vaikka tällä: http://www.kickjs.org/example/shader_editor/
Joskus olen tutkiskellut esimerkkejä Lighthousesta: http://www.lighthouse3d.com/tutorials/glsl-tutorial/ , tosin ovat hiukan ajasta jäljessä, suurimman osan esimerkeistä ollessa GLSL 1.2:lle. Toimivatpahan ainakin valtaosalla. ;)
Stackoverflowsta löytyi vielä vinkkeinä: http://www.arcsynthesis.org/gltut/ , joka on näköjään geneerinen opas moderniin graffaohjelmointiin ja http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Chapter-2.3:-Rendering.html , joka on tarkemmin juuri OpenGL:lle ja GLSL:lle, tosin suppeahko.
Olisikohan tässä oppaan/isomman koodivinkin paikka vaikkapa WebGL:lle + GLSL:lle, tai ihan geneerisesti graffaohjelmoinnista OpenGL:n + GLSL:n kanssa ja parin efektin kera.. ;)
Anaatti kirjoitti:
Siis myös itse modellit olisi tarkoitus renderöidä shadereilla.
Tuo on jollekulle ehkä hieman harhaanjohtavasti sanottu, joten yritän selittää.
Ohjelman perusrakenne on ihan vastaava kuin ennenkin: pitää ladata mallit ja tekstuurit, syöttää ne OpenGL:lle ja kutsua asiaankuuluvia renderöintifunktioita. Shaderit eivät tee mitään itsekseen, ja pelkästään mallin renderöinti ilman värejä tai efektejä ei tietääkseni edelleenkään vaadi shadereita.
Kuitenkin suurin osa niistä tiedoista, jotka ennen syötettiin jollekin yhteen tarkoitukseen tehdylle funktiolle, syötetäänkin nyt shaderien muuttujiin, ja käytännössä kaikki väreihin liittyvät laskelmat (valot, tekstuurit) tehdään shadereissa.
Lisäksi shadereilla voi tehdä erilaisia efektejä. Kuitenkaan ihan kaikki ei ole mahdollista tai tehokasta suoraan yhdellä shaderilla, vaan joskus pitää ensin renderöidä kuva tekstuuriksi ja sitten vasta kokonaisuus ruudulle uuden efektishaderin kanssa.
Olen opetellut OpenGL:n alkeita näillä oppailla (Suomipelit.fi:stä):
Osa 1 - Ikkunan luominen
Osa 2 - 3D-grafiikka
Osa 3 - Teksturointi
Osa 4 - Valot ja varjot
Tarkoittaako tämä
User137 kirjoitti:
glBegin/glEnd jne on vanhentunutta tekniikkaa joita ei enää saisi käyttää.
että nuo oppaat ovat jo vanhentuneita ja täytyy opetella asiat uusiksi?
Hengilö, nuo oppaathan ovat melkein 10 vuotta vanhoja eivätkä olleet edes silloin kovin moderneja. Lisäksi oppaiden koodit ovat epäsiistejä ja melko huonoja ja aivan turhaan Windows-spesifisiä.
Eli jos olen täysin oikein ymmärtänyt renderöinti tapahtuu modernimmalla tavalla hieman samaan tapaan kuin teksturointi.
Eli luodaan glGenBuffers -funktiolla olio näytönohjaimen muistiin. Sidotaan olio näytnohjaimen yksikköön glBindBuffer -funktiolla, jonka jälkeen pusketaan verteksi data näytönohjaimelle glBufferData -funktiolla.
Tämän jälkeen kerrotaan näyttikselle mihin tapaan sen tulee lukea tietoa muististaan glVertexAttribPointer -funktiolla ja lopuksi käsketään näyttiksen renderöidä muistissaan oleva data glDrawArrays -funktiolla. glDrawArrays -funktion kutsu kierrätää näyttiksen verteksi datan shaderi ohjelman läpi, johon voidaan määrittää omia haluttuja toimintoja.
...ymmärsinkö oikein, vai onko jokin vieläkin "modernimpi" tapa renderöidä?
Suunnilleen noin, mutta pikselivarjostimelle (fragment shader) ei tietenkään anneta verteksidataa vaan jokainen renderöitävä pikseli. Sinänsä shaderien käyttö ei ole sidoksissa näihin luettelemiisi funktioihin, vaan ne toimisivat aivan samalla tavalla myös vanhan koodin kanssa.
....eli siis itse asiassa tämä ei muuta mitenkään minun nykyistä renderöinti tapaa ainoa vain etten ole tehnyt mitään Shadereillä. Alussa tosiaan oli hieman hämmentävää kun sanottiin että renderöinti tehtäisiin Shadereillä. Tosi asiassa Shaderit toimivat ilmeisemmin vain ns. Filttereinä.
Nykyinen koodini käyttää piirossa glDrawElements -funktiota glDrawArrays -funktion sijaan. Tämä lienee sama kumpaa funktiota periaatteessa käyttää?
Ja fragment shaderit tosiaan hanskaavat tuon pikseli kohtaisen eli interpoloi verteksien väliset pikselit ja ajaa ne shaderin läpi.
Kyllä, shaderit ovat tavallaan filttereitä (tosin joskus hyvin mutkikkaita).
Funktiot glDrawArrays ja glDrawElements ovat myös ihan toisiaan vastaavat, toinen vain piirtää verteksit järjestyksessä ja toinen käyttää erillistä indeksitaulukkoa. Indeksien etu on, että samaa verteksiä ei tarvitse toistaa datassa useaan kertaan, jolloin siirrettävän tiedon määrä vähenee ja ohjelma toimii nopeammin.
Kohtuullisen ajantasaista tietoa on mm. osoitteessa http://www.opengl-tutorial.org/.
Just tällä viikolla taistelin GLSL:n kanssa, nxPascal:iin liittyen (ja lopulta kaikki toimi kyllä). Päädyin 2D:n osalta tällaseen:
vertex shader:
#version 120 attribute vec2 in_position; attribute vec2 in_texCoord; attribute vec4 in_color; varying vec2 texCoord; varying vec4 color; uniform mat4 pmv; void main() { texCoord = in_texCoord; color = in_color; gl_Position = pmv * vec4(in_position, 0.0, 1.0); }
Fragment:
#version 120 varying vec2 texCoord; varying vec4 color; uniform sampler2D texture; uniform vec3 ambient; uniform vec3 diffuse; void main() { vec4 texColor = texture2D(texture, texCoord); gl_FragColor = texColor * (vec4(ambient, 0.0) + color * vec4(diffuse, 1.0)); }
Eli VBO verteksidata tulee attribuuttien kautta, ja uniformien avulla muutamia globaaleja arvoja, kuten kamera ja värit. Sitten pääohjelmassa käytetään funktioita: glBindBuffer(), glBufferData(), glVertexAttribPointer(), glEnableVertexAttribArray() ja glDrawArrays(), muun muassa...
Voin sanoa että on äärettömän hankalaa löytää tutoriaalia kaikkeen tuohon, kun verteksidata on muotoa
T2DVertex = packed record // size 8 float v: TVector2f; uv: TVector2f; color: TfRGBA; end;
Joitain linkkejä:
Wikibooks
Yleisiä ohjeita (ei koodia)
Ja kun sitä ei oikein missään kunnolla sanota, indeksi joka annetaan glVertexAttribPointer():lle, voi hakea suoraan shader-ohjelmasta glGetAttribLocation()-funktiolla. Indeksejä ei siis tarvitse alkaa itse kustomoimaan nollasta alkaen. Se lienee "layout-tekniikkaa"? Ei välttämättä toimi vanhemmilla ajureilla/korteilla. En ole ihan 100-varma.
Eiköhän nämä vastaukset tyydyttäneet tiedonjanoni tältä osin. Suur kiitos vastanneille.
Tähän vois vielä mainita että shadereitakin on päivitelty vuosien varrella. Vanhatkin shaderit tukee attribuutteja, mutta suurin osa oppaista käyttää aiemmin yleisempää, nyt vanhentutta koodia shadereissakin. Tällasia kun:
ftransform(), gl_Normal, gl_MultiTexCoord0, gl_NormalMatrix, gl_LightSource jne. ei enää sais käyttää.
Mutta sitten jos alkaa käyttää pelkästään uusinta OpenGL 3 tai 4 tyyliä, niin ei enää toimi vanhoilla korteilla. Luulen että ne layoutit on just uusinta uutta. Attribuutit pitäis olla kaikenkattavia.
Aihe on jo aika vanha, joten et voi enää vastata siihen.