Spermulator 3D-Remake Final Report

Names: Sanika Bharvirkar, Circle Chen, Ashley Kao, Grant Zhao

Team 7
Link to this webpage: https://akao03.github.io/cs184-final-project-webpage/final-deliverable.html
Link to proposal webpage: https://akao03.github.io/cs184-final-project-webpage/proposal.html
Link to video: https://youtu.be/vfhD42AkNYw
Link to slides: https://https://docs.google.com/presentation/d/1s1tMPiY9L4b_FIPGE11HsnPa8kVIH6NJxUzUkmI2dg8/edit?usp=sharing

Link to homepage: https://akao03.github.io/cs184-final-project-webpage/

Abstract

In this project we turned Spermulator, a 2D game, into 3D. This involved turning all assets into 3D, adapting camera views, and developing new textures that would accurately represent light reflection. Our starting point was a 2D rendering pipeline in Unity with basic game function code and minimal if no shading. Only one of us had Unity experience at all, and only with a 2D pipeline. Over the course of the project, we learned a lot about Unity's 3D rendering pipeline, including converting all the assets and camera system to 3D. Every shader we made was from scratch in Unity's shader graph or custom .shader files, with the aid of online YouTube tutorials. We custom made our uterus model mesh and bacteria mesh in Blender and imported them into Unity, where we brought all the parts together. The result is Spermulator: 3D!

2D Spermulator
Spermulator 2D was our starting point.
3D Spermulator
Example of what Spermulator 3D looks like now.

Technical Approach

3D Asset Creation

To convert the original 2D Spermulator game into a 3D experience, we recreated all major assets in 3D, including the player sperm, bacteria cells, immune cells, and the uterus environment. We used a preexisting 3D sperm model and custom-modeled the uterus and bacteria in Blender, leveraging subdivision surface modeling for smoother organic geometry. Asset import and scaling were carefully adjusted to ensure consistency across the scene.

3D Freelook Movement and Camera System

Our control system was designed around four goals:
(1) Allow the player to look freely using the mouse,
(2) Move the player using WASD, relative to the camera,
(3) Rotate the sperm model to face its movement direction,
(4) Have the camera follow the player without feedback jitter.

To implement (1), we track pitch and yaw manually and directly rotate the player object based on mouse input. For (2), keyboard input is transformed from camera space to world space using the player’s rotation.

For (3), the sperm model (a child of the player GameObject) is rotated to face the movement direction without interfering with physics. We apply a fixed correction to account for the model’s default forward direction, then use Quaternion.Slerp for smooth interpolation.

For (4), we use Cinemachine’s ThirdPersonFollow to follow the player’s position. However, we disable Cinemachine’s PanTilt rotation module to avoid circular dependencies—PanTilt rotates the camera based on input, which would then affect player rotation and feed back into camera movement. By separating input-driven rotation (handled by the player) from camera follow behavior, we achieved stable and consistent controls.

Minimap

Based on TA suggestions in the proposal and milestone report, we decide to add a minimap to show the egg location and map layout. To do this, we used a secondary camera to render the scene from above. This camera was set to orthographic mode and positioned directly above the player. We then created a Render Texture in Unity and assigned it to the minimap camera. The minimap camera's output was displayed on a UI Raw Image component, allowing us to overlay it on the main game screen. To show the player's location, we transformed the player's position to the minimap's coordinate system and drew a small arrow representing the player, which points towards the player's direction.

Procedural Sperm Wiggle Shader

To simulate realistic swimming, we implemented a procedural GPU animation using Unity’s Shader Graph (URP). Rather than rigging a skeletal tail, we applied sinusoidal vertex displacement along the mesh.

The sperm model uses a custom coordinate system: local X = forward, Y = left, Z = down. In the shader, vertex Y and Z positions are displaced using sine and cosine functions of the form:


			Y += sin(X * frequency + time * speed) * amplitude  
			Z += cos(X * frequency + time * speed) * amplitude
			

This results in a helical tail motion, mimicking 3D swimming behavior. To keep the head stable while animating the tail, we attenuate the amplitude linearly based on X position:


			attenuation = 1 - saturate(X / maxX)
			

All displacement occurs in object space to ensure consistent behavior regardless of prefab scale. Our sperm prefab used a non-uniform scale (0.4, 0.4, 1), which required careful handling to avoid distortion.

Sperm Glow Shader

The sperm's glow shader was built on top of its existing shader graph from movement. We added two parameters to the shader graph, Color and GlowAmt, to control the color of the sperm and the intensity of its glow respectively. Color also controls the emission of the sperm, and by extension the color of its glow. Next, we added a Bloom effect to the scenes where we wanted the sperm to glow and adjusted the parameters such that pixels above the threshold brightness would glow with the desired intensity. Finally, to allow the camera to see the post-processing effects, we enabled post-processing under the camera object's options.

Preliminary 3D object glow
Sperm Glow Shader Graph

Custom Shading for the Game Map, Egg, Bacteria

The shading for the bacteria mucus uses translucency and emissions to simulate the mucus’s semi-transparent appearance. The shader uses a lerp and scrolling speed to make the emission seem translucent as well.

