
24K Gold, Lavender Pearls, and inked custom materials in your 3D plots
Blender, ZBrush, SculptGL, or other programs are the way to go if you want to create sophisticated 3D figures for a presentation, paper, or blog post. However, if you already generate your shapes programmatically or derive them from external data sources, it is convenient to render them using the same set of tools.
WLJS Notebook uses THREE.js on the frontend to render 3D graphics, such as this example:
Graphics3D[{Sphere[]}] (*VB[*)(Graphics3D[{Sphere[{0, 0, 0}]}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeF5BwL0osyMhMLjZ2QajwySwugfDYgERwQUZqUWoaM7JsJpBmQBAAo2ERrg=="*)(*]VB*) This makes the pipeline quite customizable. To access the frontend realm, you only need to define a Wolfram Language symbol there, since the two worlds are tightly interconnected.
.js
core.Pearl = (args, env) => {
alert('I am Pearl');
} Now, if you use the symbol Pearl as a graphics primitive or directive in a Graphics3D expression, it will display the message I am Pearl:
Graphics3D[{Pearl[], Sphere[]}] You can also access the shared variables of the 3D scene through env. In the simplest case, you can directly modify some of them as follows:
.js
core.Pearl = (args, env) => {
env.opacity = 0.1;
} Graphics3D[{Pearl[], Sphere[]}] (*VB[*)(Graphics3D[{Opacity[0.1], Sphere[{0, 0, 0}]}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeF5BwL0osyMhMLjZ2SWOCqfDJLC6BqGAHEv4FicmZJZVFs2aCwE57iAwbkAguyEgtSk1jRtaXCaQZEAQAlPwaag=="*)(*]VB*) To modify the color and other properties, you need access to THREE.js classes and their objects. Most heavy libraries are shared through the interpretate.shared interface.
.js
let THREE;
core.Pearl = async (args, env) => {
if (!THREE) {
await interpretate.shared.THREE.load()
THREE = interpretate.shared.THREE.THREE;
}
env.color = new THREE.Color('gold');
} Graphics3D[{Pearl[], Sphere[]}] (*VB[*)(Graphics3D[{Pearl[], Sphere[{0, 0, 0}]}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeF5BwL0osyMhMLjZ2SWOCqfDJLC5JYwDxWIFEQGpiUQ5EAxuQCC7ISC1KTWNGVpwJUowgANMvFIE="*)(*]VB*) Mesh materials
In the same way, we can supply custom shaders to use as materials for our objects. The Wolfram Language does not have an API for this, so WLJS adds one:
MeshMaterial // Context %22CoffeeLiqueur%60Extensions%60Graphics3D%60%22 There are already a few built-in materials that react to lighting differently:
Graphics3D[{
PointLight[Red, {-2,0,5}],
PointLight[Green, 3{2,0,5}],
MeshMaterial[MeshToonMaterial[]], Sphere[{0,-2,0}],
MeshMaterial[MeshPhysicalMaterial[]], Sphere[{0,2,0}]
}, Lighting->None] (*VB[*)(Graphics3D[{PointLight[RGBColor[1, 0, 0], {-2, 0, 5}], PointLight[RGBColor[0, 1, 0], {6, 0, 15}], MeshMaterial[MeshToonMaterial[]], Sphere[{0, -2, 0}], MeshMaterial[MeshPhysicalMaterial[]], Sphere[{0, 2, 0}]}, Lighting -> None])(*,*)(*"1:eJylUcsKwjAQbOsD8eI3+AeCHjz7QA+tSPUDWuvGLISsJinoz1ubSFsQBIuXgezMzs5mxyeKme95nu6WsCVxZoF9DUvYqPTKMdPTFetXihC1aRR7QmlCvHDDOrY2KCHeLJYkSKF1Ra+Ct6CywGdRFG+yZ8nfHJ3e/+LYr8eNLOl2mpSwJMYAQrzlkKtkfTcgNZLUSbNeEoHmUWpAYSqY9dCzFp1HIll3u7k2y+HKQcFHSJev3v3fkPMWnXv+0Jilok3QoP7qoCLjXIA7irsRyour7kjCC49nmL4="*)(*]VB*) To write your own, you need to know GLSL, the language used for GPU data processing. Everything you need is in the THREE.ShaderMaterial API:
flowchart TB
%% Styling
classDef shader fill:#4c1d95,stroke:#c084fc,stroke-width:2px,color:#f8fafc
classDef gpu fill:#7c2d12,stroke:#fb923c,stroke-width:2px,color:#f8fafc
subgraph CPU ["1. Three.js (CPU)"]
Mat["<b>ShaderMaterial</b><br/>• uniforms (global)<br/>• attributes (normals, colors etc)<br/>• vertex shader program<br/>• fragment shader program "]:::js
end
subgraph GPU ["2. GPU Pipeline"]
direction LR
VS["<b>Vertex Shader</b><br/>Positions each vertex<br/><i>Output: gl_Position</i>"]:::shader
Rast["<b>Rasterizer</b><br/>Turns triangles into pixels<br/>& interpolates <code>varying</code> data"]:::gpu
FS["<b>Fragment Shader</b><br/>Colors each pixel<br/><i>Output: gl_FragColor</i>"]:::shader
Screen["<b>Screen</b>"]:::gpu
end
Mat -->|"Attributes"| VS
Mat -->|"Uniforms"| FS
VS -->Rast
Rast --> FS
FS --> Screen
Note that MeshMaterial expects the inner expression to return a material factory. Let's try the simplest one, using only a fragment shader:
.js%0Alet%20THREE%3B%0A%0Acore.PearlMaterial1%20%3D%20async%20%28args%2C%20env%29%20%3D%3E%20%7B%0A%20%20if%20%28%21THREE%29%20%7B%0A%20%20%20%20await%20interpretate.shared.THREE.load%28%29%0A%20%20%20%20THREE%20%3D%20interpretate.shared.THREE.THREE%3B%0A%20%20%7D%0A%0A%20%20return%20function%28%29%20%7B%0A%20%20%20%20return%20new%20THREE.ShaderMaterial%28%7B%0A%20%20%20%20%20%20%20%20name%3A%20%27PearlMaterial%20%2F%20Test%201%27%2C%0A%20%20%20%20%20%20%20%20uniforms%3A%20%7B%7D%2C%0A%20%20%20%20%20%20%20%20fragmentShader%3A%20%60%0A%20%20%20%20%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20gl_FragColor%20%3D%20vec4%281.0%2C%200.77%2C%200.34%2C%201.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%60%2C%0A%20%20%20%20%20%20%20%20lights%3A%20false%2C%0A%20%20%20%20%20%20%20%20toneMapped%3A%20false%0A%20%20%20%20%20%20%7D%29%3B%20%20%20%20%0A%20%20%7D%0A%7D Graphics3D[{MeshMaterial[PearlMaterial1[]], Sphere[]}] (*VB[*)(Graphics3D[{MeshMaterial[PearlMaterial1[]], Sphere[{0, 0, 0}]}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeF5BwL0osyMhMLjZ2SWOCqfDJLC6BqDAAEs75aWmpqT6ZhaWppUUJrhUlqXnFmfl5xQkIrQm+qcUZvoklqUWZiTlpDCCdfEAiIDWxKAcmbAgxkQ1IBBdkpBalpjEj25cJ0oUgAM1NK9c="*)(*]VB*) Without a vertex shader, we don't know much about the camera position or viewpoint. However, if we define vertexShader, we also need to add boilerplate code that determines where the points of our mesh should be placed. This is typical in 3D applications:
We do this by calculating where the points are in the scene by multiplying the position of the mesh in the scene (modelViewMatrix) and the position of the point. After that we multiply this value with the camera's relation to the scene (projectionMatrix) so the camera settings within THREE.js are respected by our shader. The gl_Position is the value that the GPU takes to draw our points. https://dev.to/maniflames/creating-a-custom-shader-in-threejs-3bhi
Let's pass the data from the vertex shader to the fragment shader to create basic lighting:
.js%0Alet%20THREE%3B%0A%0Acore.PearlMaterial2%20%3D%20async%20%28args%2C%20env%29%20%3D%3E%20%7B%0A%20%20if%20%28%21THREE%29%20%7B%0A%20%20%20%20await%20interpretate.shared.THREE.load%28%29%0A%20%20%20%20THREE%20%3D%20interpretate.shared.THREE.THREE%3B%0A%20%20%7D%0A%0A%20%20return%20function%28%29%20%7B%0A%20%20%20%20return%20new%20THREE.ShaderMaterial%28%7B%0A%20%20%20%20%20%20%20%20name%3A%20%27PearlMaterial%20%2F%20Test%202%27%2C%0A%20%20%20%20%20%20%20%20uniforms%3A%20%7B%7D%2C%0A%20%20%20%20%20%20%20%20vertexShader%3A%20%60%0A%20%20%20%20%20%20%20%20%20%20varying%20vec3%20vPaperNormal%3B%20%2F%2Fnew%21%0A%20%20%20%20%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20vPaperNormal%20%3D%20normalize%28normalMatrix%20%2A%20normal%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20gl_Position%20%3D%20projectionMatrix%20%2A%20modelViewMatrix%20%2A%20vec4%28position%2C%201.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%60%2C%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20fragmentShader%3A%20%60%0A%20%20%20%20%20%20%20%20%20%20varying%20vec3%20vPaperNormal%3B%0A%20%20%20%20%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20vec3%20n%20%3D%20normalize%28vPaperNormal%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20gl_FragColor%20%3D%20vec4%28vec3%281.0%2C%200.77%2C%200.34%29%2A%28clamp%28dot%28n%2C%20vec3%281.0%2C1.0%2C1.0%29%29%2C%200.0%2C0.75%29%20%2B%200.25%29%2C%201.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%60%2C%0A%20%20%20%20%20%20%20%20lights%3A%20false%2C%0A%20%20%20%20%20%20%20%20toneMapped%3A%20false%0A%20%20%20%20%20%20%7D%29%3B%20%20%20%20%0A%20%20%7D%0A%7D Graphics3D[{MeshMaterial[PearlMaterial2[]], Sphere[]}] (*VB[*)(Graphics3D[{MeshMaterial[PearlMaterial2[]], Sphere[{0, 0, 0}]}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeF5BwL0osyMhMLjZ2SWOCqfDJLC6BqDAAEs75aWmpqT6ZhaWppUUJrhUlqXnFmfl5xQkIrQm+qcUZvoklqUWZiTlpDCCdfEAiIDWxKAcmbAQxkQ1IBBdkpBalpjEj25cJ0oUgAM17K9g="*)(*]VB*) Here, we normalize vPaperNormal because the fragment shader linearly interpolates it between pixel coordinates. This interpolation breaks the normalization.
To make it look like gold, we can add a tinted specular highlight (which makes metal look metallic). The vertex shader stays as is, while the fragment shader also uses the half-vector between the light and viewing directions. The normal is in view space, so the viewing direction is simply (0, 0, 1). Because the highlight is multiplied by the gold color, it remains gold-tinted rather than white, which is how real metals behave.
.js%0Alet%20THREE%3B%0A%0Acore.PearlMaterial3%20%3D%20async%20%28args%2C%20env%29%20%3D%3E%20%7B%0A%20%20if%20%28%21THREE%29%20%7B%0A%20%20%20%20await%20interpretate.shared.THREE.load%28%29%0A%20%20%20%20THREE%20%3D%20interpretate.shared.THREE.THREE%3B%0A%20%20%7D%0A%0A%20%20return%20function%28%29%20%7B%0A%20%20%20%20return%20new%20THREE.ShaderMaterial%28%7B%0A%20%20%20%20%20%20%20%20name%3A%20%27PearlMaterial%20%2F%20Test%203%27%2C%0A%20%20%20%20%20%20%20%20uniforms%3A%20%7B%7D%2C%0A%20%20%20%20%20%20%20%20vertexShader%3A%20%60%0A%20%20%20%20%20%20%20%20%20%20varying%20vec3%20vPaperNormal%3B%20%2F%2Fnew%21%0A%20%20%20%20%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20vPaperNormal%20%3D%20normalize%28normalMatrix%20%2A%20normal%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20gl_Position%20%3D%20projectionMatrix%20%2A%20modelViewMatrix%20%2A%20vec4%28position%2C%201.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%60%2C%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20fragmentShader%3A%20%60%0A%20%20%20%20%20%20%20%20%20%20varying%20vec3%20vPaperNormal%3B%0A%20%20%20%20%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20vec3%20n%20%3D%20normalize%28vPaperNormal%29%2C%20l%20%3D%20normalize%28vec3%281.0%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20diff%20%3D%20clamp%28dot%28n%2C%20l%29%2C%200.0%2C%200.75%29%20%2B%200.25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20spec%20%3D%20pow%28max%28dot%28n%2C%20normalize%28l%20%2B%20vec3%280.0%2C%200.0%2C%201.0%29%29%29%2C%200.0%29%2C%2040.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20gl_FragColor%20%3D%20vec4%28vec3%281.0%2C%200.77%2C%200.34%29%20%2A%20%28diff%20%2B%20spec%20%2A%201.5%29%2C%201.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%60%2C%0A%20%20%20%20%20%20%20%20lights%3A%20false%2C%0A%20%20%20%20%20%20%20%20toneMapped%3A%20false%0A%20%20%20%20%20%20%7D%29%3B%20%20%20%20%0A%20%20%7D%0A%7D Graphics3D[{MeshMaterial[PearlMaterial3[]], Sphere[]}] (*VB[*)(Graphics3D[{MeshMaterial[PearlMaterial3[]], Sphere[{0, 0, 0}]}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeF5BwL0osyMhMLjZ2SWOCqfDJLC6BqDAAEs75aWmpqT6ZhaWppUUJrhUlqXnFmfl5xQkIrQm+qcUZvoklqUWZiTlpDCCdfEAiIDWxKAcmbAwxkQ1IBBdkpBalpjEj25cJ0oUgAM2pK9k="*)(*]VB*) Neat!
HandInked Materials
By exploring these ideas and adding a noisy texture on top, I prepared a set of mesh materials for Graphics3D that render surfaces as if they were hand-inked with softly glazed pigment: a flat key/fill shading model with paper-fleck grain, a soft sheen, and darkened edges.
It includes a named palette (Gold, Lavender, Teal, Coral, etc.) plus custom colors and two special finishes: Ice (pearlescent, with grain adapted to atom size) and Glass (translucent, frosted sky-blue). To use it in your plot, copy and paste this shader code into your notebook:
.js%0Aconst%20paperPalette%20%3D%20%7B%0A%20%20Gold%3A%20%27%23e8bd81%27%2C%0A%20%20Oxygen%3A%20%27%23dcaa5b%27%2C%0A%20%20Lavender%3A%20%27%23a47bd5%27%2C%0A%20%20Coral%3A%20%27%23f04b37%27%2C%0A%20%20Red%3A%20%27%23ed382a%27%2C%0A%20%20Teal%3A%20%27%23239bb8%27%2C%0A%20%20Cyan%3A%20%27%2338bedb%27%2C%0A%20%20Blue%3A%20%27%231652bb%27%2C%0A%20%20Green%3A%20%27%23168c3c%27%0A%7D%3B%0Aconst%20paperReady%20%3D%20interpretate.shared.THREE.load%28%29.then%28%28%29%20%3D%3E%20interpretate.shared.THREE.THREE%29%3B%0A%0A%2F%2F%20Ice-only%20raster%20material.%20%0Afunction%20createPaperIceRasterMaterial%28THREE%2C%20options%20%3D%20%7B%7D%29%20%7B%0A%20%20const%20roughness%20%3D%20Math.min%281%2C%20Math.max%280%2C%20options.roughness%20%3F%3F%200.42%29%29%3B%0A%20%20const%20grain%20%3D%20Math.min%280.1%2C%20Math.max%280%2C%20options.grain%20%3F%3F%200.014%29%29%3B%0A%20%20const%20opacity%20%3D%20Math.min%281%2C%20Math.max%280%2C%20options.opacity%20%3F%3F%201%29%29%3B%0A%20%20const%20outputChunk%20%3D%20THREE.ShaderChunk.colorspace_fragment%0A%20%20%20%20%3F%20%27colorspace_fragment%27%20%3A%20%27encodings_fragment%27%3B%0A%20%20const%20material%20%3D%20new%20THREE.ShaderMaterial%28%7B%0A%20%20%20%20name%3A%20%27Pearl%20%2F%20Ice%27%2C%0A%20%20%20%20uniforms%3A%20%7B%0A%20%20%20%20%20%20baseColor%3A%20%7B%20value%3A%20new%20THREE.Color%28%27%23cee6ef%27%29%20%7D%2C%0A%20%20%20%20%20%20pearlRoughness%3A%20%7B%20value%3A%20roughness%20%7D%2C%0A%20%20%20%20%20%20pearlGrain%3A%20%7B%20value%3A%20grain%20%7D%2C%0A%20%20%20%20%20%20pearlOpacity%3A%20%7B%20value%3A%20opacity%20%7D%2C%0A%20%20%20%20%20%20pearlHighlight%3A%20%7B%20value%3A%20options.highlight%20%3F%3F%201.0%20%7D%2C%0A%20%20%20%20%20%20pearlWash%3A%20%7B%20value%3A%20options.wash%20%3F%3F%200.10%20%7D%2C%0A%20%20%20%20%20%20keyDirection%3A%20%7B%20value%3A%20new%20THREE.Vector3%28-0.65%2C%200.85%2C%201.25%29.normalize%28%29%20%7D%2C%0A%20%20%20%20%20%20fillDirection%3A%20%7B%20value%3A%20new%20THREE.Vector3%280.8%2C%20-0.2%2C%200.6%29.normalize%28%29%20%7D%0A%20%20%20%20%7D%2C%0A%20%20%20%20vertexShader%3A%20%60%0A%20%20%20%20%20%20varying%20vec3%20pearlNormal%3B%0A%20%20%20%20%20%20varying%20vec3%20pearlViewPosition%3B%0A%20%20%20%20%20%20varying%20vec3%20pearlLocalPosition%3B%0A%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%23include%20%3Cbeginnormal_vertex%3E%0A%20%20%20%20%20%20%20%20%23include%20%3Cdefaultnormal_vertex%3E%0A%20%20%20%20%20%20%20%20pearlNormal%20%3D%20normalize%28transformedNormal%29%3B%0A%20%20%20%20%20%20%20%20pearlLocalPosition%20%3D%20position%3B%0A%20%20%20%20%20%20%20%20%23include%20%3Cbegin_vertex%3E%0A%20%20%20%20%20%20%20%20%23include%20%3Cproject_vertex%3E%0A%20%20%20%20%20%20%20%20pearlViewPosition%20%3D%20-mvPosition.xyz%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%60%2C%0A%20%20%20%20fragmentShader%3A%20%60%0A%20%20%20%20%20%20uniform%20vec3%20baseColor%3B%0A%20%20%20%20%20%20uniform%20vec3%20keyDirection%3B%0A%20%20%20%20%20%20uniform%20vec3%20fillDirection%3B%0A%20%20%20%20%20%20uniform%20float%20pearlRoughness%3B%0A%20%20%20%20%20%20uniform%20float%20pearlGrain%3B%0A%20%20%20%20%20%20uniform%20float%20pearlOpacity%3B%0A%20%20%20%20%20%20uniform%20float%20pearlHighlight%3B%0A%20%20%20%20%20%20uniform%20float%20pearlWash%3B%0A%20%20%20%20%20%20varying%20vec3%20pearlNormal%3B%0A%20%20%20%20%20%20varying%20vec3%20pearlViewPosition%3B%0A%20%20%20%20%20%20varying%20vec3%20pearlLocalPosition%3B%0A%20%20%20%20%20%20float%20grainHash%28vec3%20p%29%20%7B%0A%20%20%20%20%20%20%20%20p%20%3D%20fract%28p%20%2A%200.1031%29%3B%0A%20%20%20%20%20%20%20%20p%20%2B%3D%20dot%28p%2C%20p.yzx%20%2B%2033.33%29%3B%0A%20%20%20%20%20%20%20%20return%20fract%28%28p.x%20%2B%20p.y%29%20%2A%20p.z%29%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20float%20grainNoise%28vec3%20p%29%20%7B%0A%20%20%20%20%20%20%20%20vec3%20cell%20%3D%20floor%28p%29%3B%0A%20%20%20%20%20%20%20%20vec3%20f%20%3D%20fract%28p%29%3B%0A%20%20%20%20%20%20%20%20f%20%3D%20f%20%2A%20f%20%2A%20%283.0%20-%202.0%20%2A%20f%29%3B%0A%20%20%20%20%20%20%20%20return%20mix%28%0A%20%20%20%20%20%20%20%20%20%20mix%28mix%28grainHash%28cell%29%2C%20grainHash%28cell%20%2B%20vec3%281%2C0%2C0%29%29%2C%20f.x%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20mix%28grainHash%28cell%20%2B%20vec3%280%2C1%2C0%29%29%2C%20grainHash%28cell%20%2B%20vec3%281%2C1%2C0%29%29%2C%20f.x%29%2C%20f.y%29%2C%0A%20%20%20%20%20%20%20%20%20%20mix%28mix%28grainHash%28cell%20%2B%20vec3%280%2C0%2C1%29%29%2C%20grainHash%28cell%20%2B%20vec3%281%2C0%2C1%29%29%2C%20f.x%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20mix%28grainHash%28cell%20%2B%20vec3%280%2C1%2C1%29%29%2C%20grainHash%28cell%20%2B%20vec3%281%2C1%2C1%29%29%2C%20f.x%29%2C%20f.y%29%2C%20f.z%29%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20vec3%20N%20%3D%20normalize%28pearlNormal%29%20%2A%20%28gl_FrontFacing%20%3F%201.0%20%3A%20-1.0%29%3B%0A%20%20%20%20%20%20%20%20vec3%20V%20%3D%20isOrthographic%20%3F%20vec3%280.0%2C%200.0%2C%201.0%29%20%3A%20normalize%28pearlViewPosition%29%3B%0A%20%20%20%20%20%20%20%20vec3%20H%20%3D%20normalize%28keyDirection%20%2B%20V%29%3B%0A%20%20%20%20%20%20%20%20float%20key%20%3D%20pow%28clamp%28dot%28N%2C%20keyDirection%29%20%2A%200.5%20%2B%200.5%2C%200.0%2C%201.0%29%2C%201.3%29%3B%0A%20%20%20%20%20%20%20%20float%20fill%20%3D%20max%28dot%28N%2C%20fillDirection%29%2C%200.0%29%3B%0A%20%20%20%20%20%20%20%20float%20facing%20%3D%20max%28dot%28N%2C%20V%29%2C%200.0%29%3B%0A%20%20%20%20%20%20%20%20float%20edge%20%3D%20pow%281.0%20-%20facing%2C%202.0%29%3B%0A%20%20%20%20%20%20%20%20float%20wash%20%3D%20grainNoise%28pearlLocalPosition%20%2A%202.8%20%2B%20vec3%2811.7%2C%204.2%2C%208.1%29%29%3B%0A%20%20%20%20%20%20%20%20float%20broad%20%3D%20pow%28max%28dot%28N%2C%20H%29%2C%200.0%29%2C%20mix%2816.0%2C%204.0%2C%20pearlRoughness%29%29%3B%0A%20%20%20%20%20%20%20%20float%20tight%20%3D%20pow%28max%28dot%28N%2C%20H%29%2C%200.0%29%2C%20mix%28130.0%2C%2030.0%2C%20pearlRoughness%29%29%3B%0A%20%20%20%20%20%20%20%20vec3%20pigment%20%3D%20baseColor%20%2A%0A%20%20%20%20%20%20%20%20%20%20mix%28vec3%280.93%2C%200.96%2C%201.06%29%2C%20vec3%281.06%2C%201.01%2C%200.96%29%2C%20wash%29%20%2A%0A%20%20%20%20%20%20%20%20%20%20%281.0%20%2B%20pearlWash%20%2A%20%28wash%20-%200.5%29%29%3B%0A%20%20%20%20%20%20%20%20float%20shade%20%3D%20%280.34%20%2B%200.66%20%2A%20key%20%2B%200.07%20%2A%20fill%29%20%2A%20%281.0%20-%200.18%20%2A%20edge%29%3B%0A%20%20%20%20%20%20%20%20vec3%20rgb%20%3D%20pigment%20%2A%20shade%3B%0A%20%20%20%20%20%20%20%20%2F%2F%20Adapt%20grain%20to%20the%20visible%20size%20of%20each%20atom.%0A%20%20%20%20%20%20%20%20float%20footprint%20%3D%20max%28length%28dFdx%28pearlLocalPosition%29%29%2C%20length%28dFdy%28pearlLocalPosition%29%29%29%3B%0A%20%20%20%20%20%20%20%20float%20fineWeight%20%3D%201.0%20-%20smoothstep%280.35%2C%201.2%2C%20footprint%20%2A%2064.0%29%3B%0A%20%20%20%20%20%20%20%20float%20mediumWeight%20%3D%201.0%20-%20smoothstep%280.35%2C%201.2%2C%20footprint%20%2A%2016.0%29%3B%0A%20%20%20%20%20%20%20%20float%20coarseWeight%20%3D%201.0%20-%20smoothstep%280.35%2C%201.2%2C%20footprint%20%2A%204.0%29%3B%0A%20%20%20%20%20%20%20%20float%20coarse%20%3D%20grainNoise%28pearlLocalPosition%20%2A%204.0%29%3B%0A%20%20%20%20%20%20%20%20float%20medium%20%3D%20grainNoise%28pearlLocalPosition%20%2A%2016.0%29%3B%0A%20%20%20%20%20%20%20%20float%20fine%20%3D%20grainNoise%28pearlLocalPosition%20%2A%2064.0%29%3B%0A%20%20%20%20%20%20%20%20float%20surfaceGrain%20%3D%20mix%28coarse%2C%20medium%2C%20mediumWeight%29%3B%0A%20%20%20%20%20%20%20%20surfaceGrain%20%3D%20mix%28surfaceGrain%2C%20fine%2C%20fineWeight%29%3B%0A%20%20%20%20%20%20%20%20rgb%20%2A%3D%201.0%20%2B%203.5%20%2A%20pearlGrain%20%2A%20coarseWeight%20%2A%20%282.0%20%2A%20surfaceGrain%20-%201.0%29%3B%0A%20%20%20%20%20%20%20%20float%20highlight%20%3D%20%280.40%20%2A%20broad%20%2B%200.22%20%2A%20tight%29%20%2A%20pearlHighlight%3B%0A%20%20%20%20%20%20%20%20highlight%20%2A%3D%201.0%20%2B%200.20%20%2A%20%28wash%20-%200.5%29%3B%0A%20%20%20%20%20%20%20%20rgb%20%2B%3D%20vec3%281.0%2C%200.975%2C%200.94%29%20%2A%20highlight%3B%0A%20%20%20%20%20%20%20%20rgb%20%2B%3D%20mix%28pigment%2C%20vec3%280.7%2C%200.8%2C%200.94%29%2C%200.5%29%20%2A%20edge%20%2A%200.075%3B%0A%20%20%20%20%20%20%20%20gl_FragColor%20%3D%20vec4%28max%28rgb%2C%20vec3%280.0%29%29%2C%20pearlOpacity%29%3B%0A%20%20%20%20%20%20%20%20%23include%20%3C%24%7BoutputChunk%7D%3E%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%60%2C%0A%20%20%20%20lights%3A%20false%2C%0A%20%20%20%20toneMapped%3A%20false%2C%0A%20%20%20%20transparent%3A%20opacity%20%3C%201%2C%0A%20%20%20%20depthWrite%3A%20opacity%20%3E%3D%201%2C%0A%20%20%20%20side%3A%20opacity%20%3C%201%20%3F%20THREE.DoubleSide%20%3A%20THREE.FrontSide%0A%20%20%7D%29%3B%0A%20%20material.extensions.derivatives%20%3D%20true%3B%0A%20%20return%20material%3B%0A%7D%0A%0Acore.HandInked%20%3D%20async%20%28args%2C%20env%29%20%3D%3E%20%7B%0A%20%20const%20THREE%20%3D%20await%20paperReady%3B%0A%20%20let%20uni%3B%0A%20%20let%20preset%20%3D%20%27Gold%27%3B%0A%20%20if%20%28args.length%20%3E%200%29%20preset%20%3D%20await%20interpretate%28args%5B0%5D%2C%20env%29%3B%0A%20%20if%20%28preset%20%3D%3D%3D%20%27Ice%27%29%20%7B%0A%20%20%20%20const%20values%20%3D%20await%20Promise.all%28args.slice%281%29.map%28arg%20%3D%3E%20interpretate%28arg%2C%20env%29%29%29%3B%0A%20%20%20%20const%20%5Broughness%20%3D%200.42%2C%20grain%20%3D%200.014%2C%20opacity%20%3D%201%2C%20highlight%20%3D%201%2C%20wash%20%3D%200.10%5D%20%3D%20values%3B%0A%20%20%20%20return%20function%20PaperIceFactory%28%29%20%7B%0A%20%20%20%20%20%20return%20createPaperIceRasterMaterial%28THREE%2C%20%7Broughness%2C%20grain%2C%20opacity%2C%20highlight%2C%20wash%7D%29%3B%0A%20%20%20%20%7D%3B%0A%20%20%7D%0A%20%20if%20%28preset%20%3D%3D%3D%20%27Glass%27%29%20%7B%0A%20%20%20%20%2F%2F%20Fixed-opacity%20sky-blue%20glass%0A%20%20%20%20const%20requestedFrost%20%3D%20args.length%20%3E%201%20%3F%20await%20interpretate%28args%5B1%5D%2C%20env%29%20%3A%200.2%3B%0A%20%20%20%20const%20frostAmount%20%3D%20Number.isFinite%28Number%28requestedFrost%29%29%0A%20%20%20%20%20%20%3F%20Math.max%280%2C%20Math.min%282.5%2C%20Number%28requestedFrost%29%29%29%20%3A%200.20%3B%0A%20%20%20%20const%20outputChunk%20%3D%20THREE.ShaderChunk.colorspace_fragment%20%3F%20%27colorspace_fragment%27%20%3A%20%27encodings_fragment%27%3B%0A%20%20%20%20return%20function%20PaperGlassFactory%28%29%20%7B%0A%20%20%20%20%20%20%2F%2F%20Alpha%20comes%20from%20this%20shader%2C%20not%20from%20surrounding%20Graphics3D%20Opacity%20directives.%0A%20%20%20%20%20%20return%20new%20THREE.ShaderMaterial%28%7B%0A%20%20%20%20%20%20%20%20name%3A%20%27Paper%20%2F%20Glass%27%2C%0A%20%20%20%20%20%20%20%20uniforms%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20glassTint%3A%20%7B%20value%3A%20new%20THREE.Color%28%27%2377c5df%27%29%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20glassGlint%3A%20%7B%20value%3A%20new%20THREE.Color%28%27%23e9faff%27%29%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20glassOpacity%3A%20%7B%20value%3A%200.09%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20glassFrost%3A%20%7B%20value%3A%20frostAmount%20%7D%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20vertexShader%3A%20%60%0A%20%20%20%20%20%20%20%20%20%20varying%20vec3%20vGlassNormal%3B%0A%20%20%20%20%20%20%20%20%20%20varying%20vec3%20vGlassLocal%3B%0A%20%20%20%20%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20vGlassNormal%20%3D%20normalize%28normalMatrix%20%2A%20normal%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20vGlassLocal%20%3D%20position%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20gl_Position%20%3D%20projectionMatrix%20%2A%20modelViewMatrix%20%2A%20vec4%28position%2C%201.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%60%2C%0A%20%20%20%20%20%20%20%20fragmentShader%3A%20%60%0A%20%20%20%20%20%20%20%20%20%20uniform%20vec3%20glassTint%3B%0A%20%20%20%20%20%20%20%20%20%20uniform%20vec3%20glassGlint%3B%0A%20%20%20%20%20%20%20%20%20%20uniform%20float%20glassOpacity%3B%0A%20%20%20%20%20%20%20%20%20%20uniform%20float%20glassFrost%3B%0A%20%20%20%20%20%20%20%20%20%20varying%20vec3%20vGlassNormal%3B%0A%20%20%20%20%20%20%20%20%20%20varying%20vec3%20vGlassLocal%3B%0A%20%20%20%20%20%20%20%20%20%20float%20hash31%28vec3%20p%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20p%20%3D%20fract%28p%20%2A%200.1031%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20p%20%2B%3D%20dot%28p%2C%20p.yzx%20%2B%2033.33%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20fract%28%28p.x%20%2B%20p.y%29%20%2A%20p.z%29%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20float%20noise3%28vec3%20p%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20vec3%20cell%20%3D%20floor%28p%29%2C%20f%20%3D%20fract%28p%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20f%20%3D%20f%20%2A%20f%20%2A%20%283.0%20-%202.0%20%2A%20f%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20mix%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20mix%28mix%28hash31%28cell%29%2C%20hash31%28cell%20%2B%20vec3%281%2C0%2C0%29%29%2C%20f.x%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mix%28hash31%28cell%20%2B%20vec3%280%2C1%2C0%29%29%2C%20hash31%28cell%20%2B%20vec3%281%2C1%2C0%29%29%2C%20f.x%29%2C%20f.y%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20mix%28mix%28hash31%28cell%20%2B%20vec3%280%2C0%2C1%29%29%2C%20hash31%28cell%20%2B%20vec3%281%2C0%2C1%29%29%2C%20f.x%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mix%28hash31%28cell%20%2B%20vec3%280%2C1%2C1%29%29%2C%20hash31%28cell%20%2B%20vec3%281%2C1%2C1%29%29%2C%20f.x%29%2C%20f.y%29%2C%20f.z%29%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20vec3%20N%20%3D%20normalize%28vGlassNormal%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28%21gl_FrontFacing%29%20N%20%3D%20-N%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20vec3%20V%20%3D%20vec3%280.0%2C%200.0%2C%201.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20vec3%20key%20%3D%20normalize%28vec3%28-0.45%2C%200.78%2C%200.95%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20facing%20%3D%20abs%28dot%28N%2C%20V%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20rim%20%3D%20pow%281.0%20-%20facing%2C%201.65%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20light%20%3D%200.86%20%2B%200.15%20%2A%20max%28dot%28N%2C%20key%29%2C%200.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20glint%20%3D%20pow%28max%28dot%28N%2C%20normalize%28key%20%2B%20V%29%29%2C%200.0%29%2C%2022.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20cloud%20%3D%20noise3%28vGlassLocal%20%2A%208.0%20%2B%20vec3%287.1%2C%201.3%2C%202.9%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20fiber%20%3D%20noise3%28vGlassLocal%20%2A%2031.0%20%2B%20vec3%282.4%2C%208.7%2C%204.2%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20frost%20%3D%201.5%20%2A%20%28cloud%20-%200.5%29%20%2B%200.5%20%2A%20%28fiber%20-%200.5%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20speck%20%3D%20hash31%28vec3%28floor%28gl_FragCoord.xy%20%2F%202.5%29%2C%201.71%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20vec3%20rgb%20%3D%20glassTint%20%2A%20light%20%2A%20%281.0%20%2B%200.35%20%2A%20glassFrost%20%2A%20frost%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20rgb%20%3D%20mix%28rgb%2C%20glassGlint%2C%200.14%20%2A%20glint%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20rgb%20%2A%3D%201.0%20-%200.20%20%2A%20rim%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20veil%20%3D%20clamp%281.0%20%2B%202.2%20%2A%20glassFrost%20%2A%20frost%2C%200.30%2C%202.10%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%20alpha%20%3D%20clamp%28glassOpacity%20%2A%20%280.78%20%2B%201.45%20%2A%20rim%20%2B%200.16%20%2A%20glint%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2A%20veil%20%2A%20%281.0%20%2B%200.20%20%2A%20glassFrost%20%2A%20%28speck%20-%200.5%29%29%2C%200.0%2C%200.52%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20gl_FragColor%20%3D%20vec4%28max%28rgb%2C%20vec3%280.0%29%29%2C%20alpha%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%23include%20%3C%24%7BoutputChunk%7D%3E%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%60%2C%0A%20%20%20%20%20%20%20%20lights%3A%20false%2C%0A%20%20%20%20%20%20%20%20toneMapped%3A%20false%2C%0A%20%20%20%20%20%20%20%20transparent%3A%20true%2C%0A%20%20%20%20%20%20%20%20depthWrite%3A%20false%2C%0A%20%20%20%20%20%20%20%20side%3A%20THREE.DoubleSide%0A%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%7D%3B%0A%20%20%7D%0A%20%20%0A%20%20if%20%28paperPalette%5Bpreset%5D%29%20%7B%0A%20%20%20%20const%20color%20%3D%20new%20THREE.Color%28paperPalette%5Bpreset%5D%29%3B%0A%20%20%20%20uni%20%3D%20%7B%20pigment%3A%20%7B%20value%3A%20color%20%7D%2C%20shineTint%3A%20%7B%20value%3A%20new%20THREE.Color%28preset%20%3D%3D%3D%20%27Lavender%27%20%3F%20%27%23ffffff%27%20%3A%20%28preset%20%3D%3D%3D%20%27Teal%27%20%7C%7C%20preset%20%3D%3D%3D%20%27Cyan%27%29%20%3F%20%27%23d9faff%27%20%3A%20%27%23fff5de%27%29%20%7D%2C%20shineScale%3A%20%7B%20value%3A%20%28preset%20%3D%3D%3D%20%27Red%27%20%7C%7C%20preset%20%3D%3D%3D%20%27Cyan%27%29%20%3F%200.43%20%3A%201.0%20%7D%20%7D%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20uni%20%3D%20%7B%20pigment%3A%20%7B%20value%3A%20preset%20%7D%2C%20shineTint%3A%20%7B%20value%3A%20%28await%20interpretate%28args%5B1%5D%2C%20env%29%29%20%7C%7C%20new%20THREE.Color%28%27%23fff5de%27%29%20%7D%2C%20shineScale%3A%20%7B%20value%3A%20%28await%20interpretate%28args%5B2%5D%2C%20env%29%29%20%7C%7C%201.0%20%7D%20%7D%3B%0A%20%20%20%20preset%20%3D%20interpretate.hashv2%28uni%29%3B%0A%20%20%7D%0A%20%20%0A%20%20const%20outputChunk%20%3D%20THREE.ShaderChunk.colorspace_fragment%20%3F%20%27colorspace_fragment%27%20%3A%20%27encodings_fragment%27%3B%0A%20%20return%20function%20PaperMaterialFactory%28%29%20%7B%0A%20%20%20%20const%20material%20%3D%20new%20THREE.ShaderMaterial%28%7B%0A%20%20%20%20%20%20name%3A%20%27Paper%20%2F%20%27%20%2B%20preset%2C%0A%20%20%20%20%20%20uniforms%3A%20uni%2C%0A%20%20%20%20%20%20vertexShader%3A%20%60%0A%20%20%20%20%20%20%20%20varying%20vec3%20vPaperNormal%3B%0A%20%20%20%20%20%20%20%20varying%20vec3%20vPaperPosition%3B%0A%20%20%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20vPaperNormal%20%3D%20normalize%28normalMatrix%20%2A%20normal%29%3B%0A%20%20%20%20%20%20%20%20%20%20vPaperPosition%20%3D%20position%3B%0A%20%20%20%20%20%20%20%20%20%20gl_Position%20%3D%20projectionMatrix%20%2A%20modelViewMatrix%20%2A%20vec4%28position%2C%201.0%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%60%2C%0A%20%20%20%20%20%20fragmentShader%3A%20%60%0A%20%20%20%20%20%20%20%20uniform%20vec3%20pigment%3B%0A%20%20%20%20%20%20%20%20uniform%20vec3%20shineTint%3B%0A%20%20%20%20%20%20%20%20uniform%20float%20shineScale%3B%0A%20%20%20%20%20%20%20%20varying%20vec3%20vPaperNormal%3B%0A%20%20%20%20%20%20%20%20varying%20vec3%20vPaperPosition%3B%0A%20%20%20%20%20%20%20%20float%20hash31%28vec3%20p%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20p%20%3D%20fract%28p%20%2A%200.1031%29%3B%0A%20%20%20%20%20%20%20%20%20%20p%20%2B%3D%20dot%28p%2C%20p.yzx%20%2B%2033.33%29%3B%0A%20%20%20%20%20%20%20%20%20%20return%20fract%28%28p.x%20%2B%20p.y%29%20%2A%20p.z%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20float%20noise3%28vec3%20p%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20vec3%20i%20%3D%20floor%28p%29%2C%20f%20%3D%20fract%28p%29%3B%0A%20%20%20%20%20%20%20%20%20%20f%20%3D%20f%2Af%2A%283.0-2.0%2Af%29%3B%0A%20%20%20%20%20%20%20%20%20%20return%20mix%28%0A%20%20%20%20%20%20%20%20%20%20%20%20mix%28mix%28hash31%28i%29%2Chash31%28i%2Bvec3%281%2C0%2C0%29%29%2Cf.x%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mix%28hash31%28i%2Bvec3%280%2C1%2C0%29%29%2Chash31%28i%2Bvec3%281%2C1%2C0%29%29%2Cf.x%29%2Cf.y%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mix%28mix%28hash31%28i%2Bvec3%280%2C0%2C1%29%29%2Chash31%28i%2Bvec3%281%2C0%2C1%29%29%2Cf.x%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mix%28hash31%28i%2Bvec3%280%2C1%2C1%29%29%2Chash31%28i%2Bvec3%281%2C1%2C1%29%29%2Cf.x%29%2Cf.y%29%2Cf.z%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20void%20main%28%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20vec3%20N%20%3D%20normalize%28vPaperNormal%29%3B%0A%20%20%20%20%20%20%20%20%20%20vec3%20key%20%3D%20normalize%28vec3%28-0.45%2C%200.78%2C%200.95%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20vec3%20fill%20%3D%20normalize%28vec3%280.78%2C%200.15%2C%200.5%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20vec3%20V%20%3D%20vec3%280.0%2C%200.0%2C%201.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20float%20lit%20%3D%20max%28dot%28N%2Ckey%29%2C0.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20float%20shade%20%3D%200.41%20%2B%200.58%20%2A%20pow%28lit%2C0.72%29%20%2B%200.08%20%2A%20max%28dot%28N%2Cfill%29%2C0.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20float%20facing%20%3D%20max%28dot%28N%2CV%29%2C0.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20float%20ink%20%3D%201.0%20-%200.34%20%2A%20pow%281.0-facing%2C2.2%29%3B%0A%20%20%20%20%20%20%20%20%20%20vec3%20H%20%3D%20normalize%28key%2BV%29%3B%0A%20%20%20%20%20%20%20%20%20%20float%20softbox%20%3D%20pow%28max%28dot%28N%2CH%29%2C0.0%29%2C8.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20float%20core%20%3D%20pow%28max%28dot%28N%2CH%29%2C0.0%29%2C42.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20float%20fleck%20%3D%20noise3%28vPaperPosition%2A26.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20float%20micro%20%3D%20noise3%28vPaperPosition%2A55.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20float%20paper%20%3D%201.0%20%2B%200.11%2A%28fleck-0.5%29%20%2B%200.065%2A%28micro-0.5%29%3B%0A%20%20%20%20%20%20%20%20%20%20vec3%20rgb%20%3D%20pigment%20%2A%20shade%20%2A%20ink%20%2A%20paper%3B%0A%20%20%20%20%20%20%20%20%20%20rgb%20%2B%3D%20shineTint%20%2A%20shineScale%20%2A%20%280.36%2Asoftbox%20%2B%200.13%2Acore%29%3B%0A%20%20%20%20%20%20%20%20%20%20gl_FragColor%20%3D%20vec4%28max%28rgb%2Cvec3%280.0%29%29%2C1.0%29%3B%0A%20%20%20%20%20%20%20%20%20%20%23include%20%3C%24%7BoutputChunk%7D%3E%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%60%2C%0A%20%20%20%20%20%20lights%3A%20false%2C%0A%20%20%20%20%20%20toneMapped%3A%20false%0A%20%20%20%20%7D%29%3B%0A%20%20%20%20return%20material%3B%0A%20%20%7D%3B%0A%7D%3B%0Areturn%20%27HandInked%20registered%27%3B Here is a little demo:
specs = {
{HandInked["Gold"], "Gold"},
{HandInked["Lavender"], "Lavender"},
{HandInked["Coral"], "Coral"},
{HandInked["Teal"], "Teal"},
{HandInked["Green"], "Green"},
{HandInked[LightBlue, Red], "LightBlue, Red"},
{HandInked[Orange, White, 10.0], "Orange, White"},
{HandInked["Glass"], "Glass"},
{HandInked["Ice"], "Ice"}
};
Graphics3D[
MapIndexed[
With[{y = #2[[1]] - 4},
{
MeshMaterial[#1[[1]]],
Sphere[{y 0.35, y, 0}, 0.4],
Black, Directive[FontSize->14],
Text[#1[[2]], {y 0.35 - 0.2, y + 0.2, -0.6}, {1,0}]
}
] &,
specs
]
] (*VB[*)(FrontEndRef["Feb9b1528-372d-4281-928a-e728dda7d1b8"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgUJpyZZJhmaGlnoGpsbpeiaGFkY6loaWSTqppobWaSkJJqnGCZZAACUVRXE"*)(*]VB*) Use cases
The original motivation was to emulate the look of a 3D crystal structure made by hand with paper and ink. The author (me) is not quite sure that it really matches the intended mood, but the illustrations are much more eye-catching than the physically based materials (or even path tracing) used by default in Graphics3D. Here are a few examples:
(*VB[*)(FrontEndRef["F58d83b75-59a5-406b-9e0f-71bf709fb778"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgUJm1qkWBgnmZvqmlommuqaGJgl6VqmGqTpmhsmpZkbWKYlmZtbAACS7BWm"*)(*]VB*) These are not images! Try zooming and panning.
(*VB[*)(FrontEndRef["F53adba0e-4d9d-4276-b528-e91bbd28ed72"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgUJmxonpiQlGqTqmqRYpuiaGJmb6SaZGlnoploaJiWlGFmkppgbAQCjpRZL"*)(*]VB*) Can we use it on regular 3D plots? Well, not directly: the Wolfram Language strips unknown styles from PlotStyle. However, we can try to pass our custom style through the Prolog option:
Plot3D[x^2 + y^2, {x, -2, 2}, {y, -2, 2}, Prolog->Directive[{MeshMaterial[HandInked["Ice"]]}]] (*VB[*)(FrontEndRef["F5560d348-74f4-484e-a5e6-b27939a53a10"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgUJm5qaGaQYm1jompukmeiaWJik6iaapprpJhmZWxpbJpoaJxoaAACLVBUz"*)(*]VB*) Cool, isn't it? I hope this post has given you a little more artistic freedom with our notebook.
See ya!