跪拜 Guibai
← All articles
Frontend

A Three.js Tribute to Viral Zero-Budget Film 'Niu Lai'

By 李剑一 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

The demo is a compact, self-contained example of several real-time 3D techniques that trip up web developers: instanced rendering for large object counts, frame-rate-independent camera smoothing, procedural texture generation on a canvas, and proper GPU resource disposal. The full source code is provided, making it a practical reference for anyone building a third-person browser experience with Three.js.

Summary

A developer built a browser-based 3D tribute to the viral Chinese film *Niu Lai*, a movie made by a two-person crew that went from a 342-yuan opening day to a trending topic and 168 nationwide screenings. The demo loads a custom 3D character model into a Three.js scene with a procedural dirt ground texture and instanced grass that spawns dynamically as the player moves. A third-person camera follows the character with a frame-rate-independent smoothing coefficient, while keyboard input drives movement, jumping with gravity integration, and a crouch mechanic that scales the character model around its feet. The entire interaction loop is encapsulated in a `GameWorld` class that manages the renderer, scene, input state, and cleanup to prevent memory leaks.

Takeaways
An `InstancedMesh` renders thousands of grass blades as a single draw call by merging three crossed `PlaneGeometry` planes into one geometry per blade.
The camera follow uses a coefficient `a = 1 - 0.0015^dt` so the smoothing feels consistent regardless of frame rate fluctuations.
A procedural ground texture is generated on a 512×512 canvas with 2,600 semi-transparent dirt patches and 400 grass dots, then tiled 40×40 times across the ground plane.
Jump physics apply gravity as a uniform acceleration integral, resetting vertical velocity and position when `jumpY` reaches or crosses zero.
Crouching scales the character pivot's Y-axis to 0.55 with a lerp toward the target, keeping the feet planted by placing the pivot origin at ground level.
Grass spawns dynamically: accumulated movement distance triggers a new blade placement near the character, up to a maximum instance count.
The `GameWorld` class disposes the renderer, removes event listeners, and clears the scene on unmount to avoid GPU memory leaks.
Conclusions

Using `frustumCulled = false` on the grass `InstancedMesh` is a necessary hack; otherwise the entire grass field can vanish when its bounding sphere leaves the camera frustum, even if individual blades are still visible.

The pixel ratio is capped at 2 to prevent high-DPI devices from tanking performance, a small detail that many WebGL tutorials omit but which matters on modern 4K and mobile screens.

Generating the ground texture procedurally on a canvas avoids an external image dependency and keeps the demo self-contained, but the 2,600-iteration loop per texture creation is a one-time cost that could be cached in a real application.

Concepts & terms
InstancedMesh
A Three.js class that renders many copies of the same geometry with different transformations in a single GPU draw call, drastically reducing CPU overhead for repeated objects like grass or trees.
PCFSoftShadowMap
A shadow map type in Three.js that applies Percentage-Closer Filtering to produce soft-edged shadows, avoiding the hard, aliased edges of basic shadow maps.
Anisotropic filtering
A texture sampling technique that improves clarity when a surface is viewed at a steep angle, such as looking across a ground plane; it prevents the texture from blurring in the distance.
Frustum culling
An optimization where objects outside the camera's view are skipped during rendering. It can backfire on large instanced meshes, which is why it's sometimes disabled for objects like grass fields.
sRGB color space
A standard color space that matches typical monitor gamma. Three.js requires setting `outputColorSpace` to `SRGBColorSpace` so textures and lighting appear with correct brightness rather than looking washed out.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