Going further than the tutorial:

  • Changed the eye color to be hazel
  • Small modifications to the smile, pupils, highlights, and eyebrows

Live Version:
Try clicking and dragging with your mouse!


GLSL / ShaderToy Code

#define S(a, b, t) smoothstep(a, b, t)
#define sat(x) clamp(x, 0.0, 1.0)

float remap01(float a, float b, float t) {
    return sat((t - a) / (b - a));
}

float remap(float a, float b, float c, float d, float t) {
    return sat((t - a) / (b - a)) * (d - c) + c;
}

vec2 within(vec2 uv, vec4 rect) {
    return (uv - rect.xy) / (rect.zw - rect.xy);
}

vec4 createBrow(vec2 uv, float smile) {
    float offset = mix(0.1, 0.0, smile);
    uv.y += offset;
    
    float y = uv.y;
    
    // Skew eyebrows down
    uv.y += uv.x * mix(1.0, 0.625, smile) - 0.3;
    
    // Push out from center
    uv.x -= mix(-0.025, 0.05, smile);
    
    uv -= 0.5;
    
    // --- Brows ---
    
    vec4 color = vec4(0.0);
    float blur = 0.1;
    
    float dist1 = length(uv);
    float s1 = S(0.4, 0.475 - blur, dist1);
    float dist2 = length(uv - vec2(0.1, -0.2) * 0.75);
    float s2 = S(0.5, 0.5 - blur, dist2);
                         
    float browMask = sat(s1 - s2);
                         
    float colorMask = remap01(0.6, 0.9, y) * 0.8;
    colorMask *= S(0.7, 1.0, browMask);
    
    vec4 browColor = mix(vec4(0.4, 0.2, 0.2, 1.0), vec4(1.0, 0.75, 0.5, 1.0), colorMask);
    
    // --- Shadow ---
    
    uv.y += 0.1 - offset;
    blur += mix(0.0, 0.2, smile);
    
    dist1 = length(uv);
    s1 = S(0.4, 0.475 - blur, dist1);
    dist2 = length(uv - vec2(0.1, -0.2) * 0.75);
    s2 = S(0.5, 0.5 - blur, dist2);
    
    float shadowMask = sat(s1 - s2);
    
    color = mix(color, vec4(0.0, 0.0, 0.0, 1.0), S(0.0, 1.0, shadowMask) * 0.5);
    color = mix(color, browColor, S(0.2, 0.4, browMask));
    return color;
}

vec4 createEye(vec2 uv, float side, vec2 mouse, float smile) {
    // Re-center uv
    uv -= 0.5;
    
    // Swap highlight
    uv.x *= side;
    
    float dist = length(uv);
    
    // Create iris color
    vec4 irisColor = vec4(0.396, 0.263, 0.129, 1.0);
    vec4 irisColorInner = vec4(0.055, 0.169, 0.114, 0.5);
    
    // --- White of the eye ---
    
    vec4 color = vec4(1.0);
    color = mix(color, irisColor, S(0.1, 0.7, dist) * 0.5);
    color.a = S(0.5, 0.48, dist);
    
    // Edge shadow for white of eyes
    color.rgb *= 1.0 - S(0.45, 0.5, dist) * 0.5 * sat(-uv.y - uv.x * side);
    
    // --- Iris ---
    
    // Iris to follow the mouse
    dist = length(uv - mouse * 0.6);
    
    // Iris outline
    color.rgb = mix(color.rgb, vec3(0.0), S(0.3, 0.28, dist));
    
    // Draw iris
    irisColor.rgb *= 0.75 + S(0.35, 0.07, dist) * irisColorInner.rgb * 5.0; 
    irisColorInner.rgb *= 1.0 + S(0.2, 0.05, dist);
    float irisMask = S(0.28, 0.27, dist);
    color.rgb = mix(color.rgb, irisColor.rgb, irisMask);
    
    // --- Pupil ---
    
    dist = length(uv - mouse * 0.65);
    float pupilSize = mix(0.3, 0.15, smile);
    float pupilMask = S(pupilSize, pupilSize * 0.85, dist);
    
    // Constrain iris to pupil
    pupilMask *= irisMask;
    color.rgb = mix(color.rgb, vec3(0.0), pupilMask);
    
    // --- Highlights ---
    
    // Animate highlights
    float time = iTime * 3.0;
    vec2 offset = vec2(sin(time + uv.y * 25.0), sin(time + uv.x * 25.0));
    offset *= 0.01 * (1.0 - smile);
    
    uv += offset;
    
    // Draw highlights
    float highlight = S(0.1, 0.05, length(uv - vec2(-0.15, 0.15)));
    highlight += S(0.07, 0.01, length(uv - vec2(0.08, -0.08)));
    
    color.rgb = mix(color.rgb, vec3(1.0), highlight);
    
    return color;
}

