ECOVISION
Platform: Meta Quest 3
Duration: 14 weeks
Tools: Unity, Substance, RenderDoc
Role: Producor, Technical Artist
Teammates: Xun Zhang (Producer, Technical Artist)
Ayaka Morito(Producer, Game Designer)
Yuxuan (Sally) Jin (Artist, Game Designer)
Zihan (Aurora) Liu (Artist, Game Designer)
Chu (Annie) Li (Gameplay Programmer)
Yuxuan (Vector) Liu (Level Designer)
Weekly development blog: EcoVision
EcoVision is a student-developed VR project that shows how small actions can help the environment. In the game, players explore nature, interact with ecosystems, and see how their everyday choices can affect the world around them.
Many people feel that environmental problems are too big to solve alone. EcoVision challenges this idea by letting players experience the power of individual actions through hands-on, immersive gameplay.
Stylized Grass Rendering:
Pre-Blade Grass Rendering:
The main rendering method for grass uses Unity’s DrawProceduralIndirect function, an API Unity provides for instancing that treats each blade of grass as an individual instance for rendering. To generate the final render of the grass, both the vertex data and the instance data required for each blade must be provided.
To obtain the instance data, a compute shader is used to calculate the instance data buffer.
High Level Pipeline:
Source vertices provide spawn points (positions, surface normals).
Compute pass
For each source vertex:
Spawn
N
blades with random placement around the vertex.For each blade, generate a number of segments, assemble a triangle strip, and append triangles to a triangle buffer.
Draw pass
A grass shader reads the appended triangle buffer and renders with Graphics.DrawProceduralIndirect.
Compute Pass:
From a source vertex, we generate multiple random positions for blades to form a grass cluster. For each blade, we also assign a yaw rotation so it has a facing direction around the surface normal.
Each blade gets its own shape:
Height and width are randomized within min/max ranges.
Bend increases with height, and the way that bend accumulates from base to tip is shaped using a curvature control.
We also scale the very bottom slightly using a bottom width modifier to give the grass blade a more natural look.
Now we actually build the blade:
We march from the base to the tip in segments. For each segment:
t = 0…1 measures progress up the blade.
Height grows linearly with t.
Width tapers as we go up (wide at the base, narrow near the tip).
Bend accumulates using the curvature control (so the blade leans forward progressively).
We place two vertices: one to the left and one to the right of the center (current width/2).
After the last segment we add a single tip vertex in the middle, so the blade ends in a point.
With the vertex list in hand:
Slide a 3-vertex window across the list to form triangles.
Append those triangles into a triangle buffer that all blades share.
Draw Pass:
Base Color:
To ensure performance, we use a simple color gradient along the Y-axis. While more complex methods—like dithering transparency at the root or sampling a render texture of the terrain—are possible, we choose the gradient approach for its efficiency.
Specular:
We introduce a tip influence modifier that biases smoothness toward the tip to simulate grass specular highlights.
Lighting:
The grass shader support both realtime lighting and unlit mode.
For VR optimization, we use only unlit mode in the actual project.
World Space Color Variation:
We sample a low-frequency noise texture in world space to achieve a natural look at large scale. This approach can also be used to approximate moving cloud shadows
Motion:
Wind motion is built from two parts in the compute pass:
The first is a local sway effect ("jiggle") that applies movement to each segment, gradient from top to bottom.
The second is a world-space displacement, driven by a slowly scrolling noise texture, which creates broad, cohesive gusts.
To further enhance the effect, we slightly increasing brightness and saturation near the tips in the fragment pass during gusts.
Noise Map for Color Variation
Noise Map for Wind Motion
Frustum Culling:
In the early stage of the compute pass, we partition the scene with a octree and reject grass whose vertices lie outside the camera frustum.
LOD: Segment Count per Blade
We also computes segment count based on camera distance.
Optimization:
Authoring:
Grass Painting Tool for Artists:
To input the source vertices into the compute shader, we built an in-editor grass painting tool and exposed parameters to give artists creative control.
Stylized Tree Rendering and Pipeline:
Tree Assets Creation Pipeline:
This stylized tree is not only a key asset in our game, but also serves as a pipeline guideline exploration for our artists.