2. Texturing

Texturing #

Problem statement #

Textures gives renders of 2d and 3d shapes colors and form, they are used in 3D modellin, video games and other fields of computing to give visual elemnt colors, shapes and effects.

Through the use of shaders, we can take a set of coordinates of a shape and apply to them textures and colors.

The application of the shader upon the shape can be changed through playing with the coordinates of the vertex, we will this knowledge on the exercises below

Exercise1

Redefine the shape texture coordinates to turn the above image upside down.

By playing with the coordinates, we can change the way the texture is mapped upon the shape, by playing with the vertical and horizontal coordinates, we can turn the texture upside down.

Exercises2

  1. Include the blue channel in the uv visualization (e.g., use blue with red or green channels).
  2. Use other shapes different than the quad as screen filters.

After drawing several shapes, and obtaining the shader with the blue channel values, we can implement them on the background, there is a slider for transitioning between the opacity of the texture with blue channel intensity, and the value of the blue value added.

Exercises 3

  1. Implement other coloring brightness tools such as HSV value V, HSL lightness L or Component average.
  2. Implement texture tinting by mixing color and texel interpolated data.

Within the .frag file, we can obtain the values of the effect we are trying to apply to the picture, we include:

HSV

HSL

Lightness

Color tint (picked from color picker)

Background #

Most of the code can be found within the .frag file

Within it we find the mathematical formulas we use to apply the effect upon the sample image.

Code #

Exercise 1 #

Code Texturing 1 .js
let uvShader;
let brighSlider;

function preload() {
  uvShader = readShader('/vc_page/sketches/shaders/texturing/uv1.frag',
                        { matrices: Tree.NONE, varyings: Tree.texcoords2 });
}

function setup() {
  createCanvas(300, 300, WEBGL);
  noStroke();
  
  shader(uvShader);
  // https://p5js.org/reference/#/p5/textureMode
  // best and simplest is to just always used NORMAL
  textureMode(NORMAL);
}

function draw() {
  background(0);
  /*
        y                  v
        |                  |
  (-1,1)|     (1,1)        (0,1)     (1,1)
  *_____|_____*            *__________*   
  |     |     |            |          |        
  |_____|_____|__x         | texture  |        
  |     |     |            |  space   |
  *_____|_____*            *__________*___ u
  (-1,-1)    (1,-1)       (0,0)    (1,0) 
  */
  beginShape();
   vertex(-1, -1, 0, 0, 1);
  vertex( 1, -1, 0, 1, 1);
  vertex( 1,  1, 0, 1, 0);
  vertex(-1,  1, 0, 0, 0);
  endShape();
}
Code Texturing 1 .frag
precision mediump float;
varying vec2 texcoords2;

void main() {
  gl_FragColor = vec4(texcoords2.xy, 0.0, 1.0);
}

Exercise 2 #

Code Texturing 2 .js
let easycam;
let uvShader;
let opacity, colorB;
let selection_box;
let figure = 'Rectangle';

function preload() {
  uvShader = readShader('/vc_page/sketches/shaders/texturing/uv2.frag',
              { matrices: Tree.pmvMatrix, varyings: Tree.texcoords2 });
}

function setup() {
  createCanvas(300, 300, WEBGL);
  // easycam stuff
  let state = {
    distance: 250,           // scalar
    center: [0, 0, 0],       // vector
    rotation: [0, 0, 0, 1],  // quaternion
  };
  easycam = createEasyCam();
  easycam.state_reset = state;   // state to use on reset (double-click/tap)
  easycam.setState(state, 2000); // now animate to that state
  textureMode(NORMAL);
  
  selection_box = createSelect();
  selection_box.position(10, 10);
  selection_box.style('color', 'black');
  selection_box.option('Rectangle');
  selection_box.option('Triangle');
  selection_box.option('Circle');
  selection_box.changed(selectEvent);
  
  opacity = createSlider(0, 1, 0.5, 0.01);
  opacity.position(10, 30);
  opacity.style('width', '280px');
  
  colorB = createSlider(0, 1, 0.5, 0.01);
  colorB.position(10, 50);
  colorB.style('width', '280px');
}

function selectEvent(){
  figure = selection_box.value();
}

function draw() {
  background(200);
  // reset shader so that the default shader is used to render the 3D scene
  resetShader();
  // world space scene
  axes();
  grid();
  translate(0, -70);
  rotateY(0.5);
  fill(color(255, 0, 255, 125));
  box(30, 50);
  translate(70, 70);
  fill(color(0, 255, 255, 125));
  sphere(30, 50);
  // use custom shader
  shader(uvShader);
  // https://p5js.org/reference/#/p5.Shader/setUniform
  uvShader.setUniform('opacity', opacity.value());
  uvShader.setUniform('colorB', colorB.value());
  // screen-space quad (i.e., x ∈ [0..width] and y ∈ [0..height])
  // see: https://github.com/VisualComputing/p5.treegl#heads-up-display
  beginHUD();
  noStroke();
  
  if (figure == 'Rectangle'){
    quad(0, 0, width, 0, width, height, 0, height);
  } else if (figure == 'Circle'){
    circle(width/2, height/2, width, height);
  } else {
    triangle(width/2, 0, 0, height, width, height);
  }
  endHUD();
}

