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.