Showing posts with label Graphics Programming. Show all posts
Showing posts with label Graphics Programming. Show all posts

Feb 3, 2025

3D Graphics Demo Program

Solo Project
March 2023 - May 2023
Skills Utilized: C++, OpenGL, JSON5, ImGUI, Graphics Programming, Game Engine Programming

Introduction

This was a course project exploring various graphics techniques. I implemented Toon shading, Shadow mapping, Noise-based terrain generation, Procedural mesh generation, Catmull-Rom splines, and Hermite curves.

Since the project required handling multiple scenes and shaders, I developed a simple data-driven engine that allowed assets like shaders, textures, and models to be registered and scenes to be configured dynamically at runtime through data modifications.

Showcase

↑ Toon Shading Showcase

↑ Shadow Mapping Showcase

Technical Highlights

Toon shading

This was the most enjoyable topic. The technique involves mapping the difference between the normal vector and light direction into discrete steps rather than a smooth gradient, creating a cel-shaded look seen in some games. Implementing it myself was fun, and the unique look and feel were refreshing.

One interesting challenge was mitigating aliasing artifacts at the boundaries of shading steps. A naïve implementation results in noticeable stair-step artifacts along the edges. To smooth these transitions, I used fwidth in the shader to blend the two colors over a few pixels near the boundary, reducing the harshness of the effect.

Shadow mapping

Generating shadow maps was also an interesting experience. While there are various advanced techniques, I implemented a classic, straightforward method for learning purposes. The scene is rendered from the light’s perspective to create a depth buffer, which is then used to determine shadowed areas based on depth comparisons and normal alignment.

Data-driven engine

Given the project's need for frequent scene and shader modifications, I developed a simple engine to manage objects and scenes efficiently. It automated scene and object handling and allowed assets such as shaders, textures, and models to be configured in a data-driven manner, enabling runtime modifications without recompilation.

↑ The tessellation scene's json5 file

Conclusion

Through this project, I explored various 3D graphics techniques and gained hands-on experience implementing them from scratch. I found toon shading particularly engaging, as it involved both technical problem-solving and aesthetic considerations. Shadow mapping reinforced my understanding of depth-based rendering techniques, while building a simple data-driven engine helped me appreciate the importance of flexible scene management. Overall, this project deepened my understanding of graphics programming and engine architecture.

Feb 2, 2025

Blinded

Team of 5
March 2023 - June 2023
Skills Utilized: C++, OpenGL, GLSL, JSON5, Graphics Programming

Introduction

Blinded is a boss rush-focused souls-like action game where the player restores red, green, and blue to a colorless world by defeating powerful bosses.

In this project, I developed a fragment shader for dynamic color removal and restoration, eliminating the need for multiple textures. I also implemented data-driven enemy and level design, streamlining iteration and balancing. These technical solutions enhanced both the game's artistic vision and development efficiency.

Showcase












Technical Highlights

Data Driven

I have transitioned various game elements, such as monster AI parameters, collision boxes, and level layouts, to a data-driven system using JSON5. This allowed for easy modification and extension without strict formatting constraints. By declaring a struct, exporting its members, and calling a parsing function, we could seamlessly populate in-game data with minimal effort.

Defining enemy attributes and placements in external files eliminated the need for recompiling or relaunching the game, greatly accelerating our iteration cycle. This significantly improved efficiency when fine-tuning enemy behaviors and level layouts.

↑Example of a level layout

↑Example of an enemy's AI parameters

RGB Fragment Shader for Dynamic Color Removal


To optimize asset management, I replaced pre-drawn grayscale and partial-color textures with a fragment shader that dynamically removes red, green, or blue from a full-color image. This eliminated the need for multiple texture versions, significantly reducing asset workload.

A key challenge was ensuring colors were removed correctly rather than simply setting RGB values to zero, which would turn everything black. Instead, we needed to gray out the removed components proportionally.

