Perfect Circle
Did you know that we can use PlaneGeometry to render perfectly rounded circle in Three.js with just 2 triangles?
CircleGeometry
You may know that Three.js provides build-in support for rendering circles using the CircleGeometry class. However, to make such circles perfectly rounded, especially if you plan to support zoom in your scene, you need to increase the number of triangles significantly, which can lead to performance issues in complex scenes.
Here's a basic example of rendering a circle using CircleGeometry with 8 segments (triangles):
import { CircleGeometry, MeshBasicMaterial, Mesh } from 'three' const radius = 5 const segments = 8 // (triangles) const geometry = new CircleGeometry(radius, segments) const material = new MeshBasicMaterial() const mesh = new Mesh(geometry, material) scene.add(mesh)
PlaneGeometry with Custom Shader
To reduce the triangles count while maintaining a perfectly rounded shape, we can use PlaneGeometry which inherently has fewer triangles. By applying a custom shader, we can make the plane appear as a circle.
The idea is to use a fragment shader that calculates the distance from the center of the plane and discard pixels outside a certain radius, effectively creating a circular appearance.
Here's how you can implement this:
import { PlaneGeometry, ShaderMaterial, Mesh } from 'three' const vertexShader = ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); } ` const fragmentShader = ` varying vec2 vUv; void main() { float distanceFromCenter = length(vUv * 2.0 - 1.0); if (distanceFromCenter > 1.0) { discard; } gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // red color } ` const size = 10 const geometry = new PlaneGeometry(size, size) const material = new ShaderMaterial({ vertexShader, fragmentShader, }) const mesh = new Mesh(geometry, material) scene.add(mesh)
Some Considerations
While this method effectively reduces the triangle count and maintains a circular appearance, it's important to not that the PlaneMesh remains a plane. This means that interactions such as mouse events will still be detected across the entire plane, not just the visible circle. You can actually see this behaviour in the demo scene above, where the mouse hover events are handled inside the plane wireframe, not just the circle's appearance.
By leveraging PlaneGeometry and custom shaders, you can create visually appealing circles with improved performance, making this technique a valuable tool in you Three.js toolkit.