Going further than the tutorial:

  • Made the eyes hazel
  • Made small modifications to the mouth and highlights

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 createEye(vec2 uv) {
    // Re-center uv
    uv -= 0.5;
    
    float dist = length(uv);
    
    // Create iris
    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);
    
    // Edge shadow for white of eyes
    color.rgb *= 1.0 - S(0.45, 0.5, dist) * 0.5 * sat(-uv.y - uv.x);
    
    // Iris outline
    color.rgb = mix(color.rgb, vec3(0.0), S(0.3, 0.28, dist));
    
    irisColor.rgb *= 0.75 + S(0.35, 0.07, dist) * irisColorInner.rgb * 5.0; 
    irisColorInner.rgb *= 1.0 + S(0.2, 0.05, dist);
    
    color.rgb = mix(color.rgb, irisColor.rgb, S(0.28, 0.27, dist));
    //color.rgb = mix(color.rgb, irisColorInner.rgb, S(0.23, 0.20, dist));
    
    // Pupil
    color.rgb = mix(color.rgb, vec3(0.0), S(0.15, 0.14, dist));
    
    // 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);
    
    color.a = S(0.5, 0.48, dist);
    
    return color;
}

vec4 createMouth(vec2 uv) {
    // Reset origin
    uv -= 0.5;
    
    vec4 color = vec4(0.5, 0.18, 0.05, 1.0);
    
    uv.y *= 1.6;
    uv.y -= uv.x * uv.x * 4.0;
    
    float dist = length(uv);
    color.a = S(0.3, 0.28, dist);
    
    // Teeth
    float teethDist = length(uv - vec2(0.0, 0.45));
    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);
    
    // Create the 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));
    
    // Create highlight
    float highlight = S(0.41, 0.39, dist);
    highlight *= remap(0.41, -0.1, 0.75, 0.0, uv.y);
    color.rgb = mix(color.rgb, vec3(1.0), highlight);
    
    // Create 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) {
    vec4 color = vec4(0.0);
    
    // Mirror face
    uv.x = abs(uv.x);
    
    vec4 head = createHead(uv);
    vec4 eye = createEye(within(uv, vec4(0.03, -0.1, 0.37, 0.25)));
    vec4 mouth = createMouth(within(uv, vec4(-0.3, -0.41, 0.3, -0.11)));
    
    color = mix(color, head, head.a);
    color = mix(color, eye, eye.a);
    color = mix(color, mouth, mouth.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;
    
    vec3 color = vec3(0.0);
	
    // Output to screen
    fragColor = createSmiley(uv);
}

Tutorial I followed:


Resources:
ShaderToy

Leave a Reply

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