Realizado por Prashant Jeswani Tejwani

Índice del contenido:

  1. Introducción
  2. Diseño
  3. Código implementado
  4. Descargar código en Processing
  5. Referencias

Introducción

Se implementa un shader de fragmentos y vértices:

  • Se describe el trabajo realizado argumentando las decisiones adoptadas para la solución propuesta
  • Se incluye las referencias y herramientas utilizadas
  • Se muestra el resultado con un gif animado

Diseño

El diseño ha sido el que se puede observar en la siguiente figura. Se representa una figura del corazón humano el cual al hacer click, simula un latido del corazón. Para que el latido sea sequencial, se debe dejar el botón del ratón apretado.

Código implementado

A continuación se describe el trabajo realizado. Se crean e inicializan las variables necesarias.

PShader shader;
PShape heart;
boolean help;


En la función setup() se inicializan las variables definidas anteriormente, y se carga el shader de fragmento y vértice. Además, el objeto del corazón rescalado:

void setup() {
  size(700, 500, P3D);
  noStroke();
  fill(204);
  help = true;
  shader = loadShader("fragment_shader.glsl", "vertex_shader.glsl");
  heart = loadShape("Human-Heart.obj");
  heart.scale(20);
  shader.set("fraction", 1.0);
}


En la función draw() se muestra u oculta el menú si el usuario ha pulsado la tecla ‘h’. Sino, se pasa el tiempo desde que se ha cargado el shade, se detecta si ha habido un click para activar el shader cargando el shader mediante la función shader(s). Luego, se establece las luces direccionales y se dibuja la figura mediante la función shape(s).

void draw() {
  if (help) {
    background(0);
    fill(255);
    textSize(26);
    text("Shaders II", width/2 - 60, height/2 - 80);
    textSize(14);
    text("Click for heart beating", width/2 - 80, height/2 - 40);
    text("Press 'h' to show/hide menu", width/2 - 100, height/2 - 20);
    text("© Prashant Jeswani Tejwani", 20, height - 20);
  } else {
    background(0);
    shader.set("time", millis() / 1000.0);

    // Activate shader por defecto
    if (mousePressed) {
      shader(shader);
    } else {
      resetShader();
    }

    float dirY = (mouseY / float(height) - 0.5) * 2;
    float dirX = (mouseX / float(width) - 0.5) * 2;
    directionalLight(204, 204, 204, -dirX, -dirY, -1);
    translate(width/2, height/2);
    rotateX(PI);
    rotateY(PI);
    heart.setFill(color(152, 0, 46));
    shape(heart);
  }
}


Si la variable help es verdadera, se imprime un lienzo de ayuda de los controles que dispone el usuario:


La función keyPressed() detecta cuando el usuario presiona la tecla ‘h’ mostrar u ocultar el menú.

void keyPressed() {
  if (key == 'h') help = !help;
}


El shader de fragmentos cargado es el siguiente. Los uniform y varying son definidos con sus correspondientes tipos, al principio del código. EL shader se encarga de establecer la intensidad del color de la luz sobre el corazón.

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

uniform float fraction;

varying vec4 vertColor;
varying vec3 vertNormal;
varying vec3 vertLightDir;

void main() {
  float intensity = max(0.0, dot(vertLightDir, vertNormal));
  vec4 color;

  if (intensity > pow(0.95, fraction)) {
    color = vec4(vec3(1.0), 1.0);
  } else if (intensity > pow(0.5, fraction)) {
    color = vec4(vec3(0.6), 1.0);
  } else if (intensity > pow(0.25, fraction)) {
    color = vec4(vec3(0.4), 1.0);
  } else {
    color = vec4(vec3(0.2), 1.0);
  }

  gl_FragColor = color * vertColor;
}


El shader de vértices cargado es el siguiente. Los uniform, attribute y varying son definidos con sus correspondientes tipos, al principio del código. El shader se encarga de simular el latido del corazón calculando la nueva posición mediante la variable newPosition.

uniform mat4 transform;
uniform mat3 normalMatrix;
uniform vec3 lightNormal;

attribute vec4 position;
attribute vec4 color;
attribute vec3 normal;

varying vec4 vertColor;
varying vec3 vertNormal;
varying vec3 vertLightDir;

uniform float time;

// 2D Random
float random (in vec2 st) {
  return fract(sin(dot(st.xy, 
    vec2(7.98, 60.2)))
    * 4268.541);
}

void main() {
  vec4 newPosition = position + vec4(normal, 1.0) * random(vec2(time)) / 40;
  gl_Position = transform * newPosition;
  vertColor = color;
  vertNormal = normalize(normalMatrix * normal);
  vertLightDir = -lightNormal;
}


A continuación, se muestra el resultado final mediante un gif animado:

Descargar código en Processing

Para la correcta ejecución en Processing, es necesario instalar el modo Shader. Esto se puede hacer de la siguiente manera:

Para descargar el código en Processing, acceda a: Descargar código en Processing o acceda a la carpeta del repositorio del proyecto en: Repositorio del proyecto


Referencias

Guión de prácticas

Página de Processing

Creación del enlace de descarga

Objetos .obj