Editor’s Note: We’re excited to welcome Tomoyuki Nakata, a Creative Developer at baqemono, as a contributor to our Three.js special feature leading up to the inaugural Three.js conference in Paris. He’s showcasing this fascinating mouse-following lens effect crafted using Three.js and GLSL. Join us in this captivating journey!
🇫🇷 Still on the fence about attending? Don’t miss your chance to grab a ticket to the first-ever Three.js conference in Paris! Use code CODROPS for 15% off and secure your spot before they run out →
Concept Overview
Imagine merging a black-and-white image with a full-color one, creating an effect where a square area nimbly follows your mouse cursor. In this tutorial, we'll tackle the development of just such an effect using Three.js and GLSL.
The core involves layering two images—the grayscale one serves as the backdrop, while the color image is displayed only where the mouse pointer directs this square. To enhance the interaction, we’ll introduce lens distortion and radial RGB shifts within the square area, all while giving the grayscale layer a dynamic wave and noise texture effect.
Despite appearances, this visually rich effect is constructed from fundamental components. We will fashion a square mask that retains its proportions across various screen sizes, apply both the lens and RGB shifting effects in the fragment shader, and enable smooth animation as the square tracks the mouse seamlessly. Additionally, a graphical user interface (GUI) will allow for real-time adjustments to parameters.
The inspiration for this project sprang from a Pinterest design showcasing a similarly styled effect—where a grayscale image's colored section appeared to be cut out by a lens effect square. You can view the original inspiration here. I envisioned that implementing it with WebGL could yield even more fascinating outcomes, particularly with mouse interactivity and unique grayscale effects. Thus, the idea for our interactive lens effect was born.
Elements of the Final Effect
The completed effect comprises several key components:
- A full-screen grayscale image.
- A color image displayed solely within the square's confines.
- A lens distortion effect reminiscent of a CC Lens that bulges outward from the square's center.
- A pronounced RGB shift that escalates towards the square's perimeter.
- A dynamically resizing square mask that retains its shape regardless of screen dimensions.
- Smooth mouse tracking with a slight delay for a more fluid interaction.
- Wave patterns and random distortions applied to the grayscale image for added depth.
- A GUI that supports real-time parameter adjustments.
While the end result may appear intricate, there are no extensive post-processing techniques or complicated 3D models involved; instead, we achieve the entire effect through a fragment shader by harmonizing the listed components.
Organizing the Project Files
The project structure for this tutorial is neatly organized as follows:
src/
└── scripts/
├── webgl/
│ ├── glsl/
│ │ ├── chunks/
│ │ │ ├── ccLens.glsl
│ │ │ ├── coverUv.glsl
│ │ │ └── random3.glsl
│ │ ├── frag/
│ │ │ └── frag.glsl
│ │ └── vert/
│ │ └── vert.glsl
│ ├── mesh/
│ │ └── Mesh.ts
│ ├── stage/
│ │ └── Stage.ts
│ └── Webgl.ts
└── index.ts
glsl: Contains reusable functions, withchunksfor shader utilities,fragfor fragment shaders, andvertfor vertex shaders.mesh(Mesh): Responsible for window resizing, loading textures, creating meshes, sizing them correctly, and updating uniforms.stage(Stage): Manages the scene setup, viewport sizing, camera creation, and rendering updates.Webgl: Initializes theMeshandStageclasses while connecting the GUI and handling animation loops.
This class structure is quite straightforward, allowing us to direct our focus primarily on the implementation within the fragment shader.