function mouseWheel(event) {
  //comment to enable page scrolling
  return false;
}
Code Texturing 2 .frag
precision mediump float;

varying vec2 texcoords2;
varying vec4 color4;

uniform float opacity;
uniform float colorB;

void main() {
  gl_FragColor = vec4(texcoords2.xy, colorB , opacity);
}

Exercise 3 #

Code Texturing 3 .js
let lumaShader;
let img;
let grey_scale;
let selection_box;
let coloring_tool = 0;
let color_picker;
let tinting;

function preload() {
  lumaShader = readShader('/vc_page/sketches/shaders/texturing/luma.frag',
                        { varyings: Tree.texcoords2 });
  // image source: https://t.ly/Dz8W
  img = loadImage('https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Fire_breathing_2_Luc_Viatour.jpg/800px-Fire_breathing_2_Luc_Viatour.jpg');
}

function setup() {
  createCanvas(700, 500, WEBGL);
  noStroke();
  textureMode(NORMAL);
  
  colorMode(RGB, 1);
  
  shader(lumaShader);

  selection_box = createSelect();
  selection_box.position(15, 20);
  selection_box.style('color', 'black');
  selection_box.option('Original', 0);
  selection_box.option('Component Average', 1);
  selection_box.option('Luma', 2);
  selection_box.option('HSV', 3);
  selection_box.option('HSL', 4);
  selection_box.changed(selectEvent);
  
  tinting = createCheckbox('Tinting', false);
  tinting.style('color', 'white');
  tinting.position(15, 45);
  
  color_picker = createColorPicker(color(1.0, 0.0, 0.0));
  color_picker.position(15, 70);
  
  lumaShader.setUniform('texture', img);
}

function selectEvent(){
  coloring_tool = selection_box.value();
}

function draw() {
  background(0);
  
  tintingColor = color_picker.color();
  
  lumaShader.setUniform('coloring_tool', coloring_tool);
  
  if (tinting.checked()) {
    lumaShader.setUniform('tinting', true);
    lumaShader.setUniform('tintingColor', [red(tintingColor), green(tintingColor), blue(tintingColor), 1.0]);
  } else {
    lumaShader.setUniform('tinting', false);
  }
  
  quad(-width / 2, -height / 2, width / 2, -height / 2,
        width / 2, height / 2, -width / 2, height / 2);
}

Code Texturing 3 .frag
precision mediump float;

uniform int coloring_tool;
uniform sampler2D texture;
varying vec2 texcoords2;

uniform bool tinting;
uniform vec4 tintingColor;

// returns component average of given texel
float avg(vec3 texel) {
  return (texel.r + texel.g + texel.b)/3.0;
}

// returns luma of given texel
float luma(vec3 texel) {
  return 0.299 * texel.r + 0.587 * texel.g + 0.114 * texel.b;
}

// returns hsv of given texel
float hsv(vec3 texel) {
  return max(max(texel.r, texel.g), texel.b);
}

// returns hsl of given texel
float hsl(vec3 texel){
  float maxColor = max(max(texel.r, texel.g), texel.b);
  float minColor = min(min(texel.r, texel.g), texel.b);
  return (maxColor + minColor)/2.0;
}

//returns texture with tinting color
vec4 tintingTex(vec4 texel){
  return tintingColor * texel;
}

void main() {
  // texture2D(texture, texcoords2) samples texture at texcoords2 
  // and returns the normalized texel color
  vec4 texel = texture2D(texture, texcoords2);
  
  if (coloring_tool == 0){
    texel = texel;
  }else if (coloring_tool == 1){
    texel = vec4((vec3(avg(texel.rgb))), 1.0);
  }else if (coloring_tool == 2){
    texel = vec4((vec3(luma(texel.rgb))), 1.0);
  }else if (coloring_tool == 3){
    texel = vec4((vec3(hsv(texel.rgb))), 1.0);
  }else if (coloring_tool == 4){
    texel = vec4((vec3(hsl(texel.rgb))), 1.0);
  }
  
  if (tinting){
    texel = tintingTex(texel);
  }
  
  gl_FragColor = texel;
}

Conclusions #

Textures, 2D or 3D can be applied upon shapes, these shaders can contain both the information of the texture, or as shown in 2nd and 3rd Exercise, the math behind effects which can be applied upon a sample image.

References #