Maybaygiare.org

Blog Network

shaderit

kuten Hello Triangle-kappaleessa mainittiin, shaderit ovat pieniä ohjelmia, jotka lepäävät GPU: lla. Nämä ohjelmat suoritetaan kunkin tietyn osan grafiikkaputken. Perusmerkityksessä shaderit ovat vain ohjelmia, jotka muuntavat panoksia tuotoksiksi. Shaderit ovat myös hyvin eristettyjä ohjelmia siinä mielessä, että ne eivät saa kommunikoida keskenään; ainoa kommunikaatio, joka niillä on, on niiden syötteiden ja ulostulojen kautta.

edellisessä luvussa sivuttiin lyhyesti varjostajien pintaa ja sitä, miten niitä oikein käytetään. Selitämme nyt shadereita ja erityisesti OpenGL-Varjostuskieltä yleisemmin.

GLSL

shaderit kirjoitetaan C-kielen kaltaisella GLSL-kielellä. GLSL on räätälöity käytettäväksi grafiikan kanssa ja sisältää hyödyllisiä ominaisuuksia, jotka on suunnattu erityisesti vektori-ja matriisimanipulaatioon.

shaderit alkavat aina versioilmoituksella, jota seuraa lista Tulo-ja lähtömuuttujista, univormuista ja sen päätehtävästä. Jokaisen shaderin syöttöpiste on sen päätoiminnossa, jossa käsittelemme mitä tahansa syöttömuuttujia ja tulostamme tulokset sen lähtömuuttujista. Älä huoli, jos et tiedä, mitä univormut ovat, palaamme niihin pian.

varjostimella on tyypillisesti seuraava rakenne:

#version version_numberin type in_variable_name;in type in_variable_name;out type out_variable_name; uniform type uniform_name; void main(){ // process input(s) and do some weird graphics stuff ... // output processed stuff to output variable out_variable_name = weird_stuff_we_processed;}

kun puhutaan nimenomaan vertex-varjostimesta, jokaista tulomuuttujaa kutsutaan myös vertex-attribuutiksi. On enimmäismäärä vertex attribuutteja meillä on lupa ilmoittaa rajoitettu laitteisto. OpenGL takaa, että käytettävissä on aina vähintään 16 4-komponenttista vertex-attribuuttia, mutta jotkut laitteistot voivat sallia enemmän, mitä voit hakea kyselemällä GL_MAX_VERTEX_ATTRIBS:

int nrAttributes;glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;

Tämä palauttaa usein minimin 16, jonka pitäisi riittää useimpiin tarkoituksiin.

tyypit

GLSL: ssä on muiden ohjelmointikielten tapaan tietotyyppejä, joilla voidaan määritellä, millaisen muuttujan kanssa halutaan työskennellä. GLSL: ssä on suurin osa tutuista oletusperustyypeistä, kuten C: intfloatdoubleuint ja bool. GLSL: stä löytyy myös kaksi konttityyppiä, joita tulemme käyttämään paljon, nimittäin vectors ja matrices. Keskustelemme matriiseista myöhemmin.

vektorit

vektori GLSL: ssä on 1,2,3-tai 4-komponenttinen kontti mille tahansa juuri mainitulle perustyypille. Ne voivat olla seuraavassa muodossa (n edustaa komponenttien määrää):

  • vecnn kelluu.
  • bvecn: vektori n booleans.
  • ivecnn kokonaislukuja.
  • n allekirjoittamattomat kokonaisluvut.
  • dvecn: vektori n kaksoiskomponentit.

suurimman osan ajasta käytämme perus – vecn koska kellukkeet riittävät useimpiin tarkoituksiimme.

vektorin komponentteihin pääsee vec.x missä x on vektorin ensimmäinen komponentti. Voit käyttää .x.y.z ja .w ensimmäisen, toisen, kolmannen ja neljännen komponentin käyttöön. GLSL mahdollistaa myös rgba väreille tai stpq tekstuurikoordinaateille, käyttäen samoja komponentteja.

vektoridatatyyppi mahdollistaa jonkin mielenkiintoisen ja joustavan komponenttivalinnan, jota kutsutaan swizzlingiksi. Swizzling antaa meille mahdollisuuden käyttää syntaksia näin:

vec2 someVec;vec4 differentVec = someVec.xyxx;vec3 anotherVec = differentVec.zyw;vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

