RayGL makes use of special prefixes and directives. THe main script and all dependencies are composed in a single uber shader(Multi pass is controlled with conditional defines and the pass directive). If you want to debug the final glsl output, for now, just make a small typo and click "Apply"(Shortcuts are Alt-A and Alt-Enter). The side editor will show the final shader text along with the error message.
All scripts must use "$main(vec4 outputColor, vec2 fragCoords)" as the entry point for the shader execution(The one from the currently active script will be used). The use of the "$" character does the namespacing to prevent conflicts and allow for variable/function reuse in other scripts.
Code modularization is performed with the following directives:
In a script we define eg a function
vec3 $myfunc(float myArg) {
return vec3(myArg);
}
We get the editor state in a url from the Save link, and short url it to "someUrl"
We can then import it in another script and namespace it using the "@" prefix as follows.
#import myModule < http://someUrl >
$main(vec4 outputColor, vec3 fragCoords)
{
outputColor.rgb = @myModule.myfunc(123);
}
Scripts can define special placeholders in a global namespace to allow for overridable behavior:
#export a_global_func < vec3 (vec3 col) >
#export a_global_var < vec3 = vec3(0.1, 0.3, 0.2) >
// We import the global namespace like this:
#import GLOBAL <>
vec3 $a_global_func( vec3 col )
{
return col * @GLOBAL.a_global_var;
}
$main(vec4 outputColor, vec3 fragCoords)
{
outputColor.rgb = @GLOBAL.a_global_func(outputColor);
}
Functions must be defined but variables must not, they will be automatically added on the top of the shader using the configuration.
We then save/short url this script in someUrl and make a new one like:
#import myModule <someUrl>
#export a_global_func < vec3 (float myArg) >
vec3 $a_global_func( vec3 col )
{
return col + vec3(0.1);
}
$main(vec4 outputColor, vec3 fragCoords)
{
// the $main function from the previous script will use the a_global_func defined in this script
@myModule.main(outputColor, fragCoords);
}
This way we can call code and override parts of it. Check the sdf module in the core lib for a more complex example.
Fetch and use images from the web in sampler2D uniforms like this:
#import IMG < http://imageUrl >
vec4 texel = texture2D(@IMG.tex, uv);
vec4 infor = @IMG.inf.xyzw;
The "tex" property is a sampler2D and the inf property the following vec4:
{ x: imageWidth, y: imageHeight, z: 0.0, y:0.0 }
Fetch and use cubemaps in samplerCube uniforms. The image url provided must contain all six sides of the cube in 1x6, 6x1, 3x2 or 2x3 arrangement.
#import CUBE < http://imageUrl >
vec4 texel = textureCube(@CUBE.tex, dir.xyz);
vec4 infor = @CUBE.inf.xyzw;
The "tex" property is a samplerCube and the inf property the following vec4:
{ x: cubeSideWidth, y: cubeSideHeight, z: 0.0, y:0.0 }
Fetch and use video frames in sampler2D uniforms. Videos are always muted and looping.
#import VID < http://videoUrl >
vec4 texel = texture2D(@VID.tex, dir.xyz);
vec4 infor = @VID.inf.xyzw;
The "tex" property is a sampler2D and the inf property the following vec4:
{ x: imageWidth, y: imageHeight, z: currentTime, y: videoDuration }
Stream 2D textures from websockets. Servers just have to push arraybuffers of unsigned byte RGBA format prepended by a 2 Uint64s header width with the width/height of the current frame and a single float32 serverData which is accesible through the inf uniform for a fixed total of 8 header bytes.
#import NET < ws://serverUrl >
vec4 texel = texture2D(@NET.tex, dir.xyz);
vec4 infor = @NET.inf.xyzw;
The "tex" property is a sampler2D and the inf property the following vec4:
{ x: imageWidth, y: imageHeight, z: lastReceivedFrameTime, y: serverData }
Sets up multi pass.
// Pass textures are half the screen size
#pass MYPASS <view: 0.5 0.5; rel>
$main(vec4 outputColor, vec3 fragCoords)
{
vec2 uv = fragCoords.xy / iResolution.xy;
#if defined @MYPASS.PASS
// This will run in the pass we defined
outputColor.rg = uv.xy;
#else
// This will run in the main pass, read
// from the texture generated by MYPASS
outputColor.gbr = texture2D(@MYPASS.tex, uv).rgb;
#endif
}
Passes render directly to the screen or in framebuffer textures. Framebuffers exposed in the @PASSNAME.tex property and a .inf vec4 property like:
{ x: textureWidth, y: textureHeight, z: 0., y: 0. }
Framebuffers are paired so the last rendered frame can be accesed through the .tex property for implementing feedback effects.
Configuration of the pass is performed using a style parser in the directive text.
view - The first two numbers are used for the width and height of the viewport, the next two for the offset. These are interpreted as pixels by default and if the rel keyword is used, relative to the screen size.
The keywords supported are the following:
screen - Don't use framebuffers, output directly on screen
rel - Interpret provided coordinates as fractions of the screen size
func-xxx - Set the blendFunction like this:
#pass PASS1 < func: one msa >
#pass PASS2 < func: one msa one zero >
They keyword can be accompanied by 2 or 4 tokens, to use blendFunc or blendFuncSeparate respectively. Tokens are shortcuts for the following gl contants:
- "one": "ONE", "zero": "ZERO", "src": "SRC_COLOR", "msc": "ONE_MINUS_SRC_COLOR", "dsc": "DST_COLOR", "mdc": "ONE_MINUS_DST_COLOR", "sra": "SRC_ALPHA", "msa": "ONE_MINUS_SRC_ALPHA", "dsa": "DST_ALPHA", "mda": "ONE_MINUS_DST_ALPHA", "sat": "SRC_ALPHA_SATURATE"
eq-xxx Like above, takes one or two tokens and passes them to blendEquation or blendEquationSeparate. Tokens are:
- "add": "FUNC_ADD", "sub": "FUNC_SUBTRACT", "rev": "FUNC_REVERSE_SUBTRACT", "min": "MIN_EXT", "max": "MAX_EXT"
Mind that rayglider will try to enable the blend_minmax extension but it is not guaranteed that the min and max modes will actually be available.
wrap Sets the wrap options for the framebuffer textures:
#pass P1 <view: 1 1; rel; wrap: clamp repeat>
- "mirror": "MIRRORED_REPEAT", "clamp": "CLAMP_TO_EDGE", "repeat": "REPEAT"
freq Controls how often this pass will be performed. Accepts an argument for the number of frames to skip between invocations.
#pass P1 <view: 1 1; rel; freq: 10>
mipmap Try to generate mipmaps for the framebuffer texture after render.
cube Render a cubemap texture. In this case the texture uniform for the pass will be a samplerCube. It will perform the pass 6 times during which iFace uniform will take the values 0-6 to configure each face accordingly.
map Reduce the texture. Will perform the number of passes provided with the second argument each time halving both dimensions. The texture exposed as the uniform for the pass will be the previous level while performing the reduce pass. For all other passes it will be the last level.
vmap Same as map but will reduce only the height, useful for physic collision calculations
rmap Same as map but will iterate the levels in reverse order, from smaller to bigger. Useful for implementing cone marching
Sets up a render pass like #pass but post passes will run after all normal passes. Useful for making post process modules and gui elements