1 # El Pipeline Gráfico de WebGL 2 3 Sistemas Gráficos (86.43) - 1C2024 - FIUBA 4 Martin J. Klöckner - [mklockner@fi.uba.ar](mailto:mklockner@fi.uba.ar) 5 6 > 1. ¿Qué es el Pipeline Gráfico en el contexto de WebGL y Three.js? 7 8 El pipeline gráfico hace referencia a la serie de procedimientos que hacen falta 9 para renderizar un objeto 3D en la pantalla de una computadora, en el contexto 10 de WebGL y Three.js, el objeto se renderiza en un navegador. 11 12 ![principales etapas del pipeline gráfico junto con las entradas y salidas](./graphics-pipeline.png) 13 14 En la figura anterior se puede ver una vista simplificada de las etapas del 15 pipeline gráfico junto con las entradas y salidas del mismo. 16 17 > 2. ¿Cuáles son las etapas principales del Pipeline Gráfico? 18 19 Las etapas principales del Pipeline Gráfico son el procesamiento de vertices 20 ("vertex shader"), la etapa de pasterización y la etapa de procesamiento de 21 fragmentos ("fragment shader" o "pixel shader"). 22 23 > 3. ¿Qué función desempeña el "vertex shader" en el proceso de renderizado? 24 25 El procesador de vertices o "vertex shader" se encarga de aplicar 26 transformaciones a cada uno de los vertices, la principal tarea es transformar 27 la posición 3D del vértice en una posición 2D en el plano virtual que se 28 mostrara en pantalla, así como también obtener la profundidad y almacenarla en 29 el `z-buffer`.[^1] 30 31 [^1]: [“Shader” Wikipedia, The Free Encyclopedia, 2 Mar 2024.](https://en.wikipedia.org/w/index.php?title=Shader) 32 33 Los "vertex shader" pueden manipular propiedades del vértice como la posición, 34 el color o las coordenadas de la textura, no pueden crear nuevos vértices. 35 36 > 4. ¿Cómo se define un vertex shader en GLSL, cuáles son las salidas mínimas 37 > necesarias (que valores debe retornar obligatoriamente)? 38 39 En GLSL el trabajo mínimo de un vertex shader es asignar a la variable 40 especial `gl_Position` un vector de 4 dimensiones (x, y, z, w). Esta variable 41 luego será entregada a la siguiente etapa del pipeline gráfico.[^2] 42 43 [^2]: ["A Primer on Shaders", Learn WebGL, Wayne Brown, 2015.](http://learnwebgl.brown37.net/rendering/shader_primer.html) 44 45 Una implementación en GLSL del vertex shader más básico para renderizar un objeto 46 se puede ver a continuación: 47 48 ```glsl 49 // Vertex Shader 50 uniform mat4 u_Transform; 51 uniform vec4 u_Color; 52 attribute vec3 a_Vertex; 53 54 void main() { 55 // Transforma la posicion del vertice 56 gl_Position = u_Transform * vec4(a_Vertex, 1.0); 57 } 58 ``` 59 60 El vertex shader se debe encargar de aplicar las transformaciones necesarias al 61 objeto para transformarlo del espacio de modelado a espacio de pantalla. Para 62 eso realiza un cambio de base multiplicando por la matriz de modelado, vista, 63 proyección y finalmente de viewport. 64 65 > 5. ¿Qué datos se pueden pasar al vertex shader a través de atributos? 66 67 Los atributos que se pueden pasar al "vertex shader" se denominan "vertex 68 attributes" y pueden contener por ejemplo posiciones, normales o coordenadas de 69 textura. 70 71 > 6. ¿Qué es una variable uniform en el contexto de shaders? 72 73 En GLSL una variable `uniform` es una variable de alcance global la cual se 74 puede utilizar para recibir parámetros del programa que utiliza el shader.[^3] 75 El valor de las variables de tipo `uniform` se almacena en el propio objeto 76 compilado del shader.[^4] 77 78 [^3]: ["GLSL Object" OpenGL Wiki, 2015.](https://www.khronos.org/opengl/wiki/GLSL_Object) 79 [^4]: ["Uniform (GLSL)" OpenGL Wiki, 2015.](https://www.khronos.org/opengl/wiki/Uniform_(GLSL)) 80 81 Las variables de tipo `uniform` se denominan así porque no cambian entre las 82 diferencias instancias de la ejecución del shader. Recordemos que en el caso de 83 los "vertex shaders", por ejemplo, se ejecuta una instancia del shader por cada 84 vértice. Las variables `uniform` se diferencian de otras variables como las de 85 entrada y salida de cada etapa, las cuales por lo general cambian en cada 86 invocación del shader. 87 88 Un ejemplo común de utilización de variables uniform es para informar al shader 89 del variables constantes, o que permanecerán constantes en la ejecución del 90 shader, por ejemplo el tiempo, ya que durante la ejecución del shader la 91 variable permanecerá constante y no cambiará, quizás en la próxima iteración 92 cambie o quizás no, pero eso es ajeno al alcance del shader, si no más bien 93 depende de quien lo invoca. 94 95 > 7. ¿Cuál es la diferencia entre un vertex shader y un fragment shader? 96 97 La diferencia entre "fragment shader" y "vertex shader" es 98 que el trabajo del primero es asignar un color a cada fragmento, mientras que el 99 trabajo del segundo es, como vimos previamente, aplicar transformaciones a cada 100 vértice. 101 102 Recordemos que un "fragmento" es el conjunto de un pixel y toda su información 103 necesaria para ser renderizado. 104 105 > 8. ¿Cómo se almacenan los valores de color de cada pixel en el fragment 106 > shader? 107 108 En el fragment shader se almacenan en la variable `gl_FragColor` 109 110 ```glsl 111 // Fragment shader 112 uniform vec4 u_Color; 113 114 void main() { 115 gl_FragColor = u_Color; 116 } 117 ``` 118 119 > 9. ¿Qué es un `sampler2D` y cómo se utiliza en un fragment shader? 120 121 Un sampler en GLSL es un tipo de variable especial que se utiliza par acceder a 122 una textura desde un shader. En particular `sampler2D` es un tipo especial de 123 sampler que se utiliza para acceder o almacenar texturas bidimensionales desde 124 un shader. 125 126 > 10. ¿Cuál es el propósito del rasterizador en el Pipeline Gráfico? 127 128 El rasterizador se encarga de convertir cada primitiva (generada en la etapa de 129 "vertex post-processing") en un fragmentos, para luego ser enviados al "fragment 130 shader".[^5] 131 132 [^5]: ["Rendering Pipeline Overview" OpenGL Wiki, 2022.](https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview) 133 134 Un fragmento es un conjunto de estados que se utiliza para computar la salida de 135 un pixel (o `sample` si se utiliza `multisampling`) en el `framebuffer`. Este 136 conjunto de estados para un fragmento incluye la posición en el espacio de 137 pantalla y una lista de datos arbitrarios provenientes de etapas previas del 138 pipeline gráfico, como el "vertex shader". 139 140 Este ultimo conjunto de datos se computa para cada fragmento mediante la 141 interpolación de los valores de los vertices. El método de interpolación queda 142 definido por el shader que procesó esos datos. 143 144 > 11. ¿Cuál es la función principal de la GPU en el Pipeline Gráfico? 145 146 La GPU se encarga de la mayor parte del pipeline gráfico, ya que procesa desde 147 el "vertex shader" hasta el renderizado de la imagen en pantalla. La GPU obtiene 148 los píxeles a procesar en el vertex shader de su propia memoria, la cual es 149 distinta de la memoria del CPU, es el CPU el que se encarga de enviar los 150 píxeles a renderizar a la memoria de la GPU. 151 152 > 12. ¿Cómo se extrae un valor de una textura en un fragment shader? 153 154 Para obtener un valor de una textura en un fragment shader, primero se debe 155 tener acceso a la misma mediante un `sampler`, como se mencionó previamente, 156 luego se puede utilizar la función `texture`, la cual obtiene el valor de la 157 textura para las coordinas pedidas. Como se muestra en el siguiente ejemplo de 158 "fragment shader": 159 160 ```glsl 161 uniform sampler2D myTexture; // Sampler asociado a la textura 2D 162 163 void main() { 164 vec2 textureCoordinates = vec2(0.5, 0.5); 165 vec4 texelColor = texture(myTexture, textureCoordinates); 166 gl_FragColor = texelColor; 167 } 168 ``` 169 170 > 13. ¿Qué son las variables varying en el contexto de los shaders? 171 172 Las variables declaradas de tip `varying` son aquellas que cambian para cada 173 fragmento. Se utilizan para pasar datos interpolados entre las diferentes etapas 174 del pipeline gráfico, como el "vertex shader" y el "fragment shader". 175 176 A la variable se le asigna un valor en el "vertex shader" y es automáticamente 177 interpolado a lo largo de la superficie de una primitiva (por ejemplo la 178 superficie de un triangulo) antes de que llegue al "fragment shader". El valor 179 puede ser usado por el "fragment shader" pero no cambiado ya que el propósito 180 de estas variable es que sea interpolado automáticamente 181 182 > 14. ¿Qué información se comparte entre el vertex shader y el fragment shader a 183 > través de las variables varying? 184 185 Como se menciono antes las variables de tipo `varying` son aquellas que se 186 interpolan automáticamente a los largo de la superficie de una primitiva. 187 Algunos ejemplos de datos que se pueden interpolar son colores, coordenadas 188 de textura o coordenadas de vectores normales. 189 190 > 15. ¿Cómo se realiza la interpolación de valores de los vértices en un 191 > fragment shader? 192 193 En un fragment shader, la interpolación de valores de los vértices se realiza 194 automáticamente por el hardware gráfico durante el proceso de rasterización. 195 196 > 16. ¿Qué es la matriz de vista? 197 198 La matriz de vista se utiliza para transformar los objetos desde su sistema de 199 coordenadas del mundo al sistema de coordenas de cámara. 200 201 La matriz de vista se utiliza para definir la posición y orientación de la 202 cámara en relación con los objetos en la escena, simulando así el punto de vista 203 desde el cual se está observando la escena. 204 205 > 17. ¿Qué es la matriz de modelado? 206 207 La matriz de modelado se utiliza para transformar cada objeto desde sus sistema 208 de coordenadas de modelado a un sistema de coordenadas de mundo. 209 210 El sistema de coordenadas de modelado, o espacio de modelado, es un sistema en 211 el cual el objeto esta posicionado en un cero arbitrario (definido por el 212 diseñador). Cada objeto se modela por separado, por lo que cada objeto posee su 213 propio espacio de modelado. Mediante la matriz de vista, se posiciona cada 214 objeto con respecto a un sistema de coordenada común. 215 216 > 18. ¿Qué es la matriz de proyección? 217 218 La matriz de proyección se utiliza para transformar los objetos de espacio de 219 cámara a un espacio normalizado entre `(-1.0; 1.0)` 220 221 > 19. ¿Cómo se crea la matriz de transformación de la vista a partir de la 222 > posición y orientación de la cámara? 223 224 La matriz de vista transforma de coordenadas de mundo a coordenadas de cámara, 225 en el espacio de cámara, la cámara se posiciona en el origine y todos los 226 objetos relativos a esta. 227 228 En `three.js` se puede construir la matriz de vista de la siguiente forma: 229 230 ```js 231 var viewMatrix = mat4.create(); 232 mat4.lookAt(viewMatrix, cameraPosition, target, [0, 1, 0]); 233 ``` 234 235 > 20. ¿Cómo se transforman las normales en un vertex shader para mantener su 236 > coherencia durante las transformaciones de modelo y vista? 237 238 > 21. ¿Cuál es la diferencia entre `gl.LINE_STRIP` y `gl.LINE_LOOP` al dibujar 239 > líneas en WebGL? 240 241 > 22. ¿Cómo se dibuja una línea entre un par de vértices utilizando el modo 242 > `gl.LINES`? De un ejemplo 243 244 > 23. ¿Cuál es el propósito de `gl.TRIANGLE_FAN` y cómo difiere de 245 > `gl.TRIANGLES`? Ejemplifique 246 247 > 24. ¿Qué ocurre cuando se utiliza `gl.TRIANGLE_STRIP` en lugar de 248 > `gl.TRIANGLES`? de un ejemplo 249 250 > 25. ¿Cuál es el papel del rasterizador en la transformación de primitivas en 251 > píxeles? 252 253 > 26. ¿Cuál es el papel de los núcleos (cores) en una GPU y cómo se organizan? 254 255 > 27. ¿Qué es la memoria de video (VRAM) y cómo se diferencia de la memoria RAM 256 > convencional? 257 258 > 28. ¿Cómo se gestionan las variables `uniforms` en un shader y cuál es su 259 > propósito? 260 261 > 30. ¿Cómo se puede optimizar el rendimiento en WebGL al minimizar el número de 262 > llamadas al pipeline gráfico? 263 264 > 31. ¿Qué papel desempeña el atributo `gl_Position` en un vertex shader y cómo 265 > afecta el resultado final del renderizado? 266 267 > 32. ¿Cuál es la diferencia entre el mapeo de texturas en coordenadas UV y 268 > coordenadas de proyección en un fragment shader?