voit käyttää mitä tahansa enintään 4 kirjaimen yhdistelmää luodaksesi uuden (samantyyppisen) vektorin, kunhan alkuperäisessä vektorissa on kyseiset komponentit; .z komponentti vec2 esimerkiksi. Voimme myös siirtää vektorit argumentteina erilaisille vektorikonstruktorin kutsuille, vähentäen vaadittavien argumenttien määrää:

vec2 vect = vec2(0.5, 0.7);vec4 result = vec4(vect, 0.0, 0.0);vec4 otherResult = vec4(result.xyz, 1.0);

vektorit ovat siten joustava datatyyppi, jota voimme käyttää kaikenlaisille tuloille ja ulostuloille. Koko kirjan näet runsaasti esimerkkejä siitä, miten voimme luovasti hallita vektoreita.

in and out

shaderit ovat sinänsä mukavia pieniä ohjelmia, mutta ne ovat osa kokonaisuutta ja siksi haluamme, että yksittäisillä shadereilla on panoksia ja tuotoksia, jotta voimme liikutella tavaraa ympäriinsä. GLSL määritteli in ja out avainsanat nimenomaan tätä tarkoitusta varten. Jokainen shader voi määrittää tulot ja lähdöt käyttäen näitä avainsanoja ja missä lähtö muuttuja vastaa tulo muuttuja seuraavan shader vaiheessa ne siirretään pitkin. Vertex ja fragment shader eroavat kuitenkin hieman.

vertex shader saanee jonkinlaisen syötön, muuten se olisi aika tehoton. Vertex shader eroaa sen tulo, että se saa syötteen suoraan vertex data. Määrittääksemme, miten vertex-data on järjestetty, määritämme syöttömuuttujat sijaintitietojen avulla, jotta voimme määrittää suorittimen vertex-attribuutit. Tämä on nähty edellisessä luvussa nimellä layout (location = 0). Vertex shader siis vaatii ylimääräistä asettelu erittely sen tuloa, jotta voimme yhdistää sen vertex tiedot.

on myös mahdollista jättäälayout (location = 0)määrittele ja kysy OpenGL-koodisi attribuuttipaikkoja glgetattriblocationin kautta, mutta asetan ne mieluummin vertex shader-merkistöön. Se on helpompi ymmärtää ja säästää (ja OpenGL) jonkin verran työtä.

toinen poikkeus on se, että fragment shader vaatii vec4 color output-muuttujan, koska fragment shaderin täytyy luoda lopullinen lähtöväri. Jos et määritä tulostusväriä fragmenttien varjostimessa, näiden fragmenttien väripuskurilähtö jää määrittelemättömäksi (mikä yleensä tarkoittaa, että OpenGL muuttaa ne joko mustiksi tai valkoisiksi).

joten jos haluamme lähettää dataa yhdestä varjostimesta toiselle, meidän täytyy ilmoittaa tuloste lähettävässä varjostimessa ja vastaava tulo vastaanottavassa varjostimessa. Kun tyypit ja nimet ovat yhtä suuret molemmin puolin, OpenGL yhdistää nämä muuttujat yhteen ja sitten on mahdollista lähettää dataa shaderien välillä (tämä tapahtuu ohjelmaobjektin linkittämisen yhteydessä). Näyttää, miten tämä toimii käytännössä aiomme muuttaa varjostimet edellisestä luvusta antaa vertex shader päättää värin fragment shader.

Vertexcolor

#version 330 corelayout (location = 0) in vec3 aPos; // the position variable has attribute position 0 out vec4 vertexColor; // specify a color output to the fragment shadervoid main(){ gl_Position = vec4(aPos, 1.0); // see how we directly give a vec3 to vec4's constructor vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // set the output variable to a dark-red color}

Fragment shader

#version 330 coreout vec4 FragColor; in vec4 vertexColor; // the input variable from the vertex shader (same name and same type) void main(){ FragColor = vertexColor;} 

voit nähdä, että julistimme vertexColor-muuttujan vec4 lähdön, jonka asetimme vertexcolor-varjostimeen ja julistamme samankaltaisen vertexColor-tulon fragment shader: iin. Koska molemmilla on sama tyyppi ja nimi, fragmenttivarjostimen vertexColor liittyy verteksivarjostimen vertexcoloriin. Koska asetamme värin tummanpunaiseen väriin vertex-varjostimessa, myös tuloksena olevien fragmenttien pitäisi olla tummanpunaisia. Seuraava kuva näyttää tulosteen:

