Universidad Nacional de Luján
Departamento de Ciencias Básicas
Sistemas Distribuidos y Programación Paralela 2026 Dr. David Petrocelli
TP 4
📑 Índice del documento

Trabajo Práctico Nº 4

Programación Paralela (Shaders)

Fecha de entrega: 15/05/2026


Requisitos, consideraciones y formato de entrega


Contenidos del programa relacionados


Práctica

La programación paralela es la respuesta a la necesidad de tener escalabilidad vertical. A diferencia de un sistema distribuido (que requiere múltiples nodos coordinándose asincrónicamente), la programación paralela vive sobre un único nodo y su eje central es la sincronización y la gestión de recursos compartidos.

La caracterización clásica de las arquitecturas paralelas es la taxonomía de Flynn [FLY72] —SISD, SIMD, MISD, MIMD—; las GPUs modernas operan bajo el modelo SIMT (Single Instruction, Multiple Thread), una variante de SIMD propia de NVIDIA descrita en el paper original de CUDA [NIC08]. Para un tratamiento sistemático del modelo de programación masivamente paralelo, el textbook de referencia es Kirk & Hwu [KIR16]; para una panorámica del estado del arte de GPU computing, ver el survey de Owens et al. [OWE08].

Pipeline de Renderizado GPU

Comencemos con lo básico

Imaginemos un proceso A que debe realizar un cómputo paralelo. Este cálculo normalmente se realiza en GPU, ya que la misma ofrece un elevado número de núcleos de bajo rendimiento individual (miles, optimizados para throughput), mientras que la CPU ofrece muy pocos núcleos de alto rendimiento (optimizados para latencia).

El procesamiento de una tarea en paralelo tiene implicancias que se deben tener en cuenta porque su funcionamiento es sustancialmente diferente al que estamos acostumbrados en CPU.

La forma más “visible” de entender estas diferencias es mediante el procesamiento de gráficos —más concretamente, video en tiempo real—. La referencia académica de cabecera para entender el pipeline de renderizado en tiempo real es Akenine-Möller, Haines & Hoffman [AKE18].

En conclusión, procesar un video aplicando una operación como un filtro de escala de grises a cada píxel requiere F * Y * X operaciones. Si esto lo hiciéramos en CPU con un único núcleo, tendríamos que hacer 3 for anidados y sería costoso en tiempo computacional.

Cabe aclarar que el procesamiento paralelo no se limita a procesamiento gráfico; comenzaremos esta guía con este enfoque ya que es la forma más divertida y didáctica de realizar una introducción.


Hit #1 — Pixel shaders y pipeline de renderizado

Visiten el artículo de Wikipedia sobre Shaders [SHADER] y lean los apartados sobre Pixel Shaders (en inglés). Empiecen a elaborar un informe documentando someramente los tipos de shaders. En esta práctica nos enfocamos en los Pixel Shaders que operan en 2D — el referente clásico del lenguaje GLSL es Rost et al. [ROS09].

Consideración: corto y conciso.

Visiten WebGL Fundamentals [WEBGL] y agreguen al informe la descripción del pipeline de renderizado, relacionándolo con el artículo anterior.

Consideración: corto y conciso.

Dividan los 6 pasos del pipeline en aquellos que corresponden al procesamiento 3D y los que corresponden al 2D.

Visiten el artículo de Wikipedia sobre Video post-processing [POSTPR] y agreguen al informe los conceptos básicos. ¿En qué etapa del pipeline se ejecutan?

Consideración: corto y conciso.

Diríjanse a ShaderToy [STOY], plataforma que permite programar shaders gráficos interactivos sobre GPU vía WebGL. Hagan clic en “Nuevo” arriba a la derecha, expandan las “Entradas del shader” y agreguen al informe un listado de las entradas posibles indicando tipo, nombre y descripción breve de qué representa cada una.

Diríjanse al howto de ShaderToy [STOYH] y agreguen al informe un listado de las salidas posibles de los Pixel Shaders, su tipo y una breve descripción de qué representa cada una.

Cuando crea un nuevo ShaderToy, el código de ejemplo que le sugiere la web es el siguiente:

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;

    // Time varying pixel color
    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));

    // Output to screen
    fragColor = vec4(col,1.0);
}

Con apoyo de internet o lo que considere, explique en profundidad cada parte de este shader “hello world”. Debe explicar como mínimo:


Hit #2 — Pintando con código

Vean el video de Inigo Quilez “Painting a Landscape with Maths” [QUI13], donde usando matemáticas, trigonometría, shaders y mucha creatividad pinta un paisaje completo usando solamente código (funciones matemáticas aplicadas píxel a píxel).

