Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# lab06-proceduralFace

# Saahil Gupta - lab06-proceduralFace
- [Shadertoy Link](https://www.shadertoy.com/view/W3jBDK)
- Please note: for some reason, ShaderToy decided to play mean today and not let me make my project public or unlisted, even though my account is verified. Funny stuff. Hopefully this will be fixed by the submission deadline and I will be able to update this readme. However, in the case that it isn't, I've included the code in this PR at `main.glsl`.

The following images show a regular face, surprised face, sleepy face, and sad face :)

<img width="1006" height="560" alt="image" src="https://github.com/user-attachments/assets/0b3c2c66-c20d-46c3-8e45-5c6e248849ce" />

<img width="992" height="558" alt="image" src="https://github.com/user-attachments/assets/ac3856a1-ee64-4aad-a19e-936ff4522609" />

<img width="1007" height="564" alt="image" src="https://github.com/user-attachments/assets/ca93fef8-b0be-4d7a-a142-3fe177039d9d" />

<img width="994" height="553" alt="image" src="https://github.com/user-attachments/assets/767f1431-ed19-4bc7-9e8b-f79c879ee4b1" />


# Instructions
Let's practice parameterization! We'll be starting with an oh-so-beautiful gingerbread man face that looks like this:
![image](https://github.com/user-attachments/assets/4707eb0a-b25e-4eda-84e3-3bb336981781)

Expand Down
149 changes: 149 additions & 0 deletions main.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// PROCEDURAL GINGERBREAD FACE

float faceSDF(vec3 queryPos)
{
// Procedural Face Attributes
// These should all be values [0, 1]
float EYE_SEPARATION = 0.5;
float SURPRISE = 0.0;
float SLEEPY = 0.0;
float SADNESS = 1.0;


float ball = sphereSDF(queryPos, vec3(0.0, 1.0, 0.0), 2.0);

float eyePosition = mix(0.5, 0.9, EYE_SEPARATION);
float eyeLeft = sphereSDF(queryPos, vec3(-eyePosition, 0.9, 2.0), mix(mix(0.2, 0.4, min(SURPRISE + SADNESS * 0.6, 1.0)), 0.0, SLEEPY));
float eyeRight = sphereSDF(queryPos, vec3(eyePosition, 0.9, 2.0), mix(mix(0.2, 0.4, min(SURPRISE + SADNESS * 0.6, 1.0)), 0.0, SLEEPY));

float eyeBrowLeft = capsuleSDF(queryPos, vec3(-0.5, mix(mix(1.5, 2.0, min(SURPRISE + SADNESS * 0.4, 1.0)), 1.0, SLEEPY), 2.0), vec3(-0.9, mix(mix(1.5, 1.7, SURPRISE), 0.9, SLEEPY), 2.0), 0.1);
float eyeBrowRight = capsuleSDF(queryPos, vec3(0.5, mix(mix(1.5, 2.0, min(SURPRISE + SADNESS * 0.4, 1.0)), 1.0, SLEEPY), 2.0), vec3(0.9, mix(mix(1.5, 1.7, SURPRISE), 0.9, SLEEPY), 2.0), 0.1);

float mouthBase = sphereSDF(queryPos, vec3(0.0, mix(-0.2, 0.0, SLEEPY), mix(1.5, 1.7, SLEEPY)), mix(mix(mix(0.1, 0.8, SURPRISE), 0.08, SLEEPY), 0.0, SADNESS));


float eyes = smoothUnion(eyeRight, eyeLeft, 0.1);
float eyebrows = smoothUnion(eyeBrowLeft, eyeBrowRight, 0.1);
float eyeStuff = smoothUnion(eyes, eyebrows, 0.0);
float faceBase = smoothSubtraction(mouthBase, ball, 0.05);
return smoothUnion(faceBase, eyeStuff, 0.1);
}


float sceneSDF(vec3 queryPos)
{
float model1 = faceSDF(queryPos);

return model1;

float plane = planeSDF(queryPos, 0.0);

//return smoothUnion(plane, smoothUnion(smoothUnion(model2, model1, 0.25), model3, 0.25), 0.25);
}


Ray getRay(vec2 uv) {
Ray ray;

float len = tan(3.14159 * 0.125) * distance(EYE, REF);
vec3 H = normalize(cross(vec3(0.0, 1.0, 0.0), REF - EYE));
vec3 V = normalize(cross(H, EYE - REF));
V *= len;
H *= len * iResolution.x / iResolution.y;
vec3 p = REF + uv.x * H + uv.y * V;
vec3 dir = normalize(p - EYE);

ray.origin = EYE;
ray.direction = dir;
return ray;
}

Intersection getRaymarchedIntersection(vec2 uv)
{
Ray ray = getRay(uv);
Intersection intersection;

vec3 queryPoint = ray.origin;
for (int i=0; i < MAX_RAY_STEPS; ++i)
{
float distanceToSurface = sceneSDF(queryPoint);

if (distanceToSurface < EPSILON)
{

intersection.position = queryPoint;
intersection.normal = vec3(0.0, 0.0, 1.0);
intersection.distance = length(queryPoint - ray.origin);

return intersection;
}

queryPoint = queryPoint + ray.direction * distanceToSurface;
}

intersection.distance = -1.0;
return intersection;
}


vec3 estimateNormal(vec3 p) {
return normalize(vec3(
sceneSDF(vec3(p.x + EPSILON, p.y, p.z)) - sceneSDF(vec3(p.x - EPSILON, p.y, p.z)),
sceneSDF(vec3(p.x, p.y + EPSILON, p.z)) - sceneSDF(vec3(p.x, p.y - EPSILON, p.z)),
sceneSDF(vec3(p.x, p.y, p.z + EPSILON)) - sceneSDF(vec3(p.x, p.y, p.z - EPSILON))
));
}

vec3 getSceneColor(vec2 uv)
{
Intersection intersection = getRaymarchedIntersection(uv);

DirectionalLight lights[3];
vec3 backgroundColor = vec3(0.);


lights[0] = DirectionalLight(normalize(vec3(10.0, 10.0, 15.0)),
SUN_KEY_LIGHT);
lights[1] = DirectionalLight(vec3(0., 1., 0.),
SKY_FILL_LIGHT);
lights[2] = DirectionalLight(normalize(-vec3(15.0, 0.0, 10.0)),
SUN_AMBIENT_LIGHT);
backgroundColor = SUN_KEY_LIGHT;

vec3 albedo = vec3(0.5);
vec3 n = estimateNormal(intersection.position);

vec3 color = albedo *
lights[0].color *
max(0.0, dot(n, lights[0].dir));

if (intersection.distance > 0.0)
{
for(int i = 1; i < 3; ++i) {
color += albedo *
lights[i].color *
max(0.0, dot(n, lights[i].dir));
}
}
else
{
color = vec3(0.5, 0.7, 0.9);
}
color = pow(color, vec3(1. / 2.2));
return color;
}

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

// Make symmetric [-1, 1]
uv = uv * 2.0 - 1.0;

// Time varying pixel color
vec3 col = getSceneColor(uv);

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