Full Details / Source Download Release Build

Solo Project
January 2024 — April 2024

**Demo Video Coming Soon**

A C++ DX11 3D renderer coded from scratch as a practice project. Features Cook-Torrance PBR shading with various rendering techniques such as parallax occlusion, bloom, image-based lighting, tessellation and shadow mapping. Click links above for details (full feature list and caveats) as well as a downloadable demo with a basic scene editor made with Dear ImGui. An improved renderer/engine is currently in development using DX12.

Download Interactive Demo

Solo Project
January 3 2022

After seeing a widely used black dissolve effect in some Darkest Dungeon II gameplay, I challenged myself to recreate the shader as close as possible in a day. Although it is possible the original effect used scrolling textures, my version is created exclusively with procedural noise in an HLSL shader. Try the downloadable demo to see an interactive sample UI and environment that mimics the game (press space to toggle environment and tab to toggle UI with animation).

The “flaming” edges are created with a noise alpha cutoff, with a step(...) function to get the hard edges. Voronoi noise was used as an additional alpha mask to create the familiar circular holes. UVs can be stretched via variables in the shader to make the circles look irregular. The scenes in the demo were created with material instances on quads of various sizes. I am satisfied with the final result, however additional particles and animation edits need to be made for a more accurate replication. Also, upon further inspection, the original effect contains one more layer of subtle scrolling noise to “wiggle” the hard edges on certain environmental elements.

The Fragment Shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
fixed4 frag(v2f i) : SV_Target {
    float2 unscaledUV = i.uv;
    i.uv *= float2(1 + _UvStretch_X, 1 + _UvStretch_Y);

    float gradient = (1 - saturate(i.uv.y + _GradientHeight)) * (1 - saturate(i.uv.x + _GradientWidth));

    float mask = (unscaledUV.x + 0.1) / (1 - unscaledUV.y);
    mask = smoothstep(_AngleMask - 0.9, _AngleMask, mask);

    float noise = Unity_SimpleNoise_float(i.uv + _UVShift - _NoiseScrollVector * _Time.x, _NoiseScale);

    float vOut;
    float vCells;
    Unity_Voronoi_float(
        ((i.uv + _UVShift) * float2(_VoronoiStretch_X, _VoronoiStretch_Y)) - _VoronoiScrollVector * _Time.x,
        _VoronoiAngleSpeed * _Time.x + 2,
        _VoronoiCellDensity, vOut, vCells
    );

    _VoronoiThreshold *= 1 - gradient;

    vOut = saturate(
        Unity_Remap_float(vOut, float2(0, 1), float2(-_VoronoiThreshold, _VoronoiThreshold) * mask)
    );

    noise = step(noise * mask, _NoiseStep * gradient);
    vOut = step(vOut, _VoronoiStep);

    return _Color * noise * vOut;
}

Note: Functions Unity_Voronoi_float(...), Unity_Remap_float(...), and Unity_SimpleNoise_float(...) are adapted from Unity’s Shader Graph documentation.

All variables that start with an underscore can be controlled in gameplay code. For example, _GradientHeight and _GradientWidth are the primary variables used to “dissolve in” sprite elements vertically or horizontally (this is used for the UI panel toggle animation). The two parameters are consolidated into the gradient variable to control both noise alpha thresholds at the same time. The _NoiseStep and _VoronoiStepvariables can be used to roughly control the length of the “dissolved” end (see video below for demonstration).

Note: For a real project I would probably limit all shader variables in the inspector with sliders with normalized values.

Since the scene uses many elements with the same shader, the noise generation will noticeable synchronize and look unnatural. To alleviate this, I added the _UVShift variable in line 10 so a random noise offset can be generated in gameplay code when the scene loads. A more elegant solution would be to generate a hash value based on object position within the shader.

Lastly, a mask value is calculated in the shader based on UV to create an optional diagonal alpha mask (line 7 — 8). The angle of this mask is controlled by only one variable for convenience; not very flexible but it is good enough for the demo. This mask is used to make the angled dissolved UI bars on the top of the screen.

Download Playable Build

Solo Project
July 25 2020 — July 27 2020

A clone of the mobile game Alto’s Adventure in Unity; made in a 2 day weekend as a challenge. The main appeal to the original game is the relaxing and satisfying gameplay, which is exemplified by the one button controls and what I believe to be customized physics.

Note: Recreation features similar visuals, backflip boost mechanic and slide physics. Mouse cursor unfortunately not hidden in build.

I opted for a mix of the built in Unity physics engine and application of some manual forces to make the movement more smooth. For example, a light constant downward force is applied every physics update whenever the player object is touching the ground. Additionally, whenever the player is not touching the ground, a raycast is cast directly below to get the normal of the ground. The player will automatically be gradually rotated to match the angle of the ground below. All these tweaks make it so the player does not need to worry about sliding with the ground unless interacting with the jump/spin mechanic.

In my implementation, the player object will still occasionally launch over smooth hills. The original game is quite strict in keeping the character in contact with the smooth ground curves. This leads me to believe the original may lean more heavily in manually calculated movement rather than relying on a physics engine. I also added a couple of personal touches in my build not present in the original; such as additional particle effects and a “glide” mechanic that lets the player briefly float mid air based on number of backflips performed (indicated by UI bar element).

Solo Project
August 2023, Ongoing

My first website.
Web technologies used: Jekyll, Liquid, Kramdown, Bundler, jQuery, SASS (Ruby, Javascript, HTML, and CSS/SCSS).
Theme: A heavily modified version of the “Forty” theme by HTML5 UP, adapted to Jekyll by Andrew Banchich.

2020 — Present

I streamed the development of Last Secutor full time (5+ streams a week) from 2020 - 2022 on Twitch. My streams primarily consisted of C#/HLSL programming. I also developed almost the entirety of PHYSARUM live on stream. Other streams activities include shader experiments, game rendering/programming research, game design discussion, and C++ practice. I still do occasional programming streams currently, but much less often.

Despite being a very small stream I always encourage aspiring game devs and programmers in chat to ask any technical questions that they may have. This has lead to impromptu lessons on beginner-friendly fundamental programming topics such as memory management, value vs. reference types, classes vs. structs and hash functions. As well as frequent explanations on how my game code/shaders work and pros and cons of different implementations. I have tutored many viewers in game development and programming over the years and gained a small following on my Twitch and Discord channel, where I answer questions off stream and personally handle all bug reports for my released projects.