Unity 3D Shader
Crystal Shader
Crystal Shader

Overview
Overview
This shader was made in Unity using Universal Render Pipeline and Shader Graph with an HLSL file to implement a procedural Voronoi Generator. It creates an stylized crystal looking shader by using Voronoi cell data as normals.
The shader is built around a custom Voronoi implementation that calculates important data such as feature point positions and distance to this feature points. By using these values with a series of mathemathical operations and coloring using Shader Graph, we can simulate crystal faces on the surface.
It also exposes a high level of modularity, allowing artists to control parameters such as crystal coloration, cell density, edge colorations, and even surface properties as metallic and smoothness.
This shader is best to be used in stylized environments, magical materials and fantasy visual effects where a procedural crystalline appearance is desired instead of a high poly mesh and a custom texture.
This shader was made in Unity using Universal Render Pipeline and Shader Graph with an HLSL file to implement a procedural Voronoi Generator. It creates an stylized crystal looking shader by using Voronoi cell data as normals.
The shader is built around a custom Voronoi implementation that calculates important data such as feature point positions and distance to this feature points. By using these values with a series of mathemathical operations and coloring using Shader Graph, we can simulate crystal faces on the surface.
It also exposes a high level of modularity, allowing artists to control parameters such as crystal coloration, cell density, edge colorations, and even surface properties as metallic and smoothness.
This shader is best to be used in stylized environments, magical materials and fantasy visual effects where a procedural crystalline appearance is desired instead of a high poly mesh and a custom texture.

Voronoi Generation
Voronoi Generation
It is important to remark that the shader does not make use of a previously modeled mesh, and instead, it generates color gradients and normal vectors which interact with lighting to give this crystalline appeareance.
As a first step, we need to understand how a Voronoi pattern is generated. In summary, it works by defining feature points in the space. These feature points segment the space into cells, where each coordinate belongs to the cell of its closest feature point. This is achieved by evaluating the distance between a coordinate and nearby feature points and selecting the smallest distance.
Unity provides a built-in Voronoi node through Shader Graph, but it has several limitations for this use case, as it operates in a 2D domain and only exposes limited data. While this is enough for simple procedural textures, it does not belong to a 3D domain, and does not provide the information required to built our shader.
It is important to remark that the shader does not make use of a previously modeled mesh, and instead, it generates color gradients and normal vectors which interact with lighting to give this crystalline appeareance.
As a first step, we need to understand how a Voronoi pattern is generated. In summary, it works by defining feature points in the space. These feature points segment the space into cells, where each coordinate belongs to the cell of its closest feature point. This is achieved by evaluating the distance between a coordinate and nearby feature points and selecting the smallest distance.
Unity provides a built-in Voronoi node through Shader Graph, but it has several limitations for this use case, as it operates in a 2D domain and only exposes limited data. While this is enough for simple procedural textures, it does not belong to a 3D domain, and does not provide the information required to built our shader.

It is fundamental to note that the Voronoi generation algorithm requires a reference coordinate system in which the space can be subdivided and feature points can be generated consistently. The choice of this reference directly affects how the Voronoi cells are distributed across the surface. For the purposes of this shader, the surface normal vector is used as the initial reference coordinate. Since normals provide a direction for every point on the mesh, they define a continuous space that can be sampled by the Voronoi generator.
It is fundamental to note that the Voronoi generation algorithm requires a reference coordinate system in which the space can be subdivided and feature points can be generated consistently. The choice of this reference directly affects how the Voronoi cells are distributed across the surface. For the purposes of this shader, the surface normal vector is used as the initial reference coordinate. Since normals provide a direction for every point on the mesh, they define a continuous space that can be sampled by the Voronoi generator.

