Material+ kioptimalizált uniformok Szécsi László 3D Grafikus Rendszerek hack
Használatlan uniformok kioptimalizálva nem jelenik meg a reflectionben hibát okoz, pedig gyakran csak tesztelés miatt hagyunk ki valamit legyen csak warning
Közös uniformok – hozzuk létre előre Material.shared = { // csak példa modelMatrix : new Mat4(), modelMatrixInverse : new Mat4(), modelViewProjMatrix : new Mat4(), rayDirMatrix : new Mat4(), eyePos : new Vec3(), texOffset : new Vec2(), }; // Lehet ennél kifinomultabb megoldást is alkalmazni // ahol nem kell megismételni JS-ben a uniform típusát. // De a közös uniform úgyis több shaderben is megjelenhet, // ahol a típusoknak azonosnak kell lennie. // Így a legegyszerűbb.
Material uniformok – csak warning legyen a hiányzó tulajdonság, ne hiba // Material konstruktor végén return new Proxy(this, { get : function(target, name){ if(!(name in target)){ console.error( "WARNING: Ignoring attempt" + " to access material property '" + name + "'. Is '" + name + "' an unused uniform?" ); return Material.dummy; } return target[name]; }, });
Dummy objektum //bármely függvényhívás vagy tulajdonságelérés //megengedett, de hatástalan Material.dummy = new Proxy(new Function(), { get: function(target, name){ return Material.dummy; }, apply: function(target, thisArg, args){ });
Szintfelület sugárkövetése Szécsi László 3D Grafikus Rendszerek 4. labor
Környezet megjelenítése háttérként teljes képernyős téglalapot kell rajzolni (hurrá!) új VS: kiszámolja a sugárirányt át kell adni a képernyőkoordinátából-világkoordináta- mínusz-szempozíció-számító mátrixot (a.k.a. rayDirMatrix) a kamera ezt kiszámolja nem transzformál (mert full viewport quad) z=0.99999, minden mőgé FS megkapja a VS-től varying-ben a sugárirányt ezzel címzi a textúrát visszaadja a kapott színt
Sugárirány kiszámítása NDC-ből (EVP)-1 -et nevezzük rayDirMatrix-nak
TextureCube.js - betöltés var TextureCube = function(gl, mediaFileUrls) { gl.pendingResources[mediaFileUrls[0]] = ++gl.pendingResources[mediaFileUrls[0]] || 1; this.mediaFileUrls = mediaFileUrls; this.glTexture = gl.createTexture(); this.loadedCount = 0; this.images = []; var theTexture = this; for(var i=0; i<6; i++){ this.images[i] = new Image(); this.images[i].onload = function() { theTexture.loaded(gl); } this.images[i].src = mediaFileUrls[i]; } };
TextureCube.js – erőforrás létrehozása TextureCube.prototype.loaded = function(gl){ this.loadedCount++; if(this.loadedCount < 6) return; gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.glTexture); for(var i=0; i<6; i++){ gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.images[i]); } gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); gl.generateMipmap(gl.TEXTURE_CUBE_MAP); gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); if( --gl.pendingResources[this.mediaFileUrls[0]] === 0 ) { delete gl.pendingResources[this.mediaFileUrls[0]]; };
Háttér fragment shader gyártsuk le a TextureCube-ot, kössük be a fenti FS-t használó Material-ba, a GameObject használja ezt az anyagot // kell egy sampler uniform uniform samplerCube envMapTexture; // kiolvasni a sugárirányban textureCube( envMapTexture, rayDir) this.skyCubeTexture = new TextureCube(gl, [ "media/posx512.jpg", "media/negx512.jpg", "media/posy512.jpg", "media/negy512.jpg", "media/posz512.jpg", "media/negz512.jpg",] ); this.backgroundMaterial. envMapTexture.set ( this.skyCubeTexture);
Ne felejtsük el a GameObject adja át a rayDirMatrix-ot az új shadereket vegyük be az index.HTML-be gyártsuk le szükséges shader, program, material objektumokat a doboztextúrát kössük be a háttér anyagába legyen quadGeometry legyen egy mesh a quadGeometry és a fenti material használatával legyen egy gameObject a mesh-sel
Várt eredmény – egér forgat
Terep textúrából uniform sampler2D noiseTexture; float f(vec3 p){ return texture2D(noiseTexture, p.xz * 0.01).r - p.y; } freki y = 0 sík implicit egyenlete, ehhez keverjük a 2D zajt terep implicit függvénye
Kilépési, belépési pont uniform vec3 eye; varying vec3 rayDir; uniform samplerCube background; void main(void) { vec3 d = normalize(rayDir); float t1 = (1.0 - eye.y) / d.y; float t2 = (0.0 - eye.y) / d.y; float tstar = max(min(t1, t2), 0.0); float tend = max(max(t1, t2), 0.0);
Ray marching vec3 p = eye + d * tstar; vec3 step = d * min((tend - tstar)/128.0, 0.05); //feladat: // ciklus fut 128-szor // p léptetése step-pel // ha elértük a felületet, break
Magasság szerinti árnyalás ha az utolsó tesztnél még nem értük el a felületet, akkor adjuk vissza a háttérszínt különben p.y-t
Várt eredmény
Normálvektor megjelenítése vec3 gradient = vec3( f(p + vec3(+0.05, 0.0, 0.0) ) - f(p + vec3(-0.05, 0.0, 0.0) ) , f(p + vec3(0.0, +0.05, 0.0) ) - f(p + vec3(0.0, -0.05, 0.0) ) , f(p + vec3(0.0, 0.0, +0.05) ) - f(p + vec3(0.0, 0.0, -0.05) ) ); //normalizáljuk, adjuk vissza, mint színt
Várt eredmény
Változó lépéshossz ray marchingban minden ciklusban növeljük a lépést 2%-kal kezdeti lépés nem a teljes táv 128-ada, hanem 580- ada (geometriai sor)
Várt eredmény
Bináris keresés ray marching ciklus után, ha volt metszés, az eredmény pontosítása pozíció visszállítása fél lépéssel ciklusban lépés felezése előre vagy hátralépés attól függően, hogy a felület alatt vagy felett vagyunk-e
Várt eredmény
Cseréljük le az implicit függvényt 3D zajra nem magasságmező, lesznek hidak is normálvektor analitikusan számolható
Egyszerű zaj freki float f(vec3 r) { vec3 s = vec3(7502, 22777, 4767); float w = 0.0; for(int i=0; i<16; i++) { w += sin( dot(s - vec3(32768, 32768, 32768), r * 40.0) / 65536.0); s = mod(s, 32768.0) * 2.0 + floor(s / 32768.0); } return w / 32.0 + 0.5 - r.y; y = 0 sík implicit egyenlete, ehhez keverjük a 3D zajt
Egyszerű zaj gradiense vec3 fGrad(vec3 r) { vec3 s = vec3(7502, 22777, 4767); vec3 w = vec3(0.0, 0.0, 0.0); for(int i=0; i<16; i++) { w += cos( dot(s - vec3(32768, 32768, 32768), r*40.0) / 65536.0) * (s - vec3(32768, 32768, 32768)) * 40.0; s = mod(s, 32768.0) * 2.0 + floor(s / 32768.0); } return w / 65536.0 - vec3(0, 1, 0);
Várt eredmény
Köd elnyelődés csillapítás kiszóródás kibocsájtás forráshatás radiancia változása beszóródás csillapítási tényező forrástag egység hosszon a sugár mentén radiancia épp itt
Diffegyenlet megoldása elveszett százalék radiancia a sugár-felület metszéspontban radiance a sugár keződponjában, a metszésponttól távolságra összeszedett többletradiancia
Feladat: köd válasszunk csillapítást és forrástagot, tudjuk ezeket könnyen változtatni ergo érdemes ezekre változót gyártani lehet skalár, vagy RGB, ha színes a köd a fenti képletet értékeljük ki a FS végefelé a háttér végtelen messze legyen
Várt eredmény nulla forráshatás, fekete köd
Várt eredmény színes köd, nem látszik a háttér (nyilván)
Nem-konstans köd lejjebb sűrűbb, feljebb ritkább, exponenciálisan extra Nem-konstans köd lejjebb sűrűbb, feljebb ritkább, exponenciálisan y a sugáron
Diffegyenlet megoldás extra Diffegyenlet megoldás
extra Várt eredmény
Házi feladat metaball ray marching implicit függvény mozgóközéppontú radiális bázisfüggvények összege ebben keressük a szintfelületet
Várt eredmény kb.
Metaball implicit egyenlet általában f t -A
2 metaball: A szintérték hatása
Lehetséges metaball-súlyfüggvények Blinn Perlin Wyvill f r ha r > 1, akkor 0 ha r > 1, akkor 0
Ray marching f t
Házihoz legyen Phong BRDF vagy env mapping legalább 3 labda legyen animálódjanak a labdák középpontjai Blinn bázisfüggvény teljesen elég a többi akkor hasznos ha véges tartójú kell, hogy a távoliakat ne kelljen hozzáadni Gradiens számítása analitikusan nem differenciákkal közelítve