-
Notifications
You must be signed in to change notification settings - Fork 209
Custom Materials
Blotter Materials describe how your texts will be rendered and provide the interface through which you can introduce variety in the characteristics of any effect. Blotter comes with a growing set of ready-to-use materials, but if you would like information on creating custom materials for private use, or for contributing new materials to Blotter, you should follow the guide below.
Note: I'll do my best to explain concepts here in a understandable way, but this guide is for those interested in supplying their own GLSL
code to create new effects in Blotter. If you're new to GLSL
, you may find starting with The Book of Shaders helpful, but dont be afraid to continue here. If you're experienced with GLSL
, you may find this guide a little light, but feel free to ask any questions.
Every Blotter material relies on its mainImage
property for rendering its effect through a Fragment shader or, to put it more accurately, what is called a "Pixel Shader".
What are pixel shaders?
If you render a full screen quad, meaning that each of four points is placed in one of the four corners of the viewport, then the fragment shader for that quad is called pixel shader, because you could say that now each fragment corresponds to exactly one pixel of the screen. So a pixel shader is a fragment shader for a fullscreen quad.
- https://stackoverflow.com/a/19452805
When you create a new Blotter Material, you supply it with a Pixel Shader, in the form of a string, that must define a mainImage
function. This function generates rendered texts by computing a color for each pixel, and is called once per pixel. It will receive a fragCoord
and must set the value of the mainImage
out
argument.
The prototype is as follows. If you've used Shadertoy this should feel familiar.
void mainImage(out vec4 mainImage, in vec2 fragCoord);
Given this format, the following would define an acceptable program, implementing the mainImage
function to return any text rendered through it in the color red.
var mainImageSource = [
"void mainImage(out vec4 mainImage, in vec2 fragCoord) {",
" vec2 uv = fragCoord.xy / uResolution.xy;",
" vec3 red = vec3(1.0, 0.0, 0.0); // (R, G, B)",
" mainImage = vec4(red, textTexture(uv).a);",
"}"
].join("\n");
Those familiar with GLSL
may notice the call to textTexture
here. Normally in Fragment shaders one will call texture2D(sampler2D sampler, vec2 coord)
in order to retrieve texel information at a given texture coordinate. However, in Blotter texts that share a material are mapped into an atlas to be rendered together. In order to make Blotter's GLSL
API function in a predictable way, the complexity around this logic is abstracted away from the developer. For example, for any given text, fragCoord
's x
or y
values will range from 0
to the full width or height of the given text, rather than of the full atlas texture. This is also true for uResolution
. As such, you should think of your mainImage
functions as simply rendering any individual text, and sample that text using Blotter's GLSL
textTexture(vec2 coord)
function, which will retrieve the proper texture information for you.
Once you have a mainImage
definition, you can create a custom Blotter material by instantiating a new Blotter.ShaderMaterial
object.
The following will create a new instance of Blotter.ShaderMaterial
where mainImageSource
is the string representation of your Pixel Shader, and the uniforms
object is empty.
var mainImageSource = [
"void mainImage(out vec4 mainImage, in vec2 fragCoord) {",
" vec2 uv = fragCoord.xy / uResolution.xy;",
" vec3 red = vec3(1.0, 0.0, 0.0); // (R, G, B)",
" mainImage = vec4(red, textTexture(uv).a);",
"}"
].join("\n");
var material = new Blotter.ShaderMaterial(mainImageSource, { uniforms : {} });
In the example mainImage
we have been using so far, we have hardcoded the function so that texts rendered through it are output in the color red. However, if we wanted to make this Material more flexible we could utilize "uniforms" in order to render the texts in any color the user chooses.
Note: For those unfamiliar with uniforms in GLSL
, it may be helpful to reference the The Basics and the Documentation sections of Blotter's documentation site, or go over this section in the Book of Shaders.
var mainImageSource = [
"void mainImage(out vec4 mainImage, in vec2 fragCoord) {",
" vec2 uv = fragCoord.xy / uResolution.xy;",
" mainImage = vec4(uColor, textTexture(uv).a);",
"}"
].join("\n");
var material = new Blotter.ShaderMaterial(mainImageSource, {
uniforms : {
uColor : { type : "3f", value : [1.0, 0.0, 0.0] } // (R, G, B)
}
});
Now, if we wanted to update all texts rendered through the material to render in the color green, we'd simply need to update the value of the uColor
uniform on our Material's instance.
material.uniforms.uColor.value = [0.0, 1.0, 0.0];
More coming soon.