The Voronoi generation process begins by subdividing the space to a scale parameter. This creates a grid of reference cells that serves as the foundation for the algorithm. A hash function is then applied to the coordinates of each reference cell, generating a unique feature point positioned inside of it. As a consequence, if the Voronoi scale increases, the space is divided into a greater number but smaller reference cells, resulting in a higher density of feature points. Decreasing the scale will generate bigger but fewer refference cells, and therefore fewer feature points.
Once the feature points have been generated, the algorithm evaluates each coordinate independently. Using the coordinate's corresponding reference cell as a starting point, the surrounding cells are searched to locate the nearest feature point. The distance between the coordinate and every candidate feature point is calculated, and the closest one is selected. This process provides two key outputs for every coordinate: the distance to the nearest feature point and the position of that feature point itself. We can use the distance to the closest feature point and previsualize it as a color in our material.
The Voronoi generation process begins by subdividing the space to a scale parameter. This creates a grid of reference cells that serves as the foundation for the algorithm. A hash function is then applied to the coordinates of each reference cell, generating a unique feature point positioned inside of it. As a consequence, if the Voronoi scale increases, the space is divided into a greater number but smaller reference cells, resulting in a higher density of feature points. Decreasing the scale will generate bigger but fewer refference cells, and therefore fewer feature points.
Once the feature points have been generated, the algorithm evaluates each coordinate independently. Using the coordinate's corresponding reference cell as a starting point, the surrounding cells are searched to locate the nearest feature point. The distance between the coordinate and every candidate feature point is calculated, and the closest one is selected. This process provides two key outputs for every coordinate: the distance to the nearest feature point and the position of that feature point itself. We can use the distance to the closest feature point and previsualize it as a color in our material.
As established previously, every coordinate belongs to a single Voronoi cell, and each Voronoi cell is associated with a unique feature point. This allows us to treat the feature point position as a source of directional information for all coordinates within the same cell. By using this feature point position as a normal vector, we effectively create groups of pixels that share the same surface orientation. In other words, each Voronoi cell behaves as a flat surface, similar to a facet on a crystal.
Once these generated normals are supplied to Unity's Lit shading model, the engine performs its lighting calculations instead of the mesh's original smooth normals. As a result, lighting transitions happen at Voronoi cell boundaries, producing sharp highlights and distinct light reflections characteristic of crystalline materials. We can change some smoothness and metallic properties to have a good preview of how the lighting behaves when interacting with our new shaded object normals.
As established previously, every coordinate belongs to a single Voronoi cell, and each Voronoi cell is associated with a unique feature point. This allows us to treat the feature point position as a source of directional information for all coordinates within the same cell. By using this feature point position as a normal vector, we effectively create groups of pixels that share the same surface orientation. In other words, each Voronoi cell behaves as a flat surface, similar to a facet on a crystal.
Once these generated normals are supplied to Unity's Lit shading model, the engine performs its lighting calculations instead of the mesh's original smooth normals. As a result, lighting transitions happen at Voronoi cell boundaries, producing sharp highlights and distinct light reflections characteristic of crystalline materials. We can change some smoothness and metallic properties to have a good preview of how the lighting behaves when interacting with our new shaded object normals.

