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
90 changes: 20 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,72 +1,22 @@
# HW 6: Ray marching and SDFs

## Goal
In this assignment, you will be implementing SDF operators on various primitives and use a ray marcher to render them. Ray marching is a technique for rendering implicit surfaces where the ray-primitive intersection equation cannot be solved analytically.

**Warning**: this assignment diverges significantly from marching cubes, so switching options midway can be costly for your time and effort.

## Base code framework

We have provided a preview scene, and a toggle for the ray marcher rendering. When you correctly implement the ray marcher, the image should match the preview scene containing the simple geometry. Your ray marching calculation should be performed in the fragment shader.

### Ray Marcher (25 pts)

The ray marcher should generate a ray direction, and march through the scene using the distances computed from sphere tracing.

**Note**: Your scene won't be rendered until you have implemented the SDFs for primitives below.

- Generate Rays (15 pts): for each fragment inside the fragment shader, compute a ray direction for the ray marcher
- Sphere Tracing (10 pts): compute the nearest distance from the scene SDFs and update the ray marching's step size.

### SDF (50 pts)
##### Implement primitive SDFs (15pts):
These are simple primitives with well-defined SDFs. We encourage trying other SDFs not listed here, they are interesting!
- Sphere (3pts)
- Box (3pts)
- Cone (3pts)
- Torus (3pts)
- Cylinder (3pts)

##### Useful Operators (15pts)
To create constructive geometry, and interesting shapes (such as holes, bumps, etc.), implement the following operators to combine your primitive SDFs.
- Intersection (2pts)
- Subtraction (3pts)
- Union (2pts)
- Transformation (8pts)
- translation and scaling
##### Compute normals based on gradient (15 pts)