To achieve this, we used the HSV color model, which represents color using hue, saturation, and value. The hue determines the color type. By measuring the hue's proximity to the removed color, we calculated how much of it should be grayed out. The closer a color was to the removed hue, the more it was desaturated.


Rather than directly desaturating the color, we shifted the hue outside the removed color range. This ensured that a pure yellow (which contains both red and green) would still retain some green when red was removed, preventing incorrect full desaturation.
The final result was an interpolated blend between the original color and a grayscaled version based on how much of the removed component was present.

↑ Part of the final shader

This approach not only streamlined asset creation but also enabled greater gameplay flexibility, allowing players to dynamically regain colors in a non-linear progression.

Conclusion

Through Blinded, I gained valuable experience in shader programming and data-driven design. The shader development process deepened my understanding of color manipulation in graphics programming, particularly when working with RGB components and HSV models. Additionally, transitioning the game’s enemies and levels to a data-driven approach taught me how to optimize workflows and speed up iteration cycles, greatly improving both development efficiency and gameplay balance. This project enhanced my problem-solving skills, especially when faced with complex technical challenges and the need for creative solutions.

Nov 11, 2020

Q

Team of 5
September 2020 - December 2020
Skills Utilized: C++, OpenGL, Python, Graphics Programming, Scripting System, Template Metaprogramming

Introduction

During this semester's GAM class, my group decided to create a 2D action platformer game. I was responsible for constructing the graphics layer of the custom game engine using OpenGL and establishing a Python scripting system.
Built an object-oriented graphics layer encapsulating OpenGL concepts like meshes, textures, and shaders. To enable fast iteration, implemented a Python scripting system by embedding Python into the engine and exposing engine functions using template metaprogramming.

Showcase

↑ Prototype Demo

↑ Graphics showcase




↑ Scripting system showcase







↑ Scripting system showcase (integrated with the engine)




GitHubFunction Property Inspector (Part of the Python Embedder)

Responsibilites

  • Technical Director
  • Graphics Programming
  • Scripting System
  • Part of the Engine Programming
  • Other Gameplay Programming

Technical Highlights

Graphics layer of the engine

One of the challenges when working with OpenGL in an object-oriented language is that it functions as a giant state machine. While efforts have been made to encapsulate some aspects in version 4.5 and above, the core inconvenience remains. Consequently, during the design phase of the engine's graphics layer, I prioritized an object-oriented approach. I encapsulated each distinct concept, such as mesh, texture, and shader, in its own class to maximize encapsulation.


Python scripting system

Last semester, an assignment required the application of template programming. I decided to develop a Python embedder for personal exploration. And in this semester, for the game project, I decided to upgrade and use this embedder instead of relying on existing solutions, well, just for fun.
It automates the cumbersome process of registering C++ functions for using them in Python. All you have to do is one macro or a function call, depending on your preference. It heavily uses template metaprogramming for that automation.
One biggest challenge was iterating over the variadic template parameters. To expose a function to Python, you need to make another function that takes in the PyObjects, parses them then calls the original function with the parsed objects.
I was making that part of the automation and I needed to parse an arbitrary amount of arguments, so I was taking the list of the parameter types as a std::tuple. I needed to iterate over them to parse them. I couldn't use a for loop since the index of the std::tuple_element_t should be complie-time constant.
What should I do? The only thing I know in extra is the count of the parameters. After many trials and errors, I managed to figure out a solution. Make an index sequence with that count, take them with std::index_sequence<ArgumentIndices...>, and call a function for each of them with { parse<Tuple, Indices>(args)... }.




With this, I was able to register all different kinds of functions with various parameter lists with a single function call.

Conclusion

It was fun to design and build a graphics layer of the engine and implement a scripting system, which allowed us to iterate faster on the gameplay programming. Throughout the project, I gained valuable insights into OpenGL and template metaprogramming, as well as the integration of a scripting language into a game engine. These experiences collectively contributed to a significant enhancement of my skillset.