Documenten el video de forma somera. ¿Qué conclusiones sacan al respecto?

Consideración: corto y conciso.


Hit #3

Hora de ensuciarse las manos. Utilizando ShaderToy, seleccione en iChannel0 una fuente de textura para poder continuar esta guía. Puede ser una imagen de ejemplo, un video de ejemplo o, para hacerlo más entretenido, su cámara web.

El siguiente shader muestra de forma trivial cómo copiar los píxeles desde el iChannel0 a la salida:

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    vec2 uv = (fragCoord.xy / iResolution.xy);
    fragColor = texture(iChannel0, uv);
}

Hit #4

Utilizando como punto de partida el Hit #3 y sin perder de vista lo que documentó en los hits anteriores, modifique el código para poner la imagen cabeza abajo, es decir, aplicar lo que comúnmente se llama un efecto de FLIP-Y o voltear vertical. Luego haga el FLIP-X o voltee horizontal (también llamado espejo).

Si no lo había hecho previamente, amplíe su informe sobre la potencialidad de UV.

Hit #4 - Coordenadas UV y Flip

Consideración: Corto y conciso.


Hit #5

Tomando como base el ejemplo anterior, en iChannel1 agregue una fuente de textura de video (puede usar el de ejemplo de Britney Spears). Quite los efectos flips y muestre la nueva textura en lugar de la de iChannel0.

Va a implementar ahora un filtro chroma básico: su objetivo es cambiar el color de los píxeles verdes del fondo por el video proveniente de iChannel0 (su webcam) para poder bailar como malocorista detrás de Britney Spears (el baile es opcional pero altamente recomendable).

Para implementar un filtro chroma primero necesitará recordar algunos conceptos matemáticos, en particular el que proviene de la geometría: la distancia pitagórica en N dimensiones.

Vamos pasito a pasito como decía Mostaza Merlo hasta poder unir todas las piezas (https://www.youtube.com/watch?v=cGBvmbtpLcE).

  1. En primer lugar tiene que definir el color del chroma dentro de un vec4.
  2. Luego tiene que definir un umbral de chroma dentro de un float.
  3. Luego tiene que calcular la distancia entre el color del iChannel1 y el color del chroma con el teorema de distancia pitagórica en N dimensiones, siendo N=3.
  4. Finalmente elegir si mostrar la textura de un canal u otro en función de si la distancia supera o no el umbral establecido.

Modifique el umbral a diferentes valores. Analice los resultados. Documente la experiencia. Haga screenshots bailando como malocorista con el efecto chroma (opcional).

Hit #5 - Filtro Chroma Key


Hit #6 — Filtro escala de grises

Modifiquen el programa para aplicar un filtro de escala de grises [GRAY] (luminancia perceptual) y luego documenten los cambios realizados. Para profundizar en los fundamentos teóricos del procesamiento digital de imágenes, ver Gonzalez & Woods [GON18].


De Pixel Shaders a CUDA

De Pixel Shaders a CUDA: el puente conceptual

Todo lo que programaron en ShaderToy ejecuta en la GPU: cada píxel es procesado por un núcleo independiente en paralelo. Este es exactamente el mismo principio que van a usar en el TP Integrador con CUDA [CUDA, NIC08], pero aplicado a otro dominio. Para profundizar en el modelo CUDA con ejemplos prácticos antes del TPI, recomiendo Sanders & Kandrot [SAN10] —CUDA by Example, todavía la introducción más accesible al tema— y el textbook de Kirk & Hwu [KIR16] para el tratamiento formal. La correspondencia entre shaders y CUDA es directa:

Concepto en Shaders Equivalente en CUDA
Pixel Shader (fragmento) CUDA Kernel: una función que se ejecuta miles/millones de veces en paralelo, una instancia por elemento (píxel o dato).
fragCoord (coordenada del píxel) threadIdx + blockIdx (identificador del hilo en CUDA): cada instancia sabe cuál es su elemento a procesar.
iChannel (textura de entrada) Memoria global de GPU (arrays en device memory): los datos de entrada que cada hilo lee.
fragColor (salida del píxel) Escritura en memoria global: el resultado que cada hilo produce.

En el TP Integrador, en lugar de procesar píxeles, cada hilo CUDA calculará hashes (MD5) para el proof-of-work de una blockchain. El concepto es el mismo: miles de cálculos independientes ejecutándose en paralelo en la GPU. Lo que cambió fue el dominio (gráficos -> criptografía), no el paradigma.


Referencias y Bibliografía

Computación paralela y GPU — papers fundacionales

Libros de texto

Documentación y recursos web