There we go! Onnistuimme juuri lähettämään arvon vertex-varjostimesta fragment-varjostimeen. Maustetaan sitä hieman ja katsotaan, jos voisimme lähettää värin hakemuksestamme fragment shader!

univormut

univormut ovat toinen tapa siirtää dataa suorittimen sovelluksesta GPU: n shadereille. Univormut ovat kuitenkin hieman erilaisia vertex-attribuutteihin verrattuna. Ensinnäkin univormut ovat maailmanlaajuisia. Globaali, mikä tarkoittaa, että yhtenäinen muuttuja on yksilöllinen Shader-ohjelman objektia kohti, ja sitä voi käyttää mistä tahansa shader-ohjelmasta missä tahansa Shader-ohjelman vaiheessa. Toiseksi, mihin tahansa asetat univormun arvon, univormut säilyttävät arvonsa, kunnes ne joko nollataan tai päivitetään.

julistaaksemme univormun GLSL: ssä lisäämme yksinkertaisesti uniform avainsanan varjostimeen, jolla on tyyppi ja nimi. Siitä lähtien voimme käyttää shaderin univormua. Katsotaan, jos tällä kertaa voimme asettaa värin kolmion kautta yhtenäinen:

#version 330 coreout vec4 FragColor; uniform vec4 ourColor; // we set this variable in the OpenGL code.void main(){ FragColor = ourColor;} 

julistimme yhtenäisen vec4 värimme fragmenttivarjossa ja asetimme fragmentin lähdön värin tämän yhtenäisen arvon sisällölle. Koska univormut ovat globaaleja muuttujia, voimme määritellä ne missä tahansa Shader-vaiheessa, joten ei tarvitse käydä vertex shader uudelleen läpi saadakseen jotain fragment shader. Emme käytä tätä univormua vertex-varjostimessa, joten sitä ei tarvitse määritellä siellä.

jos julistat yhtenäisen, jota ei käytetä missään GLSL-koodissasi, kääntäjä poistaa muuttujan hiljaa käännetystä versiosta, mikä on syynä useisiin turhauttaviin virheisiin; pidä tämä mielessä!

univormu on tällä hetkellä tyhjä; Emme ole vielä lisänneet mitään dataa univormuun, joten kokeillaan sitä. Meidän täytyy ensin löytää indeksi/sijainti yhtenäinen attribuutti meidän shader. Kun meillä on indeksi/sijainti yhtenäinen, voimme päivittää sen arvot. Sen sijaan, että fragment shader siirtäisi yhden värin, maustetaan asioita muuttamalla väriä vähitellen ajan myötä. :

float timeValue = glfwGetTime();float greenValue = (sin(timeValue) / 2.0f) + 0.5f;int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");glUseProgram(shaderProgram);glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

ensin haetaan juoksuaika sekunneissa glfwgettimen () kautta. Sitten vaihtelemme väriä alueella 0.01.0 käyttämällä sin-funktiota ja tallennamme tuloksen vihreään arvoon.

tämän jälkeen tiedustelemme ourColor-univormun sijaintia glgetuniformlocationin avulla. Toimitamme Shader-ohjelman ja sen uniformun nimen (josta haluamme hakea sijainnin) kyselytoiminnolle. Jos glGetUniformLocation palaa -1, se ei löytänyt sijaintia. Lopuksi voimme asettaa yhtenäisen arvon käyttämällä glUniform4f funktio. Huomaa, että yhtenäisen sijainnin löytäminen ei edellytä shader-ohjelman käyttämistä ensin, mutta univormun päivittäminen edellyttää ohjelman käyttämistä ensin (kutsumalla glUseProgram-ohjelmaa), koska se asettaa univormun aktiiviselle shader-ohjelmalle.

koska OpenGL on ytimeltään C-kirjasto, sillä ei ole natiivitukea funktion ylikuormitukselle, joten missä tahansa funktiota voidaan kutsua eri tyypeillä OpenGL määrittelee uudet funktiot kullekin vaaditulle tyypille; glUniform on täydellinen esimerkki tästä. Funktio vaatii tietyn Postfixin sille univormun tyypille, jonka haluat asettaa. Muutamia mahdollisia jälkikorjauksia ovat:

  • f: funktio odottaa arvokseen float.
  • i: funktio odottaa arvokseen int.
  • ui: funktio odottaa arvokseen unsigned int.
  • 3f: funktio odottaa arvokseen 3 floats.
  • fv: funktio odottaa arvokseen float vektori/array.

