LittleJS 1.30.0

Draw

Shader

Shader - A custom fragment shader for objects and draws, 2D or 3D

  • Write a mainImage function in the post processing style, the renderer wraps it with its own program
  • It gives the surface color, then the object's color and additive color apply in 2D, and the lighting, shadows and fog in 3D; set emissive to 1 on a 3D object for the snippet's color to be final
  • Set it as obj.shader, or use setShader for 2D draws and render3D.shader for 3D draws
  • Draws that share a Shader share a batch; with no Shader set nothing changes
  • In 2D it shades textured draws, untextured ones like drawRect draw as they are
  • A render target, like a tile layer drawn in WebGL, holds premultiplied color, and so does every image when tilesPixelated is false; there iChannel0 reads premultiplied texels and the snippet's color is taken as premultiplied too. premultipliedTexture is true there, so a snippet that changes the alpha, or makes a see through color of its own, scales the rgb with it: if (premultipliedTexture) c.rgb *= k;
  • Compiled once per renderer by the first draw that needs it; a bad snippet throws with the GLSL log in debug
  • Make each Shader once, at init, and share it; every one made lives for the session with its programs
  • Names in both renderers: iChannel0 the texture, iTime, iResolution, premultipliedTexture, and localUV, 0 to 1 across the sprite or the mesh's own uv
  • Names in 3D only: worldPos, worldNormal, cameraPos, sunDirection, sunColor, ambientColor, ambientGroundColor, lightCount, lights[i], lightColors[i] and shadow(), and viewProjection, the camera's matrix from the world to the screen, for a snippet that sets its own depth: vec4 c = viewProjection * vec4(p, 1.); gl_FragDepth = c.z / c.w * .5 + .5 puts it at world point p; only on the screen, the shadow map and sceneDepth draw the mesh where it is
  • In 3D the snippet may also define void mainNormal(inout vec3 n), given the normal facing the camera after the normal map, in world space, to bend it per pixel for waves or ripples; the lighting, specular, reflection and fog then all use it, as they use a normal map
  • In 3D the shadow map is drawn without the Shader's fragment code, cut only by the texture's alpha, so a snippet that removes parts of a surface still shadows with the whole of it
  • In 3D a second snippet, the vertex code, may define void mainVertex(inout vec3 pos, inout vec3 normal), given each vertex's place and normal in world space, an instance's own in a batch, to move it for wind, waves or a wobble; it runs in the shadow map and the camera's depth too, so a moved vertex casts and hides where it is drawn. Its names: iTime, iResolution, cameraPos, iChannel0 (texture or texelFetch in the vertex stage), localUV, modelMatrix, instanceData (the four numbers an InstancedMesh3D's setDataAt gives, the fragment code may read it too) and vertexColor, the vertex color times the tint, which it may change. The object's bounds are not moved with it, so one that moves vertices far needs room to be culled by, like a bigger radius
  • A Shader of vertex code alone, new Shader('', vertexCode), keeps the 3D plugin's own look; in 2D a Shader's vertex code is not used
  • Values of the game's own, uniforms, are given by name when it is made and read by name in the snippets, which need not declare them: new Shader(code, '', {glow: 1, tint: RED}), then shader.setUniform('glow', 2) as often as needed, with no recompile; each is declared in the snippets that use it, so one may share a name with an input of the engine's in the other stage, and is sent to a program only when it changed; a name the engine's shader uses already, like tint or lightColor, fails to compile with the GLSL error naming it; a matrix list is column by column; in 3D set a value outside the 3D pass, it holds for the frame

Constructor

new Shader(fragmentCode, vertexCode, uniforms)engineDraw.js:656

Create a shader from a fragment snippet that defines void mainImage(out vec4 c, vec2 uv), and in 3D a vertex snippet that defines void mainVertex(inout vec3 pos, inout vec3 normal)

fragmentCode string optional

Left out or empty with vertex code, the 3D plugin's own look

vertexCode string optional

3D only

uniforms Object<string, ShaderUniform> optional

Values the snippets read by name, declared for them: a number is a float, a boolean a bool, a Vector2 a vec2, a Vector3 a vec3, a Color a vec4, and a list of 2, 3, 4, 9 or 16 numbers, or a Matrix4, a vec2, vec3, vec4, mat3 or mat4; change one with setUniform

const fade = new Shader(`
void mainImage(out vec4 c, vec2 uv)
{
    c = texture(iChannel0, uv);
    float fade = .5 + .5*sin(iTime);
    c.a *= fade;
    if (premultipliedTexture) c.rgb *= fade; // a tile layer, or any image with tilesPixelated false
}`);
obj.shader = fade;

Members

Methods

Members

fragmentCode : stringengineDraw.js:672

The mainImage snippet, empty for a 3D Shader of vertex code alone

noTint : booleanengineDraw.js:689

In 2D, write what mainImage gives as it is, with the draw's alpha, leaving out the draw's color and additive color, for data rather than a picture, as the light system's normal pass does; its output is taken as straight color, not premultiplied, whatever the texture; set it before the shader first draws, its program is built then

program : WebGLProgram|undefinedengineDraw.js:677

The 2D program, compiled by the first draw that needs it, read only

program3D : WebGLProgram|undefinedengineDraw.js:680

The 3D program, compiled by the 3D plugin the same way, read only

shadowProgram3D : WebGLProgram|undefinedengineDraw.js:684

The 3D depth program a Shader with vertex code draws the shadow map and the camera's depth with, so a vertex it moves casts and hides where it is drawn, read only

uniformTypes : Object<string, string>engineDraw.js:693
uniformValues : Object<string, (number|Float32Array)>engineDraw.js:695
vertexCode : stringengineDraw.js:674

The mainVertex snippet, 3D only, empty for none

Methods

Let go of this shader: its compiled programs are freed and it leaves the engine's list of shaders, which keeps every one made for a lost context, so a scene made again and again, or an editor trying snippets, does not keep them all; calling it again does nothing, and a draw with it afterwards compiles it again and takes it back, so only let go of one no object still draws with when it should stay freed

setUniform(name, value)engineDraw.js:713

Change a uniform's value: in 2D what was drawn with the shader before it keeps the old value, in 3D the value holds for the frame, so set it outside the 3D pass

name string

One of those the shader was made with

value ShaderUniform

Of the same kind it was made with