We compute two scrolling offsets: o1(t) = (t * scrolling_speed), o2(t) = 1 - o1(t) Them we build two tiled offset UV’s from a texture of an electrical current to get two currents rolling against each other:

U1​ =T(u∘k+(o1​(t),o1​(t)))

U2​ =T(u∘k+(o2​(t),o2​(t)))

And then lerp them over the current screen color to get the emission:

Emission = Lerp(S(u), U1 + U2, 0.5)

Where S(u) is the screen color pixel. This gives us the final shader! We use a base color, and then emit over it to create the effect. This implementation differed from the tutorial because we only really needed the emission effect. Additional, for this shader we chose have an input color so that it was user adjustable.

3D Egg Shader
Glowing 3D Shader with translucency effects.

The Game Map uses a combination of displacement mapping and simulated sub surface shading to mimic real flesh.

Displacement mapping:

We sample height from a height map and then add it to the current vertex position of the object to get our displacement mapping. The math for the new positions looks as follows:

position = sample(height_map) * intensity * N + position

Where N is the object normal, intensity is a user modified scalar, and position is the object vertex position. Example of displaced material:

3D Map
Map before shader displacement
3D Map
Map after shader displacement

We differed from the regular vertex displacement tutorials by displacing by a sample from an height map. This was tricky because Unity expects vertex position in Object space, and the height map is in World space. We had to convert the height map position to Object space before sampling it, and then convert the sampled value back to World space before adding it to the vertex position. We also differed from the reference by using an ambient occlusion map to make grooves seem deeper without having to edit our height map, as a common issue we ran into was that the values from our sampled height map were too low. This meant that to make the bumps seem more apparent, we would accidently make objects much to big, so the ambient occlusion map helped solve that.

Finally, we need some way for the uterus walls to have that same see-through effect real flesh does, such as when you hold a flashlight against your hand. This wasn’t fully possible in URP (only supported by HDRP), but we simulated it using emissions. Unity doesn’t ship with a function to calculate attenuation and direction from lights other than the main one, so we followed a tutorial and customized the script for our needs. The material emission is such:

Emission = intensity * shineThroughColor * pointlight_attenuation(world_position)

We additionally clamped the emission values to ensure the transition between base and emission color are not too harsh.

Uterus Wall without subsurface scattering
Uterus Wall with simulated subsurface scattering

Geometric Modeling

To construct the map environment of the 3D Spermulator game, we used Blender to model organic structures such as the vagina and uterus. The modeling process began with basic primitive meshes including cylinders, spheres, and cones which served as foundational shapes for the overall structure. We reshaped these primitives using Bezier curves to guide the organic flow of the forms, especially for the connection between the uterus and the ovaries. This allowed us to define smooth, natural contours with fine control over curvature to allow for a circular bend. To further refine and deform the geometry, we employed lattice modifiers, which enabled the smooth manipulation of grouped vertices without distorting the overall mesh structure. This was particularly useful for creating the subtle ridges and inner folds characteristic of the vaginal lining as seen in the pictures of the game. A key technique used to enhance control over the model's shape was the application of Catmull-Clark subdivision surfaces. This subdivision method increased the number of faces and edges across the mesh, allowing for greater detail and smoother curvature during sculpting such as for the vaginal canal ridges and bumps. It also gave us finer control over subtle deformations that allow us to transfer over for real-time rendering in Unity. Throughout the modeling process, we maintained a focus on structural integrity and scaling. We ensured proper mesh orientation, removed stray or excessive vertices, and corrected flipped normals to prevent lighting and collision issues in Unity.

The model of the game in blender
The ridges and bends inside the vaginal canal cylinder model in blender
the curvature using bezier's curve connecting the uterus and ovaries in blender
the barriers inside the ovary in blender but not used in final game

Problems Encountered and Solutions

Lessons Learned

Results

The game is now hosted on itch.io, playable directly from the browser on any laptop. Go to this link to play the game: https://circlecly.itch.io/spermulator-3d-remake

Below, we also provide side-by-side comparisons of 2D and 3D Spermulator.

Spermulator 2D Start Screen
Spermulator 3D Start Screen
Spermulator 2D Tutorial
Spermulator 3D Tutorial
Spermulator 2D Customization Screen
Spermulator 3D Customization Screen
Spermulator 2D Interior
Spermulator 3D Interior
Spermulator 2D Acid
Spermulator 3D Acid
Spermulator 2D Base and Mucus
Spermulator 3D Base and Mucus
Spermulator 2D Egg
Spermulator 3D Egg

References

Contributions

Sanika created a new 3D model for the bacteria, and developed the shaders for the bacteria, egg, and uterus lining.

Ashley updated the sperm to be glowing and created glowing, transparent acid patches surrounding bacteria. She also wrote the webpage reports and coordinated team meetings.

Circle did the bulk of the Unity work, including sperm movement, transformation of the camera view to 3D, adapting existing scripts for 3D. Circle also helped with integration testing, including the 3D model, the various shaders used.

Grant created the 3D model for the uterus in Blender. This model features ridges in appropriate parts of the model and is proportioned to be close to the real organ.