Compute the normals to use for shading your surface.
- Read Chapter 13 of [Morgan McGuire's notes](http://graphics.cs.williams.edu/courses/cs371/f14/reading/implicit.pdf)
##### Material (5pts)
Implement a simple Lambert material. Additional materials can earn extra points.

### Custom Scene (25 pts)
##### Create a mechanical device or a scene of your choice using all operators
- intersection, subtraction, union, transformation (20pts)
##### Animate the scene (5pts)
Use time as an input to some of your functions to animate your scene!

## Extra credits (Up to 30 pts)
- Implement SDF for [Mandelbulb](https://www.shadertoy.com/view/XsXXWS) (10pts)
- You need to implement naive raymarching (not sphere tracing) to get this to work
- Lighting effects
- Soft shadowing using secondary rays (5pts)
- Ambient occlusion (10pts)
- Additional materials besides Lambert. (5pts each)
- Additional SDFs besides the listed primitive. (5pts each)

## Resources
http://graphics.cs.williams.edu/courses/cs371/f14/reading/implicit.pdf

## Submission
- Update `README.md` to contain a solid description of your project
- Publish your project to gh-pages. `npm run deploy`. It should now be visible at http://username.github.io/repo-name
- Create a [pull request](https://help.github.com/articles/creating-a-pull-request/) to this repository, and in the comment, include a link to your published project.
- Submit the link to your pull request on Canvas.

## Deploy
- `npm run build`
- Add and commit all changes
- `npm run deploy`
- If you're having problems with assets not linking correctly, make sure you wrap you're filepaths in `require()`. This will make the bundler package and your static assets as well. So, instead of `loadTexture('./images/thing.bmp')`, do `loadTexture(require('./images/thing.bmp'))`.
## Project Description
Implemented a ray marcher with sphere tracing to render various primitives using SDFs and additional operators.

Implemented SDFs:
- sphere
- box
- cone
- torus
- cylinder
- triangular prism
- hexagonal prism

Additional operators:
- intersection
- subtraction
- union
- scaling
- computed normals based on gradients

The original RayMarching option in the program now renders a torus with Lambertian shading. The Machine option renders a combination of a few SDFs and union/subtraction operators. I had trouble getting the ray marcher to work properly for awhile, so I couldn't spend as much time playing around with the SDFs to make something cool. The camera controls also don't work anymore I don't really know why lol
157 changes: 157 additions & 0 deletions src/glsl/machine-frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@

#define MAX_GEOMETRY_COUNT 100
#ifdef GL_ES
precision highp float;
#endif

/* This is how I'm packing the data
struct geometry_t {
vec3 position;
float type;
};
*/
uniform vec4 u_buffer[MAX_GEOMETRY_COUNT];
uniform int u_count;
uniform float u_cameraFOV;
uniform mat4 u_cameraTransf;
uniform mat4 u_cameraProjectionInv;
uniform mat4 u_cameraViewInv;
uniform vec3 u_cameraPosition;
uniform float u_alpha;
uniform float u_aspect;
uniform float u_farClip;

varying vec2 f_uv;
varying vec3 f_position;

//-------------------------------------------------------------------------
float sphereSDF(vec3 p) {
return length(p) - 1.5;
}

float cylinderSDF(vec3 p, vec3 c) {
return length(p.xz - c.xy) - c.z;
}

float torusSDF(vec3 p, vec2 t) {
vec2 q = vec2(length(p.xz) - t.x, p.y);
return length(q) - t.y;
}

float boxSDF(vec3 p, vec3 b) {
vec3 d = abs(p) - b;
return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d,0.0));
}

float coneSDF(vec3 p, vec2 c) {
// c normalized
float q = length(p.xy);
return dot(c, vec2(q, p.z));
}

float triPrisSDF(vec3 p, vec2 h)
{
vec3 q = abs(p);
return max(q.z - h.y, max(q.x * 0.866025 + p.y * 0.5 , -p.y) -h.x * 0.5);
}

float hexPrisSDF( vec3 p, vec2 h )
{
vec3 q = abs(p);
return max(q.z - h.y, max((q.x * 0.866025 + q.y * 0.5), q.y) - h.x);
}

// operators
float opIntersect(float d1, float d2) {
return max(d1, d2);
}

float opUnion(float d1, float d2) {
return min(d1, d2);
}

float opSubtract(float d1, float d2) {
return max(-d1, d2);
}

// vec3 opTranslate(vec3 p, mat4 m) {
// vec3 q = inverse(m) * p;
// return sceneSDF(q);
// }

// total scene SDF
float sceneSDF(vec3 point) {
float dist1 = triPrisSDF(point, vec2(0.5,3.0));
float dist2 = opUnion(boxSDF(point, vec3(0.5,2.0,0.5)), sphereSDF(point));
float dist = opSubtract(dist1, dist2);
return dist;
}

float opScale(vec3 p, float s) {
return sceneSDF(p/s) * s;
}

vec3 estNormal(vec3 p) {
float EPSILON = 0.0001;
vec3 toRet = 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))
));
return toRet;
}
//-----------------------------------------------------------------------------------------
void main() {

// RAYMARCHING
float t = 0.1;
float EPSILON = 0.001;
float end = 100.0;
vec3 color = vec3(0.5,0.5,0.5);

// uv coordinates in NDC
float sx = (2.0 * f_uv.x) - 1.0;
float sy = 1.0 - (2.0 * f_uv.y);

vec3 eye = u_cameraPosition;
vec3 F = normalize(vec3(0.0,0.0,0.0) - eye);
vec3 R = normalize(cross(F, vec3(0.0, 1.0, 0.0)));
vec3 U = normalize(cross(R, F));

// screen point to world point
vec3 ref = eye + t * F;
vec3 len = abs(ref - eye);
vec3 V = (U * len * u_alpha);
vec3 H = (R * len * u_aspect * u_alpha);
vec3 p = ref + (sx * H) + (sy * V);

// get a ray from world point
vec3 ray_direction = normalize(p - eye);
vec3 point = eye + t * ray_direction;

for (int i = 0; i < 100; i++) {
point = eye + t * ray_direction;
float dist1 = triPrisSDF(point, vec2(0.5,3.0));
float dist2 = opUnion(boxSDF(point, vec3(0.5,2.0,0.5)),sphereSDF(point));
float dist = opSubtract(dist1, dist2);
if (dist < EPSILON) {
// then you're inside the scene surface
// color the fragment
color = vec3(0.5,0.5,0.7);
}
t += dist;
if (t >= end) {
// gone too far, end
break;
}
}

// some lame lambertian lighting, light source from the camera
vec3 normal = estNormal(point);
float d = clamp(dot(normal, normalize(u_cameraPosition - f_position)), 0.0, 1.0);

// color the output
gl_FragColor = vec4(d * color * 1.2, 1); // frag with Lambertian shading
// gl_FragColor = vec4(color, 1);

}
3 changes: 3 additions & 0 deletions src/glsl/pass-vert.glsl
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
varying vec2 f_uv;
varying vec3 f_position;

void main() {
f_uv = uv;
f_position = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
Loading