vec4 createMouth(vec2 uv, float smile) {
    // Reset origin
    uv -= 0.5;
    
    // Mouth color
    vec4 color = vec4(0.5, 0.18, 0.05, 1.0);
    
    uv.y *= 1.8;
    uv.y -= uv.x * uv.x * 2.0 * smile;
    
    uv.x *= mix(2.5, 1.0, smile);
    
    float dist = length(uv);
    color.a = S(0.5, 0.48, dist);
    
    // --- Teeth ---
    vec2 teethUv = uv;
    teethUv.y += (abs(uv.x) * 0.5 + 0.1) * (1.0 - smile);
    float teethDist = length(teethUv - vec2(0.0, 0.6));
    vec3 teethColor = vec3(1.0) * S(0.6, 0.35, dist);
    color.rgb = mix(color.rgb, teethColor, S(0.4, 0.37, teethDist));
    
    // --- Tongue ---
    float tongueDist = length(uv + vec2(0.0, 0.5));
    color.rgb = mix(color.rgb, vec3(1.0, 0.5, 0.5), S(0.5, 0.2, tongueDist));
    return color;
}

vec4 createHead(vec2 uv) {
    // Orange color
    vec4 color = vec4(0.9, 0.65, 0.1, 1.0);
    
    // Create a circle
    float dist = length(uv);
    color.a = S(0.5, 0.49, dist);
    
    // --- Edge shadow ---
    float edgeShadow = remap01(0.35, 0.5, dist);
    edgeShadow *= edgeShadow;
    color.rgb *= 1.0 - edgeShadow * 0.5;
    
    // Create outline
    color.rgb = mix(color.rgb, vec3(0.6, 0.3, 0.1), S(0.4799, 0.48, dist));
    
    // --- Highlight ---
    float highlight = S(0.41, 0.39, dist);
    highlight *= remap(0.41, -0.1, 0.75, 0.0, uv.y);
    highlight *= S(0.18, 0.19, length(uv - vec2(0.21, 0.08)));
    color.rgb = mix(color.rgb, vec3(1.0), highlight);
    
    // --- Cheeks ---
    dist =  length(uv - vec2(0.25, -0.2));
    float cheek = S(0.2, 0.01, dist) * 0.3;
    cheek *= S(0.17, 0.16, dist);
    color.rgb = mix(color.rgb, vec3(1.0, 0.1, 0.1), cheek);
    
    
    return color;
}

vec4 createSmiley(vec2 uv, vec2 mouse, float smile) {
    vec4 color = vec4(0.0);
    
    // Mirror face while determining side
    if (length(uv) < 0.5) {
        float side = sign(uv.x);
    	uv.x = abs(uv.x);
    
        vec4 head = createHead(uv);
        vec4 eye = createEye(within(uv, vec4(0.03, -0.1, 0.37, 0.25)), side, mouse, smile);
        vec4 mouth = createMouth(within(uv, vec4(-0.3, -0.43, 0.3, -0.13)), smile);
        vec4 brow = createBrow(within(uv, vec4(.03, .2, .4, .45)), smile);

        color = mix(color, head, head.a);
        color = mix(color, eye, eye.a);
        color = mix(color, mouth, mouth.a);
        color = mix(color, brow, brow.a);
	}
    return color;
}

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

    // Move origin to the center
    uv -= 0.5;
    
    // Account for aspect ratio
    uv.x *= iResolution.x / iResolution.y;
    
    // Get mouse coordinates
    vec2 mouse = iMouse.xy / iResolution.xy;
    mouse -= 0.5;
    
    //uv -= mouse * (0.5 - length(uv)) * 0.15;
    uv -= mouse * (0.5 - dot(uv, uv));//  * 1.0;
    // Initialize the color value to be black
    vec3 color = vec3(0.0);
	
    float smile = cos(iTime) * 0.5 + 0.5;
    // Output to screen
    fragColor = createSmiley(uv, mouse, smile);
}

Tutorial I followed:


Resources:
ShaderToy

Leave a Reply

Your email address will not be published. Required fields are marked *