kun haluat määrittää OpenGL: n vaihtoehdon, valitse tyyppiäsi vastaava ylikuormitettu toiminto. Meidän tapauksessamme haluamme asettaa 4 kelluketta yksitellen, jotta siirrämme tietomme glUniform4f: n kautta (huomaa, että olisimme voineet käyttää myösfvversio).

nyt kun osaamme asettaa yhtenäisten muuttujien arvot, Voimme käyttää niitä renderöintiin. Jos haluamme värin muuttuvan vähitellen, haluamme päivittää tämän yhtenäisen jokaisen kehyksen, muuten kolmio säilyttäisi yhden yksivärisen, jos asetamme sen vain kerran. Laskemme siis greenvaluen ja päivitämme uniformin jokaisen renderöinnin:

while(!glfwWindowShouldClose(window)){ // input processInput(window); // render // clear the colorbuffer glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // be sure to activate the shader glUseProgram(shaderProgram); // update the uniform color float timeValue = glfwGetTime(); float greenValue = sin(timeValue) / 2.0f + 0.5f; int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f); // now render the triangle glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3); // swap buffers and poll IO events glfwSwapBuffers(window); glfwPollEvents();}

koodi on suhteellisen suoraviivainen sovitus edellisestä koodista. Tällä kertaa päivitämme yhtenäisen arvon jokaiselle kehykselle ennen kolmion piirtämistä. Jos päivität univormun oikein, näet kolmion värin muuttuvan vähitellen vihreästä mustaksi ja takaisin vihreäksi.

Katso lähdekoodi täältä, jos olet jumissa.

kuten näette, univormut ovat hyödyllinen työkalu määritteiden asettamiseen, jotka voivat muuttaa jokaista kehystä, tai tietojen vaihtamiseen sovelluksen ja varjostimien välillä, mutta entä jos haluamme asettaa värin jokaiselle huippupisteelle? Siinä tapauksessa meidän pitäisi julistaa niin monta univormua kuin meillä on vertices. Parempi ratkaisu olisi lisätä enemmän tietoa huippupiste attribuutteja, joka on mitä aiomme tehdä nyt.

lisää attribuutteja!

näimme edellisessä luvussa, miten voimme täyttää VBO: n, määrittää vertex-attribuutin osoittimet ja tallentaa kaiken VAO: han. Tällä kertaa vertex-dataan halutaan myös lisätä väridataa. Lisäämme väridatan 3 floats vertices array. Annamme punaisen, vihreän ja sinisen värin jokaiselle kolmiomme kulmalle:

float vertices = { // positions // colors 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom right -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom left 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // top }; 

koska meillä on nyt enemmän dataa lähetettäväksi vertex shader: iin, on välttämätöntä säätää vertex shader: ia, jotta saamme myös väriarvomme vertex-attribuuttitulona. Huomaa, että asetimme acolor-attribuutin sijaintipaikaksi 1 layout specifierillä:

#version 330 corelayout (location = 0) in vec3 aPos; // the position variable has attribute position 0layout (location = 1) in vec3 aColor; // the color variable has attribute position 1 out vec3 ourColor; // output a color to the fragment shadervoid main(){ gl_Position = vec4(aPos, 1.0); ourColor = aColor; // set ourColor to the input color we got from the vertex data} 

koska emme enää käytä yhtenäistä fragmentin värille, vaan käytämme nyt ourColor-lähtömuuttujaa, joudumme muuttamaan myös fragmentinsuojainta:

#version 330 coreout vec4 FragColor; in vec3 ourColor; void main(){ FragColor = vec4(ourColor, 1.0);}

koska lisäsimme toisen vertex-attribuutin ja päivitimme VBO: n muisti meidän täytyy uudelleen määrittää vertex attribute osoittimet. Päivitetty data VBO: n muistissa näyttää nyt hieman tältä:

Interleaved data of position and color in VBO to be configured wtih functionglVertexAttribPointer/function

tietäen nykyisen layout we can update the vertex format with glVertexAttribPointer:

// position attributeglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// color attributeglVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));glEnableVertexAttribArray(1);

glVertexAttribPointer ’ n ensimmäiset argumentit ovat suhteellisen suoraviivaisia. Tällä kertaa määritämme vertex-attribuuttia attribuutin sijaintipaikalla 1. Väriarvojen koko on 3floats, emmekä normalisoi arvoja.

koska meillä on nyt kaksi huippupiste-attribuuttia, meidän on laskettava stride-arvo uudelleen. Saadaksemme seuraavan attribuutin arvon (esim.seuraava x paikkavektorin komponentti) datajoukossa meidän on siirrettävä 6floats oikealle, kolme positioarvoille ja kolme väriarvoille. Näin saadaan stride-arvo 6 kertaa suurempi kuin float tavuina (= 24 tavuina).
myös tällä kertaa joudutaan määrittelemään offset. Jokaiselle huippupisteelle positio vertex-attribuutti on ensin, joten ilmoitamme offsetiksi 0. Väri-attribuutti alkaa paikkatiedon jälkeen, joten siirtymä on 3 * sizeof(float) tavuina (= 12 tavua).

sovelluksen suorittamisen pitäisi johtaa seuraavaan kuvaan:

Katso lähdekoodi täältä, jos olet jumissa.

kuva ei välttämättä ole aivan sellainen kuin odottaisi, sillä toimitimme vain 3 väriä, ei nyt nähtävää valtavaa väripalettia. Tämä kaikki on tulosta fragmentaarisesta interpolaatiosta fragment shaderissa. Kun tekee kolmio rasterization vaiheessa yleensä johtaa paljon enemmän fragmentteja kuin vertices alun perin määritelty. Rasterizer sitten määrittää kantoja kunkin näistä fragmenttien perusteella, missä ne asuvat kolmion muoto.
näiden asemien perusteella se interpoloi kaikki fragment shaderin tulomuuttujat. Sano esimerkiksi, että meillä on linja, jossa ylempi piste on vihreä väri ja alempi piste sininen väri. Jos fragmentinvarjostin suoritetaan fragmentille, joka sijaitsee noin kohdassa 70% viivalla, sen tuloksena syntyvä värituloattribuutti olisi tällöin vihreän ja sinisen lineaarinen yhdistelmä; tarkemmin: 30% sininen ja 70% vihreä.

kolmiossa tapahtui juuri näin. Meillä on 3 kärkipistettä ja siten 3 väriä, ja päätellen kolmion pikseleistä se luultavasti sisältää noin 50000 fragmenttia, jossa fragmentti shader interpoloi värit näiden pikselien joukossa. Jos katsot värejä tarkkaan, näet, että kaikki käy järkeen: punaisesta siniseen pääsee ensin violettiin ja sitten siniseen. Fragment interpolointia sovelletaan kaikkiin fragment shaderin tuloattribuutteihin.

oma shader-luokkamme

shaderien kirjoittaminen, kokoaminen ja hallinta voi olla varsin työlästä. Viimeisenä silauksena shader-aiheeseen aiomme tehdä elämästämme hieman helpompaa rakentamalla shader-luokan, joka lukee shadereita levyltä, kokoaa ja linkittää ne, tarkistaa virheet ja on helppokäyttöinen. Tämä antaa sinulle myös hieman käsityksen siitä, miten voimme kapseloida osan tähän mennessä oppimastamme tiedosta hyödyllisiksi abstrakteiksi esineiksi.

luomme shader-luokan kokonaan otsikkotiedostoksi, lähinnä oppimistarkoituksia ja siirrettävyyttä varten. Aloitetaan lisäämällä tarvittavat sisällöt ja määrittelemällä luokkarakenne:

#ifndef SHADER_H#define SHADER_H#include <glad/glad.h> // include glad to get all the required OpenGL headers #include <string>#include <fstream>#include <sstream>#include <iostream> class Shader{public: // the program ID unsigned int ID; // constructor reads and builds the shader Shader(const char* vertexPath, const char* fragmentPath); // use/activate the shader void use(); // utility uniform functions void setBool(const std::string &name, bool value) const; void setInt(const std::string &name, int value) const; void setFloat(const std::string &name, float value) const;}; #endif

käytimme useita esiprosessoriohjeita otsikkotiedoston yläosassa. Käyttämällä näitä pieniä koodirivejä kääntäjä ilmoittaa sisällyttää ja kääntää tämän otsikkotiedoston vain, jos se ei ole vielä sisällytetty, vaikka useita tiedostoja Sisältää Shader otsikko. Tämä estää konfliktien yhdistämisen.

shader-luokalla on shader-ohjelman tunnus. Sen rakentaja vaatii tiedoston polkuja lähdekoodin vertex ja fragment shader vastaavasti, että voimme tallentaa levylle yksinkertaisia tekstitiedostoja. Lisätä hieman ylimääräistä lisäämme myös useita apuohjelma toimintoja helpottaa elämäämme hieman: käyttö aktivoi shader ohjelma, ja kaikki asetettu… funktiot tiedustelevat yhtenäistä sijaintia ja asettavat sen arvon.

tiedoston lukeminen

käytämme C++ – tiedostovirtoja lukeaksemme tiedoston sisällön useiksi string objects:

Shader(const char* vertexPath, const char* fragmentPath){ // 1. retrieve the vertex/fragment source code from filePath std::string vertexCode; std::string fragmentCode; std::ifstream vShaderFile; std::ifstream fShaderFile; // ensure ifstream objects can throw exceptions: vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); try { // open files vShaderFile.open(vertexPath); fShaderFile.open(fragmentPath); std::stringstream vShaderStream, fShaderStream; // read file's buffer contents into streams vShaderStream << vShaderFile.rdbuf(); fShaderStream << fShaderFile.rdbuf(); // close file handlers vShaderFile.close(); fShaderFile.close(); // convert stream into string vertexCode = vShaderStream.str(); fragmentCode = fShaderStream.str(); } catch(std::ifstream::failure e) { std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; } const char* vShaderCode = vertexCode.c_str(); const char* fShaderCode = fragmentCode.c_str(); 

seuraavaksi on käännettävä ja linkitettävä shaderit. Huomaa, että olemme myös tarkistaa, jos compilation/linkittäminen epäonnistui ja jos näin, tulosta compile-time virheet. Tämä on erittäin hyödyllinen virheenjäljityksessä (tarvitset ne virhelokit lopulta):

// 2. compile shadersunsigned int vertex, fragment;int success;char infoLog; // vertex Shadervertex = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertex, 1, &vShaderCode, NULL);glCompileShader(vertex);// print compile errors if anyglGetShaderiv(vertex, GL_COMPILE_STATUS, &success);if(!success){ glGetShaderInfoLog(vertex, 512, NULL, infoLog); std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}; // similiar for Fragment Shader // shader ProgramID = glCreateProgram();glAttachShader(ID, vertex);glAttachShader(ID, fragment);glLinkProgram(ID);// print linking errors if anyglGetProgramiv(ID, GL_LINK_STATUS, &success);if(!success){ glGetProgramInfoLog(ID, 512, NULL, infoLog); std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;} // delete the shaders as they're linked into our program now and no longer necessaryglDeleteShader(vertex);glDeleteShader(fragment);

käyttöfunktio on suoraviivainen:

void use() { glUseProgram(ID);} 

vastaavasti mille tahansa uniformiselle setterifunktiolle:

void setBool(const std::string &name, bool value) const{ glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); }void setInt(const std::string &name, int value) const{ glUniform1i(glGetUniformLocation(ID, name.c_str()), value); }void setFloat(const std::string &name, float value) const{ glUniform1f(glGetUniformLocation(ID, name.c_str()), value); } 

ja siinä se, täydennetty shader-Luokka. Shader-luokan käyttäminen on melko helppoa; luomme shader-olion kerran ja siitä lähtien yksinkertaisesti alamme käyttää sitä:

Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs");while(...){ ourShader.use(); ourShader.setFloat("someUniform", 1.0f); DrawStuff();}

täällä tallennimme vertex-ja fragment shader-lähdekoodin kahteen tiedostoon, joiden nimet ovat shader.vs ja shader.fs. Voit vapaasti nimetä Shader-tiedostosi miten haluat; Itse pidän laajennuksia .vs ja .fs melko intuitiivisina.

lähdekoodi löytyy täältä vastaperustetun shader-luokkamme avulla. Huomaa, että voit klikata shader-tiedostopolkuja löytääksesi shaderin lähdekoodin.

harjoitukset

  1. Säädä verteksivarjostin niin, että kolmio on ylösalaisin: ratkaisu.
  2. Määritä vaakasiirtymä uniformin kautta ja siirrä kolmio näytön oikealle puolelle verteksivarjossa käyttäen tätä offset-arvoa: ratkaisu.
  3. tulostetaan verteksiasento fragmentin varjostimeen out avainsanalla ja asetetaan fragmentin väri yhtä suureksi kuin tämä kärkiasento (katso, miten jopa kärkipisteen sijaintiarvot interpoloidaan kolmion yli). Kun olet onnistunut tekemään tämän; yritä vastata seuraavaan kysymykseen: miksi kolmion vasen alaosa on musta?: ratkaisu.

Vastaa

Sähköpostiosoitettasi ei julkaista.