Using Voronoi Information
Using Voronoi Information
Now that we have both the Voronoi distance field and the generated facet normals, we can combine them to build the preliminar crystal appearance. By using the distance field as a mask, we can assign different colors to the interior of the cells and to their edges, enhancing the perception of a defined geometric structure.
To further emphasize the edges, the distance field gradient can be modified using power and multiplication operations. Together, these adjustments produce sharper and more visible edge highlights, allowing the crystal facets to stand out more clearly. The resulting combination of faceted lighting and accentuated cell borders significantly strengthens the crystalline appearance of the material.
Now that we have both the Voronoi distance field and the generated facet normals, we can combine them to build the preliminar crystal appearance. By using the distance field as a mask, we can assign different colors to the interior of the cells and to their edges, enhancing the perception of a defined geometric structure.
To further emphasize the edges, the distance field gradient can be modified using power and multiplication operations. Together, these adjustments produce sharper and more visible edge highlights, allowing the crystal facets to stand out more clearly. The resulting combination of faceted lighting and accentuated cell borders significantly strengthens the crystalline appearance of the material.
Refference Coordinates Fix
Refference Coordinates Fix
While using surface normals as reference coordinates works well for many meshes, it introduces limitations on surfaces where the normal direction varies very little across large areas. Since the Voronoi generation depends entirely on the chosen reference coordinates, regions with nearly identical normals will produce very little variation in the generated Voronoi space. As a result, cells become stretched, elongated, or significantly larger than intended.
The most extreme example of this issue is a simple quad. Because every vertex and pixel on a flat quad shares the same normal direction, all coordinates map to the same location in the Voronoi reference space. Consequently, the generator is unable to create multiple distinct cells, causing the entire surface to belong to a single Voronoi cell with a single feature point.
While using surface normals as reference coordinates works well for many meshes, it introduces limitations on surfaces where the normal direction varies very little across large areas. Since the Voronoi generation depends entirely on the chosen reference coordinates, regions with nearly identical normals will produce very little variation in the generated Voronoi space. As a result, cells become stretched, elongated, or significantly larger than intended.
The most extreme example of this issue is a simple quad. Because every vertex and pixel on a flat quad shares the same normal direction, all coordinates map to the same location in the Voronoi reference space. Consequently, the generator is unable to create multiple distinct cells, causing the entire surface to belong to a single Voronoi cell with a single feature point.
This limitation can be addressed by modifying the reference coordinates used by the Voronoi generator. Instead of relying exclusively on the surface normal, the shader combines the surface normal with the Object Space Position. By introducing positional information into the Voronoi domain, coordinates can now have a greater difference from one another, allowing the generator to produce a more evenly distributed cell structure.
To provide additional artistic control, the contribution of both the normal vector and the Object Space Position can be adjusted through independent influence parameters. By selecting an appropriate balance between these two coordinate sources, the Voronoi cells maintain a consistent appearance even on meshes with large flat surfaces or limited normal variation.
This limitation can be addressed by modifying the reference coordinates used by the Voronoi generator. Instead of relying exclusively on the surface normal, the shader combines the surface normal with the Object Space Position. By introducing positional information into the Voronoi domain, coordinates can now have a greater difference from one another, allowing the generator to produce a more evenly distributed cell structure.
To provide additional artistic control, the contribution of both the normal vector and the Object Space Position can be adjusted through independent influence parameters. By selecting an appropriate balance between these two coordinate sources, the Voronoi cells maintain a consistent appearance even on meshes with large flat surfaces or limited normal variation.
Final Additions
Final Additions
Now that the structure of the crystal has been established, the next step is refining its visual appearance through color variation. Rather than assigning a single color to the interior of each cell, a noise function can be used to blend between two different cell colors. This interpolation creates a richer and more natural distribution of color tones.
To further enhance the stylized appearance, a Fresnel effect can be added, matching the crystal edge color. This highlights the silhouette of the object and gives the impression of a soft magical glow, reinforcing the crystalline look and helping the object stand out within the scene.
Now that the structure of the crystal has been established, the next step is refining its visual appearance through color variation. Rather than assigning a single color to the interior of each cell, a noise function can be used to blend between two different cell colors. This interpolation creates a richer and more natural distribution of color tones.
To further enhance the stylized appearance, a Fresnel effect can be added, matching the crystal edge color. This highlights the silhouette of the object and gives the impression of a soft magical glow, reinforcing the crystalline look and helping the object stand out within the scene.
By simply adjusting color combination and a the exposed parameters, the same shader can produce a wide variety of visual styles. Bright green tones can create the appearance of a healing object, while darker colors with saturated highlights can transform the material into ancient gemstones or powerful mystical artifacts. This allows a single procedural framework to support many different artistic directions.
By simply adjusting color combination and a the exposed parameters, the same shader can produce a wide variety of visual styles. Bright green tones can create the appearance of a healing object, while darker colors with saturated highlights can transform the material into ancient gemstones or powerful mystical artifacts. This allows a single procedural framework to support many different artistic directions.










