




[{"content":"","date":"29 de juny 2026","externalUrl":null,"permalink":"/ca/blog/","section":"Blogs","summary":"","title":"Blogs","type":"blog"},{"content":"He estat seguint la masterclass d\u0026rsquo;Iñigo Quilez «Live Coding \u0026ldquo;Happy Jumping\u0026rdquo;», on implementa a ShaderToy un shader amb raymarching que genera una escena amb un monstre saltador passejant per un terreny peculiar. Per a ell era un exercici d\u0026rsquo;animació, ja que aplica principis com el squash and stretch.\nPer a mi, en canvi, és una oportunitat per aprendre com es construeixen aquestes escenes tridimensionals fent servir shaders, sense models 3D, només amb matemàtiques i codi. Amb aquest article intento ampliar alguns conceptes perquè quedin més clars i aprofundir una mica més en el tema.\nMira el shader \u0026ldquo;Happy Jumping\u0026rdquo; a ShaderToy\nRaymarching\rSegons Iñigo Quilez, el raymarching es pot entendre com una tècnica per explorar una escena tridimensional mitjançant rajos. Cada píxel de la pantalla genera un raig que s\u0026rsquo;endinsa a l\u0026rsquo;espai virtual i cerca les superfícies presents a l\u0026rsquo;escena. Amb raymarching, l\u0026rsquo;escena es descriu mitjançant distàncies en lloc de calcular de forma exacta la intersecció entre un raig i la geometria (ray tracing). L\u0026rsquo;algorisme avança de forma iterativa al llarg del raig utilitzant la distància estimada fins a la superfície més propera.\nA cada iteració s\u0026rsquo;avalua una Signed Distance Function (SDF), una funció que retorna la distància amb signe fins a la superfície més propera. Aquest valor determina quant pot avançar el raig de manera segura sense travessar cap objecte. Quan la superfície és lluny, els passos poden ser grans; a mesura que el raig s\u0026rsquo;apropa a un objecte, els increments es redueixen progressivament, augmentant la precisió del càlcul.\nDe GPU Gems 2: Chapter 8.\nGràcies a aquest mecanisme, és possible representar escenes complexes definides únicament mitjançant funcions matemàtiques, sense necessitat de malles, vèrtexs o estructures geomètriques tradicionals.\n// This normalize the coordinates from (-aspectRatio,-1) to (aspectRatio, 1) vec2 p = (2.0*(fragCoord+offset)-iResolution.xy) / iResolution.y; vec3 ro = vec3(0.0,0.0,2.0); vec3 rd = normalize(vec3(p,-1.5)); // if ray doesn\u0026#39;t hit anything value -1 vec4 res = vec4(-1.0, -1.0, 0.0,1.0); float t = 0.0; for(int i = 0; i \u0026lt; 128; ++i) { vec3 pos = ro + t* rd; // map function will return a distance that is secure to advance vec4 h = map(pos,atime); t+=h.x; } Pseudocodi del raymarching\nAra bé, com es calcula aquesta distància màxima que es pot avançar sense travessar els diferents objectes? No hi ha una resposta única: depenent del tipus d\u0026rsquo;objecte hi ha diferents fórmules. El cas més senzill és el d\u0026rsquo;una esfera:\nDemostració del càlcul de h\nLa distància màxima (h) a la qual podem avançar sense travessar l\u0026rsquo;esfera és la distància entre l\u0026rsquo;origen del raig i el centre de l\u0026rsquo;esfera, menys el radi d\u0026rsquo;aquesta. Amb això ja podem començar a pintar alguna cosa: en el moment que sabem que hem trobat l\u0026rsquo;esfera, la podem mostrar en pantalla i fins i tot començar a il·luminar-la.\nPrimer render d\u0026rsquo;una esfera amb raymarching\nCalcular normals\rPer poder calcular la il·luminació de l\u0026rsquo;escena, primer hem de calcular les normals dels objectes amb què col·lisiona el raig. Quan sabem a quina distància t es produeix la col·lisió, podem trobar el punt exacte de la superfície, i a partir d\u0026rsquo;ell calcular la normal.\nvec3 pos = ro + t*rd; vec3 nor = calcNormal( pos, time ); Com que la funció map() és contínua, la direcció en què la distància canvia més ràpid (el seu gradient) coincideix amb la normal de la superfície. Per aproximar aquest gradient, prenem el punt de col·lisió i el desplacem lleugerament en cada eix (x, y, z), calculant la diferència de distància entre desplaçar el punt cap a un costat i cap a l\u0026rsquo;altre (diferències finites centrades). El resultat, normalitzat, ens dona la direcció de la normal.\nvec3 calcNormal( in vec3 pos, float time ) { float e = 0.0001; // petit desplaçament float dx = map( pos + vec3(e,0,0), time ).x - map( pos - vec3(e,0,0), time ).x; float dy = map( pos + vec3(0,e,0), time ).x - map( pos - vec3(0,e,0), time ).x; float dz = map( pos + vec3(0,0,e), time ).x - map( pos - vec3(0,0,e), time ).x; return normalize( vec3(dx, dy, dz) ); } Diferència finita centrada: en lloc de comparar el punt de col·lisió amb un únic punt desplaçat, comparem dos punts situats simètricament a banda i banda (pos+e i pos-e). Això dona una aproximació més precisa de la derivada que si només miréssim cap a un costat, ja que promitja la variació en ambdues direccions.\nCàlcul de llum\rSun Diffuse\rPer calcular quanta llum directa del sol rep un punt de la superfície, fem servir la normal de l\u0026rsquo;objecte i la direcció cap a la llum (el sol, en aquest cas). Calculem el producte escalar (dot product) entre tots dos vectors; com que tots dos són vectors unitaris (normalitzats), el resultat és directament el cosinus de l\u0026rsquo;angle que formen.\nAquest cosinus és el que es coneix com a llei del cosinus de Lambert: com més alineada estigui la normal amb la direcció de la llum (angle proper a 0°), més llum rep la superfície (cosinus proper a 1); com més de costat rebi la llum (angle proper a 90°), menys llum li arriba (cosinus proper a 0). Això simula com la mateixa quantitat de llum es \u0026ldquo;reparteix\u0026rdquo; sobre més o menys superfície segons l\u0026rsquo;angle d\u0026rsquo;incidència.\nvec3 sun_dir = normalize(vec3(0.6, 0.35, 0.5)); float sun_dif = clamp(dot(sun_dir, normal), 0.0, 1.0); Fem clamp(..., 0.0, 1.0) perquè, si l\u0026rsquo;angle supera els 90° (la superfície mira en direcció oposada al sol), el cosinus es torna negatiu — i una quantitat de llum negativa no té sentit físic. Acotant el valor entre 0 i 1 ens assegurem que aquestes superfícies simplement no rebin llum directa del sol (queden en ombra pròpia), en lloc de \u0026ldquo;restar\u0026rdquo; llum de manera incorrecta.\nSky Diffuse \u0026amp; Bounce Diffuse\rPer donar-li una il·luminació del cel i del terra (la llum que rebota), utilitzem la mateixa tècnica que amb la llum del sol.\nFem el dot product de la normal amb el vector vertical: positiu (0,1,0) per al cel, negatiu (0,-1,0) per al rebot del terra. També fem un remapatge perquè els valors vagin de 0 a 1, ja que el dot product retorna un valor entre -1 i 1. A sky_dif fem servir 0.5 + 0.5*x, que reparteix el rang de forma simètrica (meitat i meitat); a bou_dif fem servir 0.1 + 0.9*x, una barreja diferent que dona més pes al dot product (rebot més intens quan la normal mira directament cap al terra) i menys pes a un valor base constant. Finalment, el resultat es clampeja perquè no es surti del rang [0.0, 1.0].\nfloat sky_dif = clamp(0.5 + 0.5*dot(normal, vec3(0.0, 1.0, 0.0)), 0.0, 1.0); float bou_dif = clamp(0.1 + 0.9*dot(normal, vec3(0.0, -1.0, 0.0)), 0.0, 1.0); Això es pot simplificar, ja que multiplicar per un vector unitari que només té un component actiu simplement \u0026ldquo;extreu\u0026rdquo; aquest component. Per tant, podem fer servir normal.y directament en lloc de fer el dot product:\nfloat sky_dif = clamp(0.5 + 0.5*normal.y, 0.0, 1.0); float bou_dif = clamp(0.1 - 0.9*normal.y, 0.0, 1.0); Hard Shadows\rLa idea de calcular ombres en raymarching és força senzilla. Reutilitzem la mateixa funció castRay que fem servir per al raig de la càmera, però ara llancem el raig des de la superfície de l\u0026rsquo;objecte (lleugerament desplaçat al llarg de la normal, per no xocar amb si mateix per errors de precisió) en direcció a la llum. Si aquest raig topa amb alguna cosa abans d\u0026rsquo;arribar a la font de llum, el punt original està a l\u0026rsquo;ombra. float sun_sha = step(castRay(pos + 0.001*nor, sun_lig, time), 0.0); step(valor, 0.0) — la funció step(edge, x) retorna 0.0 si x \u0026lt; edge, i 1.0 si x \u0026gt;= edge. Aquí es fa servir \u0026ldquo;al revés\u0026rdquo; de com se sol llegir: step(castRay(...), 0.0) compara 0.0 contra el resultat de castRay. Com que en aquesta versió simplificada castRay retorna -1.0 quan no hi ha impacte:\nSi no hi ha impacte → castRay(...) = -1.0 → step(-1.0, 0.0) = 1.0 (0.0 ≥ -1.0) → sense ombra, llum plena. Si hi ha impacte (topa amb alguna cosa) → castRay(...) és una distància positiva (per exemple 3.5) → step(3.5, 0.0) = 0.0 (0.0 \u0026lt; 3.5) → en ombra total. Càlcul de l\u0026rsquo;oclusió\rAquesta funció simula un efecte molt subtil però important per al realisme: les zones on hi ha geometria propera envoltant un punt (racons, plecs, forats entre formes) reben menys llum ambiental, perquè part d\u0026rsquo;aquesta llum queda bloquejada pels mateixos objectes veïns. És el que fa que les aixelles d\u0026rsquo;un personatge, o l\u0026rsquo;espai entre dos dits, es vegin lleugerament més foscos encara que no hi hagi una ombra projectada pel sol.\nfloat h = 0.01 + 0.11*float(i)/4.0; vec3 opos = pos + h*nor; float d = map( opos, time ).x; occ += (h-d)*sca; Per a cadascuna de les 5 iteracions, prenem el punt de la superfície (pos) i ens movem una distància h al llarg de la normal (nor) — és a dir, ens allunyem perpendicularment cap enfora de l\u0026rsquo;objecte. Si no hi hagués res més a prop (superfície totalment convexa i lliure), la distància real fins a qualsevol altre objecte en aquest punt (d, obtinguda de map()) seria exactament h — perquè no hi ha cap obstacle interferint.\nPerò si hi ha geometria veïna a prop (un racó, un plec), la distància real d serà menor que h, perquè hi ha alguna cosa entorpint abans d\u0026rsquo;aquesta distància esperada. La diferència h - d mesura precisament això: quant \u0026ldquo;més a prop del que s\u0026rsquo;esperava\u0026rdquo; hi ha superfície allà — com més geometria veïna hi hagi, més gran serà aquesta diferència.\nreturn clamp( 1.0 - 2.0*occ, 0.0, 1.0 ); occ acumula les diferències (h-d) ponderades: com més gran sigui occ, més \u0026ldquo;tancat\u0026rdquo; està el punt per geometria veïna. En fer 1.0 - 2.0*occ, invertim l\u0026rsquo;escala: si occ és alt (molta oclusió), el resultat s\u0026rsquo;apropa a 0 (poca llum ambiental); si occ és baix o zero (superfície lliure), el resultat s\u0026rsquo;apropa a 1 (llum ambiental completa). El clamp assegura que el resultat quedi sempre en el rang [0, 1], ja que la multiplicació per 2.0 és un factor arbitrari per amplificar l\u0026rsquo;efecte (ajust artístic, no físic) i podria treure el valor fora de rang.\nCamera (Look-at)\rPer construir una càmera que sempre miri a un punt fix. Es defineix un target, i a partir d\u0026rsquo;aquí la posició de la càmera (que també serà l\u0026rsquo;origen del raig, ro).\nAmb el target i l\u0026rsquo;origen podem definir el vector \u0026ldquo;endavant\u0026rdquo; (ww), normalitzat: és simplement la direcció des de ro cap a target.\nPer generar el vector de la \u0026ldquo;dreta\u0026rdquo; (uu), fem el producte vectorial del vector endavant i el vector vertical del món. El producte vectorial (cross) entre dos vectors dona un tercer vector perpendicular a tots dos. En fer cross(ww, vec3(0,1,0)) (endavant × amunt-del-món), obtenim un vector perpendicular tant a \u0026ldquo;cap a on mira la càmera\u0026rdquo; com a l\u0026rsquo;eix vertical del món — que és exactament la direcció \u0026ldquo;dreta\u0026rdquo; de la càmera.\nUn cop tenim els vectors endavant i dreta, repetim el procés per obtenir el vector \u0026ldquo;amunt\u0026rdquo; real de la càmera (vv). No podem simplement reutilitzar vec3(0,1,0) com a amunt, perquè si la càmera mira cap amunt o cap avall, aquest \u0026ldquo;amunt del món\u0026rdquo; ja no seria perpendicular als altres dos eixos. Per això el recalculem amb cross(uu, ww), assegurant que els tres vectors formin una base perfectament ortogonal entre si.\nvec3 target = vec3(0.0, 0.75, 0.4); // ray origin vec3 ro = target + vec3(1.5 * sin(an),-.1,1.5 * cos(an)); vec3 ww = normalize(target - ro); vec3 uu = normalize(cross(ww, vec3(0,1,0))); vec3 vv = normalize(cross(uu,ww)); Aquests vectors formen la base per construir la direcció de cada raig per píxel:\n// This normalize the coordinates from [-aspectRatio,-1] to [aspectRatio, 1] vec2 p = (2.0*(fragCoord+offset) - iResolution.xy) / iResolution.y; vec3 rd = normalize(p.x*uu + p.y*vv + 1.8*ww); Aquí p.x i p.y són les coordenades normalitzades del píxel en pantalla: p.y va de -1 a 1, mentre que p.x va de -aspectRatio a aspectRatio (on aspectRatio és l\u0026rsquo;amplada entre l\u0026rsquo;alçada de la pantalla). Aquestes coordenades es combinen amb els eixos uu (dreta), vv (amunt) i ww (endavant) de la càmera per construir la direcció del raig en l\u0026rsquo;espai del món — és a dir, \u0026ldquo;quant em desvio a la dreta/amunt des del centre de la vista, i quant avanço cap endavant\u0026rdquo;. L\u0026rsquo;1.8 que multiplica ww controla el camp de visió (FOV): com més gran sigui aquest número, més \u0026ldquo;zoom\u0026rdquo; (camp de visió més estret); com més petit, més angular (FOV més ampli).\nEscena simple\rAquest és el resultat final: el shader d\u0026rsquo;Iñigo Quilez amb una escena bàsica que reuneix els elements que hem anat explicant — el bucle de raymarching, el càlcul de normals, i la il·luminació combinant la llum del sol, del cel, del rebot i les ombres.\nShader d\u0026rsquo;Iñigo Quilez a ShaderToy\n","date":"29 de juny 2026","externalUrl":null,"permalink":"/ca/blog/0_raymarching/","section":"Blogs","summary":"","title":"Introducció al Raymarching","type":"blog"},{"content":"Soc enginyera de programari especialitzada en gràfics en temps real i desenvolupament de videojocs, amb experiència tant en el sector de l\u0026rsquo;entreteniment com en el de les tecnologies interactives. Al llarg dels últims anys he participat en projectes que van des de programari per a màquines recreatives desenvolupat en C++ fins a eines de visualització mèdica i aplicacions interactives creades amb Unity. El meu treball abasta sistemes de gameplay, funcionalitats de renderització, desenvolupament d\u0026rsquo;interfícies d\u0026rsquo;usuari, eines internes i programació orientada al rendiment. Gaudeixo creant solucions tècniques robustes, dissenyant fluxos de treball eficients i col·laborant en equips multidisciplinaris amb un fort enfocament en la claredat i el manteniment del codi.\n","date":"26 d’octubre 2025","externalUrl":null,"permalink":"/ca/resume/","section":"Currículum","summary":"","title":"Currículum","type":"resume"},{"content":"\rDesenvolupadora de videojocs especialitzada en C++ i renderització en temps real, amb una sòlida base en arquitectura de motors de videojocs i experiència pràctica en programació de gràfics 3D. Tinc un Grau en Disseny i Desenvolupament de Videojocs per la UPC i un Màster en Computació Gràfica, Simulació i Realitat Virtual a la U-TAD.\nTot i que el meu enfocament principal és la programació en C/C++, també he treballat amb C#, Python, Dart i JavaScript. Tinc experiència amb biblioteques com OpenGL i SDL, i he desenvolupat el meu propi motor de videojocs, incorporant funcionalitats com l\u0026rsquo;animació esquelètica i el frustum culling mitjançant particionament espacial. També he participat en projectes col·laboratius amb Unity, incloent diverses game jams i experiències premiades.\nEm apassiona l\u0026rsquo;aprenentatge continu, la col·laboració amb equips multidisciplinaris i l\u0026rsquo;exploració de les tecnologies interactives més avançades.\nDescarrega el CV ","date":"26 d’octubre 2025","externalUrl":null,"permalink":"/ca/about-me/","section":"Main","summary":"","title":"Sobre mi","type":"page"},{"content":"Desenvolupadora de Videojocs i Artista Tècnica\n","date":"13 de juny 2022","externalUrl":null,"permalink":"/ca/","section":"Main","summary":"","title":"Main","type":"page"},{"content":"","date":"25 de juny 2021","externalUrl":null,"permalink":"/ca/tags/c++/","section":"Tags","summary":"","title":"C++","type":"tags"},{"content":"","date":"25 de juny 2021","externalUrl":null,"permalink":"/ca/tags/engine-development/","section":"Tags","summary":"","title":"Engine Development","type":"tags"},{"content":"","date":"25 de juny 2021","externalUrl":null,"permalink":"/ca/tags/graphics-programming/","section":"Tags","summary":"","title":"Graphics Programming","type":"tags"},{"content":"","date":"25 de juny 2021","externalUrl":null,"permalink":"/ca/tags/opengl/","section":"Tags","summary":"","title":"OpenGL","type":"tags"},{"content":"","date":"25 de juny 2021","externalUrl":null,"permalink":"/ca/projects/","section":"Projects","summary":"","title":"Projects","type":"projects"},{"content":"","date":"25 de juny 2021","externalUrl":null,"permalink":"/ca/tags/real-time-rendering/","section":"Tags","summary":"","title":"Real-Time Rendering","type":"tags"},{"content":"\rDescripció general\r#\rShadow és una biblioteca de renderització en temps real que implementa diverses tècniques centrals utilitzades en els motors de videojocs moderns. Està escrita íntegrament en C++ amb OpenGL, i va ser desenvolupada com el meu Treball de Fi de Grau.\nRepositori de GitHub Sobre el projecte\r#\rL\u0026rsquo;objectiu de Shadow va ser estudiar i implementar tècniques de renderització en temps real des de zero, centrant-me en els sistemes gràfics a nivell de motor en lloc d\u0026rsquo;utilitzar frameworks o pipelines de shading predissenyats. El motor segueix un pipeline de Deferred Rendering i inclou múltiples funcionalitats de post-processat i sombreig.\nCaracterístiques de la biblioteca\r#\rVaig dissenyar i implementar:\nPipeline de Deferred Rendering Layout del G-Buffer i arquitectura multipas Physically-Based Rendering (PBR) Flux de treball de Metalness/Roughness Screen Space Ambient Occlusion (SSAO) Pipeline de post-processat Bloom Normal Mapping Generació de base tangent-espai (TBN) Sistema de gestió de recursos Eines de depuració i visualització amb ImGui Bucle de renderització d\u0026rsquo;escena i sistema de materials Pipeline de renderització\r#\rShadow utilitza un flux de treball de Deferred Shading per suportar múltiples llums dinàmiques de manera eficient. Les dades de material s\u0026rsquo;emmagatzemen en un G-Buffer i la il·luminació es calcula en un pas separat.\nResultat visual\r#\rRegistre de versions\r#\rv0.1\r#\rBase del motor (finestres, sistema d\u0026rsquo;entrada, càrrega de recursos) Renderitzador de skybox Inicialització del G-Buffer Implementació base de Deferred Shading v1.0 (Finalització del Treball de Fi de Grau)\r#\rPhysically-Based Rendering (PBR) Pas SSAO Post-processat Bloom Pipeline de Normal Mapping ","date":"25 de juny 2021","externalUrl":null,"permalink":"/ca/projects/shadow-graphics-library/","section":"Projects","summary":"","title":"Shadow Graphics Library","type":"projects"},{"content":"","date":"25 de juny 2021","externalUrl":null,"permalink":"/ca/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"29 de desembre 2020","externalUrl":null,"permalink":"/ca/tags/c%23/","section":"Tags","summary":"","title":"C#","type":"tags"},{"content":"","date":"29 de desembre 2020","externalUrl":null,"permalink":"/ca/tags/game-jam/","section":"Tags","summary":"","title":"Game Jam","type":"tags"},{"content":"\rDescripció general\r#\rK.U.B.O és un joc de trencaclosques i plataformes creat durant la Game Jam de la Universitat CITM-UPC, el tema de la qual va ser Paradoxa. El nostre equip va decidir explorar aquest concepte a través de la perspectiva forçada i el disseny de nivells basat en il·lusions.\nJugues com un petit cub atrapat en un món 2D. No obstant això, en prémer el botó vermell, l\u0026rsquo;entorn revela breument la seva vertadera estructura 3D, exposant trucs de perspectiva i camins ocults necessaris per resoldre cada nivell.\nAquesta mecànica de doble vista —jugabilitat 2D amb revelacions 3D momentànies— es va convertir en la paradoxa central al voltant de la qual es va construir tot el joc.\nRepositori de GitHub Les meves contribucions\r#\rProgramació de gameplay Suport en el disseny de nivells Mecàniques de trencaclosques amb canvis de perspectiva Depuració general i poliment 🏆 Premi: Millor Joc\r#\rK.U.B.O va guanyar el Premi al Millor Joc a la Game Jam de la CITM-UPC. El jurat va destacar que va ser el projecte que millor va capturar i integrar el tema de la jam: \u0026ldquo;Paradoxa\u0026rdquo;, oferint un ús intel·ligent i coherent de les mecàniques basades en la perspectiva.\nCrèdits\r#\rAlex Morales GitHub: Alexmg99\nYessica Servin GitHub: YessicaSD\nPol Vázquez GitHub: Amade128\nHimar Bravo GitHub: Himar33\nMarc Pavón Llop GitHub: Mackitus\n","date":"29 de desembre 2020","externalUrl":null,"permalink":"/ca/projects/kubo/","section":"Projects","summary":"","title":"K.U.B.O - Game Jam","type":"projects"},{"content":"","date":"29 de desembre 2020","externalUrl":null,"permalink":"/ca/tags/unity/","section":"Tags","summary":"","title":"Unity","type":"tags"},{"content":"","date":"12 de juny 2020","externalUrl":null,"permalink":"/ca/tags/customengine/","section":"Tags","summary":"","title":"CustomEngine","type":"tags"},{"content":"\rDescripció general\r#\rThe Witcher: A Bard\u0026rsquo;s Tale és un beat-\u0026rsquo;em-up / hack-and-slash cooperatiu. Els jugadors controlen en Geralt de Rívia i la Yennefer de Vengerberg mentre lluiten per rescatar la Ciri.\nJugar Sobre el projecte\r#\rÉs un projecte universitari en grup construït amb un motor personalitzat en C++ i OpenGL. El disseny se centra en un combat responsiu, encontres cinemàtics amb caps i una presentació polida. El meu treball abasta programació de gràfics, eines de gameplay i creació de contingut.\nLes meves contribucions individuals\r#\rSkinning en GPU (skinning en vertex shader) Sistema de subtítols (importació JSON + efecte màquina d\u0026rsquo;escriure) Presentació de caps (moviment cinemàtic de càmera) Enemic dummy per a proves de combat Efectes de shader de dissolució Models dels personatges principals i assets 3D Il·luminació d\u0026rsquo;escena Disseny de HUD i poliment de menús Skinning amb shaders\r#\rPer millorar el rendiment en temps d\u0026rsquo;execució, vam moure el skinning a la GPU (vertex skinning en el vertex shader). Cada vèrtex emmagatzema:\nun ivec4 d\u0026rsquo;índexs d\u0026rsquo;ossos (màxim 4 ossos per vèrtex), i un vec4 de pesos d\u0026rsquo;ossos (un pes per índex d\u0026rsquo;os). A la CPU pugem un array de matrius de transformació d\u0026rsquo;ossos (un mat4 per os). El vertex shader llegeix els índexs i pesos dels ossos, construeix una matriu de skinning a partir de les matrius d\u0026rsquo;ossos corresponents, i transforma la posició del vèrtex i la normal íntegrament a la GPU.\nTambé vaig corregir un error on alguns models col·lapsaven cap a l\u0026rsquo;origen. La causa arrel era aplicar les transformacions d\u0026rsquo;ossos relatives a les posicions originals del vèrtex en lloc d\u0026rsquo;usar l\u0026rsquo;origen de la malla; la correcció va consistir a calcular les posicions amb skinning relatives a l\u0026rsquo;origen de la malla i aplicar correctament les transformacions d\u0026rsquo;ossos.\nVertex skinning en GPU en acció\rDemostració de l\u0026rsquo;error: model col·lapsant a l\u0026rsquo;origen abans de la correcció\rSistema de subtítols\r#\rEl joc inclou seqüències narratives que requereixen subtítols. Vam utilitzar Subtitle Horse per crear els subtítols i els vam exportar a JSON per integrar-los fàcilment al nostre motor.\nVaig implementar un Gestor de Subtítols que:\nCarrega els temps i textos dels subtítols des d\u0026rsquo;arxius JSON Mostra línies temporitzades amb un efecte de màquina d\u0026rsquo;escriure Sincronitza els subtítols amb els temps de les cinemàtiques Subtítols amb efecte de màquina d\u0026rsquo;escriure\rFotograma final de cinemàtica amb subtítols\rPresentació del cap (càmera cinemàtica)\r#\rPer a les introduccions dels caps vaig crear un sistema de càmera cinemàtica. La càmera segueix una corba de Bézier per centrar-se en el cap amb una revelació dramàtica, i després torna suaument al jugador abans de reprendre el gameplay.\nEstà implementat com un petit script que mostreja una cúbica de Bézier en temps normalitzat i interpola tant la posició com l\u0026rsquo;orientació.\nIntroducció cinemàtica del cap usant un camí de càmera Bézier\rEnemic dummy\r#\rVaig implementar un enemic dummy per a l\u0026rsquo;entrenament de combat. El dummy permet als jugadors practicar atacs i combos, i ajuda l\u0026rsquo;equip a depurar la temporització d\u0026rsquo;entrades, la detecció de cops i les transicions d\u0026rsquo;animació.\nEnemic dummy per a proves de combat\rShader de dissolució\r#\rVaig implementar un shader de dissolució per a transicions d\u0026rsquo;aparició/desaparició estilitzades. L\u0026rsquo;efecte usa una textura de soroll (o soroll procedural) i un paràmetre que controla una màscara de llindar, creant foses suaus i controlables amb detall a les vores.\nModels 3D i assets\r#\rVaig produir personatges i assets del joc, i vaig aprendre el pipeline de producció artística: escultura, retopologia a Maya, pintura de textures a Substance i creació de bases low-poly per a producció.\nGeralt de Rívia\r#\rArt conceptual de Geralt\rProcés de retopologia de Geralt\rGeralt of Rivia in a Chibi Style by Yessica Servin Dominguez on Sketchfab Yennefer de Vengerberg\r#\rConcepte i retopologia de la Yennefer\rYennefer al motor\rYennefer The Witcher by Yessica Servin Dominguez on Sketchfab Enemic Ghoul\r#\rEscultura conceptual del Ghoul\rComparació de mida del Ghoul\rGhoul - The Witcher by Yessica Servin Dominguez on Sketchfab Altres assets\r#\rVaig crear props d\u0026rsquo;entorn com arbustos, roques i minerals utilitzats en diferents escenes.\nDisseny de HUD\r#\rVaig crear el layout del HUD i icones, vaig iterar sobre prototips d\u0026rsquo;UI, i vaig implementar icones polides i elements de feedback.\nPoliment del menú principal\r#\rPer al menú principal vaig afegir sistemes de partícules i un post-procés de bloom global per fer l\u0026rsquo;escena més dinàmica i atmosfèrica.\nAjustos d\u0026rsquo;il·luminació\r#\rVaig iterar sobre la il·luminació de l\u0026rsquo;escena per fer-la coincidir amb l\u0026rsquo;atmosfera de cada arena, equilibrant llums direccionals, contribució ambiental i elements precalculats on corresponia.\nEnllaços i descàrregues\r#\rDemo / build: Jugar a GitHub Pages Codi font: Repositori de GitHub Web del joc: Lloc web del joc ","date":"12 de juny 2020","externalUrl":null,"permalink":"/ca/projects/the-witcher-a-bards-tale/","section":"Projects","summary":"","title":"The Witcher: A bard's tale","type":"projects"},{"content":"","date":"29 de desembre 2019","externalUrl":null,"permalink":"/ca/tags/glew/","section":"Tags","summary":"","title":"Glew","type":"tags"},{"content":"\rQuè és Hinata Engine?\r#\rHinata Engine és un motor de joc desenvolupat per a l\u0026rsquo;assignatura de Motors de Videojocs del Grau en Disseny i Desenvolupament de Videojocs de la UPC. Va ser creat de manera col·laborativa per Jaume Montagut i jo, amb l\u0026rsquo;objectiu principal de comprendre com funciona internament un motor de joc modern.\nEl motor inclou els sistemes habituals que s\u0026rsquo;esperen d\u0026rsquo;un editor i runtime bàsic:\nImportació de models i textures Arquitectura GameObject–Component Desat i càrrega d\u0026rsquo;escenes UI d\u0026rsquo;editor basada en ImGui Pipeline bàsic de càmera i renderització També incorpora diverses optimitzacions com:\nParticionament espacial amb Octree Frustum Culling Animació Esquelètica, una implementació completa basada en recerca per a la tasca final. Repositori de GitHub Les meves contribucions individuals\r#\rVaig ser responsable de diversos sistemes centrals del motor, incloent-hi:\nFrustum Culling\r#\rVaig implementar comprovacions de visibilitat en CPU basades en els plans del frustum de la càmera. Vaig integrar bounding boxes per al culling en temps d\u0026rsquo;execució. Vaig afegir vista de depuració a través del panell \u0026ldquo;Configuració → Camera3D\u0026rdquo;. Octree\r#\rVaig implementar el particionament espacial de l\u0026rsquo;escena per millorar la renderització i el picking. Vaig afegir visualització dins del panell d\u0026rsquo;Escena. Serialització d\u0026rsquo;escenes\r#\rVaig implementar el desat i la càrrega d\u0026rsquo;escenes jeràrquiques usant JSON mitjançant Parson. Càrrega d\u0026rsquo;escenes mitjançant arrossegar i deixar anar des de l\u0026rsquo;editor. Mouse Picking\r#\rVaig implementar ray casting des de la càmera cap a l\u0026rsquo;escena. Vaig permetre la selecció d\u0026rsquo;objectes fent clic sobre les malles. Sistema d\u0026rsquo;Animació Esquelètica\r#\rCom a part de la tasca de recerca, vam implementar un sistema complet d\u0026rsquo;animació esquelètica:\nVam usar Assimp per importar arxius .dae i .fbx al nostre format personalitzat. Vam convertir les dades importades al format propi del motor. Vam implementar animació d\u0026rsquo;ossos i skinning de malles. Vam afegir blending d\u0026rsquo;animacions entre diferents clips. Vam habilitar la visualització de la jerarquia d\u0026rsquo;ossos per a depuració. Vam suportar la manipulació manual d\u0026rsquo;ossos quan el component de l\u0026rsquo;animador està desactivat. Resum de funcionalitats del motor\r#\rAfegir models\r#\rArrossega un arxiu .fbx des del panell Assets a l\u0026rsquo;Escena per crear automàticament la jerarquia de GameObjects. Mouse Picking\r#\rFes clic en qualsevol malla a la vista d\u0026rsquo;Escena per seleccionar-la. Frustum Culling (Depuració)\r#\rObre Configuració → Camera3D i activa \u0026ldquo;Veure frustum culling\u0026rdquo;. Mou o gira la càmera per visualitzar l\u0026rsquo;efecte. Particionament espacial\r#\rL\u0026rsquo;Octree sempre és visible a la vista d\u0026rsquo;Escena per a depuració. Gestió del temps\r#\rPlay: executar el motor Pause: pausar i reprendre la simulació Gestió de recursos\r#\rLes estadístiques d\u0026rsquo;ús de recursos són visibles al panell Resources. Serialització d\u0026rsquo;escenes\r#\rArrossega un arxiu d\u0026rsquo;escena a la vista d\u0026rsquo;Escena per carregar-lo. Desa les escenes mitjançant Arxiu → Desa l\u0026rsquo;escena. Biblioteques utilitzades\r#\rSDL — finestres i entrada Dear ImGui — UI de l\u0026rsquo;editor Glew — carregador d\u0026rsquo;extensions OpenGL Parson — anàlisi JSON OpenGL 3 — backend de renderització ","date":"29 de desembre 2019","externalUrl":null,"permalink":"/ca/projects/hinata-engine/","section":"Projects","summary":"","title":"Hinata Engine","type":"projects"},{"content":"","date":"29 de desembre 2019","externalUrl":null,"permalink":"/ca/tags/imgui/","section":"Tags","summary":"","title":"ImGui","type":"tags"},{"content":"","date":"29 de desembre 2019","externalUrl":null,"permalink":"/ca/tags/parson/","section":"Tags","summary":"","title":"Parson","type":"tags"},{"content":"\rDescripció general\r#\rSaving The Flamingos és un joc cooperatiu creat durant la Not Only Games Jam de King. Dos jugadors han de cooperar per guiar-se mútuament a través de diversos nivells i rescatar els flamencs que s\u0026rsquo;han separat del seu grup.\nEls jugadors assumeixen els rols d\u0026rsquo;un Talp i una Serp, cadascun amb habilitats asimètriques:\nEl Talp porta els ulls embenats i no pot veure, però pot escoltar totes les pistes d\u0026rsquo;àudio del joc a través d\u0026rsquo;auriculars. La Serp pot veure l\u0026rsquo;entorn però no pot sentir cap so. Repositori de GitHub Com es juga\r#\rTots dos jugadors controlen el seu personatge amb el D-pad d\u0026rsquo;un comandament. També es pot utilitzar el teclat (WASD i IJKL), tot i que no és l\u0026rsquo;experiència recomanada.\nTalp (amb els ulls embenats):\nNo pot veure la pantalla. Escolta totes les pistes d\u0026rsquo;àudio. Pot detectar les \u0026ldquo;trampes de la Serp\u0026rdquo; pel so. Serp (no pot sentir):\nPot veure l\u0026rsquo;entorn. No pot sentir cap pista d\u0026rsquo;àudio. Pot identificar visualment les \u0026ldquo;trampes del Talp\u0026rdquo;. L\u0026rsquo;objectiu és que tots dos personatges col·laborin i arribin al flamenc al final de cada nivell.\nNot Only Games Jam de King\r#\rAquest joc va ser creat per a la #NotOnlyGamesJam organitzada per King, sota el tema principal:\n\u0026ldquo;Crea solucions innovadores per augmentar la diversitat i la inclusió a la societat.\u0026rdquo;\nEls subtemes de la jam van ser \u0026ldquo;Flamenc\u0026rdquo; i \u0026ldquo;Posar-se en la pell de l\u0026rsquo;altre\u0026rdquo;. Agraïm a King per convidar-nos a participar en un esdeveniment tan inspirador i inclusiu!\n🏆 Premi a la Millor Experiència d\u0026rsquo;Usuari\r#\rEl joc va rebre el Premi a la Millor Experiència d\u0026rsquo;Usuari, reconegut per les seves mecàniques asimètriques úniques, intuïtives i altament accessibles que van fomentar la col·laboració i l\u0026rsquo;empatia entre els jugadors.\nCrèdits\r#\rAlex Campanar – @IamAcaree David Lozano – @DavidLozano42 Jaume Montagut – @Jaume_Montagut Joan Valiente – @KaikJoan Yessica Servin – @Yessica_SD Spoiler (Explicació del gameplay)\r#\rSi alguna cosa no queda clara, aquí teniu el sistema de trampes:\nTrampes de la Serp: Maten la Serp. El Talp pot detectar-les pel so i pot trepitjar-les sense perill.\nTrampes del Talp: Maten el Talp. La Serp pot veure-les com a rajoles grises/marrons amb punxes i pot passar-hi sense perill.\n","date":"19 de novembre 2019","externalUrl":null,"permalink":"/ca/projects/savingtheflamingos/","section":"Projects","summary":"","title":"King's Not Only Games Jam - Saving The Flamingos","type":"projects"},{"content":"","date":"26 de juny 2019","externalUrl":null,"permalink":"/ca/tags/sdl/","section":"Tags","summary":"","title":"SDL","type":"tags"},{"content":"\rDescripció general\r#\rTankerfield és un joc de supervivència cooperatiu per a 4 jugadors on els jugadors controlen tancs i lluiten contra onades d\u0026rsquo;enemics. El projecte va ser desenvolupat per Gamificalo Studio, un equip de 8 estudiants de la Universitat CITM-UPC de Terrassa, Espanya.\nCom a part de l\u0026rsquo;equip de programació, vaig implementar diversos sistemes centrals de gameplay i motor.\nRepositori de GitHub Les meves contribucions individuals\r#\rIntegració del Quadtree en el pipeline de renderització Sistema de controladors (detecció, vibració, remapejat + desat persistent) Lògica d\u0026rsquo;IA enemiga i sistema de comportament basat en estats Sistema d\u0026rsquo;Object Pooling Extracció de sprites dels arxius originals del joc i models 3D Millores al mòdul de mapa (ordenació de tiles, càrrega de col·lisions) Extracció de sprites\r#\rVaig investigar i implementar el flux de treball per extreure sprites tant de:\narxius originals del joc models 3D (capturant i aïllant fotogrames d\u0026rsquo;animació) Això va permetre a l\u0026rsquo;equip crear assets de més qualitat i mantenir la coherència amb l\u0026rsquo;estil artístic original.\nMillores al mòdul de mapa\r#\rVaig refactoritzar i ampliar el sistema de mapa:\nQuadtree en el pipeline de renderització\r#\rVa millorar enormement el rendiment de la renderització garantint que els tiles i elements del món es descardessin i renderitzessin eficientment.\nOrdenació de tiles\r#\rOrdre correcte dels elements segons profunditat i tipus.\nCàrrega automàtica de col·lisions des de Tiled\r#\rVa fer la creació de nivells més ràpida, menys propensa a errors i més amigable per als dissenyadors.\nSistema de controladors\r#\rVaig implementar tot el pipeline de controladors:\nDetecció de controladors connectats/desconnectats Vibració del controlador Mapejat i remapejat d\u0026rsquo;entrades Integració a la UI per a indicacions de botons Desat de la configuració d\u0026rsquo;entrada entre sessions Aquest sistema va garantir una jugabilitat fluïda per als quatre jugadors i adaptabilitat a diferents controladors.\nSistema d\u0026rsquo;IA enemiga\r#\rVaig dissenyar un sistema d\u0026rsquo;IA basat en estats, facilitant l\u0026rsquo;extensió, la depuració i l\u0026rsquo;equilibri dels enemics. Alguns estats implementats:\nGET_PATH – Cerca de camí cap al jugador més proper MOVE – Seguir el camí generat BURN – En ser alcançat per foc + oli, els enemics entren en pànic i reben dany amb el temps TELEPORT – Els enemics lluny dels jugadors reapareixen més a prop UNSTUCK – Lògica per recuperar-se quan els enemics apareixen o es mouen sobre un tile no transitable Aquest disseny modular va permetre als dissenyadors afegir nous tipus d\u0026rsquo;enemics i habilitats ràpidament.\nSistema d\u0026rsquo;Object Pooling\r#\rA causa de l\u0026rsquo;elevat nombre d\u0026rsquo;enemics i projectils en pantalla, vaig implementar un object pool per evitar assignacions de memòria innecessàries. Això va millorar significativament el rendiment i l\u0026rsquo;estabilitat de memòria, especialment durant les onades avançades.\nTasques addicionals\r#\rImplementació de la lògica del llançacoets (comportament de tret basat en distància) Múltiples correccions d\u0026rsquo;errors i millores d\u0026rsquo;estabilitat al llarg del projecte ","date":"26 de juny 2019","externalUrl":null,"permalink":"/ca/projects/tankerfield/","section":"Projects","summary":"","title":"Tankerfield","type":"projects"},{"content":"","date":"16 de desembre 2018","externalUrl":null,"permalink":"/ca/tags/2d-platformer/","section":"Tags","summary":"","title":"2D Platformer","type":"tags"},{"content":"","date":"16 de desembre 2018","externalUrl":null,"permalink":"/ca/tags/game-development/","section":"Tags","summary":"","title":"Game Development","type":"tags"},{"content":"\rDescripció general\r#\rSpooky Skeleton és un joc de plataformes 2D desenvolupat amb C++ i SDL. El joc presenta un esquelet protagonista que ha de travessar dos nivells de temàtica terrorífica: una cova subterrània i el bosc exterior.\nRepositori de GitHub Les meves contribucions individuals\r#\rSistema de moviment del jugador (incloent-hi lògica de salt i acceleració personalitzada de \u0026ldquo;lliscament sobre gel\u0026rdquo;). Comportament d\u0026rsquo;enemics terrestres. Implementació de l\u0026rsquo;algorisme A* de pathfinding per a enemics. Càrrega de mapa i animacions utilitzant el format Tiled (.tmx). Implementació del sistema d\u0026rsquo;UI, totalment data-driven i carregat des d\u0026rsquo;XML. Sobre el joc\r#\rObjectiu Arribar al final de cada nivell evitant o derrotant els enemics i recollint totes les monedes.\nControls\nESPAI – Saltar FLETXES ESQUERRA / DRETA – Moure\u0026rsquo;s Q – Atacar ESC – Obrir menú Característiques destacades\r#\r❄️ Mecànica de plataforma de gel\r#\rLes rajoles de gel redueixen la fricció, fent que el jugador llisqui. Això va requerir ajustos en la lògica d\u0026rsquo;acceleració i desacceleració.\n🧟‍♂️ Enemics i combat\r#\rEl jugador pot atacar i derrotar els zombis.\n🧩 UI basada en dades\r#\rTots els elements de la UI es carreguen des d\u0026rsquo;arxius XML, permetent canvis de disseny flexibles sense modificar el codi font.\nSubsistemes principals\r#\rEl joc està estructurat al voltant d\u0026rsquo;una arquitectura modular. El mòdul principal de l\u0026rsquo;aplicació (j1App.cpp) gestiona el cicle de vida de tots els altres mòduls cridant mètodes de classe base compartits:\nAwake PreUpdate Update PostUpdate CleanUp Fàbrica d\u0026rsquo;entitats\r#\rS\u0026rsquo;utilitza un patró factoria per crear i gestionar totes les entitats del joc, millorant tant l\u0026rsquo;organització com el rendiment.\nMòdul d\u0026rsquo;UI\r#\rEl sistema d\u0026rsquo;UI està completament desacoblat de la lògica de gameplay. Gestiona esdeveniments de manera independent, permetent una separació neta entre la interfície i els sistemes principals.\nDades basades en XML\r#\rEls mapes, animacions, textures, rutes d\u0026rsquo;arxiu i dades de la UI es carreguen des d\u0026rsquo;XML. Això millora la llegibilitat, facilita la iteració i evita els \u0026ldquo;números màgics\u0026rdquo; codificats.\n","date":"16 de desembre 2018","externalUrl":null,"permalink":"/ca/projects/spooky-skeleton/","section":"Projects","summary":"","title":"Spooky Skeleton","type":"projects"},{"content":"","date":"16 de desembre 2018","externalUrl":null,"permalink":"/ca/tags/tiled-map-editor/","section":"Tags","summary":"","title":"Tiled Map Editor","type":"tags"},{"content":"\rDescripció general\r#\rLast Resort és un remake tribut del joc arcade shoot \u0026rsquo;em up de SNK de 1992. El projecte va ser desenvolupat des de zero en C++ per un equip de quatre estudiants del Grau en Disseny i Desenvolupament de Videojocs de la UPC–CITM.\nEl nostre objectiu era recrear fidelment el gameplay central, els comportaments dels enemics, les armes i la sensació general del joc original, tot aprenent com estructurar un motor de joc i un bucle de gameplay a petita escala.\nRepositori de GitHub Les meves contribucions individuals\r#\rVaig contribuir a diversos sistemes centrals de gameplay, incloent-hi:\nComportament d\u0026rsquo;enemics Vaig implementar la lògica de comportament per a diversos tipus d\u0026rsquo;enemics, incloent-hi patrons de moviment, temporització d\u0026rsquo;atacs i interaccions amb el jugador.\nImplementació d\u0026rsquo;armes Vaig desenvolupar una de les armes principals i la seva progressió de millores, integrant-la als sistemes de combat i col·lisions existents.\nCrèdits\r#\rGestió i Programació: Jaume Montagut i Guix Web: wadoren.wixsite.com/gamedev GitHub: JaumeMontagut\nArt, QA i Programació: Alejandro Gamarra Niño Instagram: @ax3_rt GitHub: alejandro61299\nQA: Dani Sanchez Flores Instagram: @vampir_nex GitHub: Dasanch\nProgramació i Revisió de Codi: Yessica Servín Domínguez Instagram: @randomgerbit GitHub: YessicaSD\nSobre el joc\r#\rAl límit de l\u0026rsquo;apocalipsi, Last Resort situa el jugador als comandaments d\u0026rsquo;una poderosa nau espacial acompanyada d\u0026rsquo;un dron de suport conegut com la \u0026ldquo;unitat\u0026rdquo;. Junts, l\u0026rsquo;objectiu és lluitar contra onades d\u0026rsquo;enemics i evitar la catàstrofe imminent.\nVídeo del remake\r#\rVídeo del joc original\r#\r","date":"3 de juny 2018","externalUrl":null,"permalink":"/ca/projects/last-resort/","section":"Projects","summary":"","title":"Last Resort","type":"projects"},{"content":"","externalUrl":null,"permalink":"/ca/resume/experience/3dtechomegazeta/","section":"Currículum","summary":"","title":"3D Tech Omega Zeta","type":"resume"},{"content":"","externalUrl":null,"permalink":"/ca/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/ca/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/ca/resume/education/","section":"Currículum","summary":"","title":"Educació","type":"resume"},{"content":"","externalUrl":null,"permalink":"/ca/resume/experience/","section":"Currículum","summary":"","title":"Experiència","type":"resume"},{"content":"","externalUrl":null,"permalink":"/ca/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","externalUrl":null,"permalink":"/ca/resume/experience/unidesa-cirsa/","section":"Currículum","summary":"","title":"Unidesa I+D Cirsa","type":"resume"}]