Technical Projects
Creating Custom Shaders
Shader Stills
Integrates Mathematical Systems including:
Fractional Brownian Motion
Random Noise
Cellular Noise
Trigonometry
Shaders animated using time.
Shadertoy Adaptions for Visualization
Main Code
vec2 st = gl_FragCoord.xy / u_resolution.xy;
st.x *= u_resolution.x / u_resolution.y;
vec3 color = vec3(0.0);
vec2 q = vec2(0.0);
q.x = fbm( st - 0.03 * (u_time));
q.y = fbm( st + 0.05 * (u_time));
vec2 r = vec2(0.);
r.x = fbm(st * 6.008 + 4.0 * noise(st) + q + vec2(8.3, 2.8)
+ 0.1 * sin(u_time));
r.y = fbm(st * 10.0 + 4. * noise(st) + q + vec2(8.3, 2.8)
+ 0.1 * cos(u_time));
color += vec3(fbm(5.0 * st * fbm(r)), fbm(5.0 * st*fbm(q)),
fbm(5.0 * st * fbm(r + q)));
color -= vec3( 1.0 - distance(vec2(0,1), vec2(st.x, st.y)))
* 0.1;
color -= vec3( 1.0 - distance(vec2(1,0), vec2(st.x, st.y)))
* 0.2;
gl_FragColor = vec4(color, 1.0);Main Code
vec2 st = gl_FragCoord.xy / u_resolution;
float stripes = 0.2 + sin(st.x * 3.14 * smoothstep(-5.0, 1.0,
sin(st.x * 4.0 + fbm(st.xx * vec2(1000.0, 20.0)) * 6.0)));
vec3 color = vec3(fbm(stripes * st.yy - 0.07), fbm(stripes * st.yy + 0.01),
fbm(stripes * st.yy + 0.03));
st *= 5.0;
float v = cellular(st) * 0.2;
color += vec3(v) * vec3(0.8, 0.8, 1.0);
gl_FragColor = vec4(color, 1.0);Real Time VFX and Shaders in Unreal Engine
Developing VFX in Unreal Engine using Niagara systems and in engine material shaders.
Methods include writing custom HLSL shaders, procedural randomness, and dynamic material parameters
Gravitational Explosion
Uses seven unique materials to provide layer and depth across the length of the explosion.
Divided into the 3 stages:
The Buildup/Anticipation
The Explosion
The Resolution
Combination of 20 different Niagara FXs Layered across the 3 stages
Combines Radial, Fractal, and Refraction manipulations of noise to create unique visual interest and accentuate chaos and intensity
Integrates Dynamic Parameters on multiple shaders
Three Stages
Materials
Niagara FXs
Stylized Explosion
Uses Dynamic Parameters on a material to dissolve the explosion
Implements Parameters and parameters over time to adjust sizes, colors, and dissolving
Layers smaller particle effects for debris coming off of explosion
Custom HLSL Shader
float result = 0.0f;
for (int i = 0; i < nSides; i++) {
for (int j = 0; j < nCopies; j++){
float angle = (i / nSides) * (time) * 3.14159f;
float2 pos = center + ((j / nCopies) * radius
* float2(cos(angle), sin(angle)));
result += length(pos - uv) < size; // Draw Circle
}
}
return(result);Custom Raytracing
Developed in C++
The Raytracer is designed with custom classes for spheres, planes, and triangles.
A scene is defined by a collection of geometry, a collection of lights, and a camera.
Code Snippets
class Sphere
{
public:
// Constructor and Destructor
Sphere(const Vector& p, const float rad, const Color& c);
~Sphere();
// Functions
float intersection(const Ray& r) const;
const Color get_color() const;
Color shade(const Vector& P, const Light& l);
Vector& get_position();
private:
// Stored Data
// Custom Vector class
Vector position;
float radius;
// Custom Color class
Color color;
};// Constructor
Sphere::Sphere( const Vector& p, const float rad, const Color& c ) :
position(p),
radius(rad),
color(c)
{}
// Destructor
Sphere::~Sphere(){}
// Intersection Function
float Sphere::intersection(const Ray& r) const
{
// Get information from sphere and ray
float f, t, tp, tn;
Vector RoPc = r.get_position() - position;
f = pow(radius, 2) - pow(RoPc.magnitude(), 2)
+ pow((r.get_direction() * RoPc), 2);
// based on f get t
if(f == 0.0){
t = - (r.get_direction() * RoPc);
return t;
} else if(f > 0.0){
tp = - (r.get_direction() * RoPc) + sqrt(f);
tn = - (r.get_direction() * RoPc) - sqrt(f);
// Find t based on gathered information
if(tp > 0.0 && tn > 0.0){
if(tp < tn){
t = tp;
} else {
t = tn;
}
} else if (tp > 0.0 && tn < 0.0){
t = tp;
} else if (tn < 0.0 && tn > 0.0){
t = tn;
} else {
t = -1.0f;
}
} else {
t = -1.0f;
}
return t;
}
// Gets stored Color
const Color Sphere::get_color() const
{
return color;
}
// Gets Diffuse Color information
Color Sphere::shade(const Vector& P, const Light& l)
{
float f;
Vector L = (l.get_position() - P).unitvector();
Vector normal = (P - position).unitvector();
// If facing light get color
if((L*normal) > 0) {
f = L*normal;
} else {
f = 0.0f;
}
Color finColor = l.get_color() * color * f;
return finColor;
}
// Gets Sphere position
Vector& Sphere::get_position(){return position;}class Plane
{
public:
// Constructor and Destructor
Plane( const Vector& p, const Vector& nd, const Color& c );
~Plane();
// Functions
float intersection(const Ray& r) const;
const Color get_color() const;
Color shade(const Vector& P, const Light& l);
private:
// Stored Data
Plane(){};
Vector position;
Vector normalDirection;
Color color;
};// Constructor
Plane::Plane( const Vector& p, const Vector& nd, const Color& c ) :
position(p),
normalDirection(nd),
color(c)
{}
// Destructor
Plane::~Plane(){}
// Intesection Function
float Plane::intersection(const Ray& r) const
{
float tcheck;
// Make sure that ray and plane CAN intersect
float dnom = r.get_direction() * normalDirection;
if(dnom == 0){
return -1.0f;
}
// Check Intersection
tcheck = - ((r.get_position() - position) * normalDirection)
/ dnom;
if(tcheck < 0){
return -1.0f;
} else {
return tcheck;
}
}
// Get Color
const Color Plane::get_color() const
{
return color;
}
// Get Shade based on light
Color Plane::shade(const Vector& P, const Light& l)
{
float f;
Vector L = (l.get_position() - P)
/ (l.get_position() - P).magnitude();
Vector normal = normalDirection.unitvector();
if(L*normal > 0)
f = L * normal;
else
f = 0.0f;
Color finColor = l.get_color() * color * f;
return finColor;
} for (int j = 0; j < img_height; ++j) {
for(int i = 0; i < img_width; ++i) {
//remapping from pixel coordinates to image plane
u = (1.0f - (2.0f * (float)(i) / (img_width - 1))) * tanhfov2;
v = (1.0f - (2.0f * (float)(j) / (img_height - 1))) * tanvfov2;
//ratrace magic
Vector rayDir = firstScene.get_Camera().view(u, v);
Ray ray = Ray(firstScene.get_Camera().get_position(),
rayDir);
pixel = Trace(ray, firstScene);
//write into ppm
int r = (int)(pixel.red() * 255.0f);
int g = (int)(pixel.green() * 255.0f);
int b = (int)(pixel.blue() * 255.0f);
outFile << r << ' ' << g << ' ' << b << ' ';
}
}Lunar Deep
Production
Producer for a game production project with a team of 18 people.
Developed game mechanics.
Implemented procedural materials
Assisted with character animation, surfacing, and VFX.