Files
ComfyUI_frontend/src/nodes/glshaders.js
2023-04-11 08:57:53 +02:00

1840 lines
52 KiB
JavaScript
Executable File

(function(global) {
if (typeof GL == "undefined")
return;
var LiteGraph = global.LiteGraph;
var LGraphCanvas = global.LGraphCanvas;
var SHADERNODES_COLOR = "#345";
var LGShaders = LiteGraph.Shaders = {};
var GLSL_types = LGShaders.GLSL_types = ["float","vec2","vec3","vec4","mat3","mat4","sampler2D","samplerCube"];
var GLSL_types_const = LGShaders.GLSL_types_const = ["float","vec2","vec3","vec4"];
var GLSL_functions_desc = {
"radians": "T radians(T degrees)",
"degrees": "T degrees(T radians)",
"sin": "T sin(T angle)",
"cos": "T cos(T angle)",
"tan": "T tan(T angle)",
"asin": "T asin(T x)",
"acos": "T acos(T x)",
"atan": "T atan(T x)",
"atan2": "T atan(T x,T y)",
"pow": "T pow(T x,T y)",
"exp": "T exp(T x)",
"log": "T log(T x)",
"exp2": "T exp2(T x)",
"log2": "T log2(T x)",
"sqrt": "T sqrt(T x)",
"inversesqrt": "T inversesqrt(T x)",
"abs": "T abs(T x)",
"sign": "T sign(T x)",
"floor": "T floor(T x)",
"round": "T round(T x)",
"ceil": "T ceil(T x)",
"fract": "T fract(T x)",
"mod": "T mod(T x,T y)", //"T mod(T x,float y)"
"min": "T min(T x,T y)",
"max": "T max(T x,T y)",
"clamp": "T clamp(T x,T minVal = 0.0,T maxVal = 1.0)",
"mix": "T mix(T x,T y,T a)", //"T mix(T x,T y,float a)"
"step": "T step(T edge, T edge2, T x)", //"T step(float edge, T x)"
"smoothstep": "T smoothstep(T edge, T edge2, T x)", //"T smoothstep(float edge, T x)"
"length":"float length(T x)",
"distance":"float distance(T p0, T p1)",
"normalize":"T normalize(T x)",
"dot": "float dot(T x,T y)",
"cross": "vec3 cross(vec3 x,vec3 y)",
"reflect": "vec3 reflect(vec3 V,vec3 N)",
"refract": "vec3 refract(vec3 V,vec3 N, float IOR)"
};
//parse them
var GLSL_functions = {};
var GLSL_functions_name = [];
parseGLSLDescriptions();
LGShaders.ALL_TYPES = "float,vec2,vec3,vec4";
function parseGLSLDescriptions()
{
GLSL_functions_name.length = 0;
for(var i in GLSL_functions_desc)
{
var op = GLSL_functions_desc[i];
var index = op.indexOf(" ");
var return_type = op.substr(0,index);
var index2 = op.indexOf("(",index);
var func_name = op.substr(index,index2-index).trim();
var params = op.substr(index2 + 1, op.length - index2 - 2).split(",");
for(var j in params)
{
var p = params[j].split(" ").filter(function(a){ return a; });
params[j] = { type: p[0].trim(), name: p[1].trim() };
if(p[2] == "=")
params[j].value = p[3].trim();
}
GLSL_functions[i] = { return_type: return_type, func: func_name, params: params };
GLSL_functions_name.push( func_name );
//console.log( GLSL_functions[i] );
}
}
//common actions to all shader node classes
function registerShaderNode( type, node_ctor )
{
//static attributes
node_ctor.color = SHADERNODES_COLOR;
node_ctor.filter = "shader";
//common methods
node_ctor.prototype.clearDestination = function(){ this.shader_destination = {}; }
node_ctor.prototype.propagateDestination = function propagateDestination( dest_name )
{
this.shader_destination[ dest_name ] = true;
if(this.inputs)
for(var i = 0; i < this.inputs.length; ++i)
{
var origin_node = this.getInputNode(i);
if(origin_node)
origin_node.propagateDestination( dest_name );
}
}
if(!node_ctor.prototype.onPropertyChanged)
node_ctor.prototype.onPropertyChanged = function()
{
if(this.graph)
this.graph._version++;
}
/*
if(!node_ctor.prototype.onGetCode)
node_ctor.prototype.onGetCode = function()
{
//check destination to avoid lonely nodes
if(!this.shader_destination)
return;
//grab inputs with types
var inputs = [];
if(this.inputs)
for(var i = 0; i < this.inputs.length; ++i)
inputs.push({ type: this.getInputData(i), name: getInputLinkID(this,i) });
var outputs = [];
if(this.outputs)
for(var i = 0; i < this.outputs.length; ++i)
outputs.push({ name: getOutputLinkID(this,i) });
//pass to code func
var results = this.extractCode(inputs);
//grab output, pass to next
if(results)
for(var i = 0; i < results.length; ++i)
{
var r = results[i];
if(!r)
continue;
this.setOutputData(i,r.value);
}
}
*/
LiteGraph.registerNodeType( "shader::" + type, node_ctor );
}
function getShaderNodeVarName( node, name )
{
return "VAR_" + (name || "TEMP") + "_" + node.id;
}
function getInputLinkID( node, slot )
{
if(!node.inputs)
return null;
var link = node.getInputLink( slot );
if( !link )
return null;
var origin_node = node.graph.getNodeById( link.origin_id );
if( !origin_node )
return null;
if(origin_node.getOutputVarName)
return origin_node.getOutputVarName(link.origin_slot);
//generate
return "link_" + origin_node.id + "_" + link.origin_slot;
}
function getOutputLinkID( node, slot )
{
if (!node.isOutputConnected(slot))
return null;
return "link_" + node.id + "_" + slot;
}
LGShaders.registerShaderNode = registerShaderNode;
LGShaders.getInputLinkID = getInputLinkID;
LGShaders.getOutputLinkID = getOutputLinkID;
LGShaders.getShaderNodeVarName = getShaderNodeVarName;
LGShaders.parseGLSLDescriptions = parseGLSLDescriptions;
//given a const number, it transform it to a string that matches a type
var valueToGLSL = LiteGraph.valueToGLSL = function valueToGLSL( v, type, precision )
{
var n = 5; //num decimals
if(precision != null)
n = precision;
if(!type)
{
if(v.constructor === Number)
type = "float";
else if(v.length)
{
switch(v.length)
{
case 2: type = "vec2"; break;
case 3: type = "vec3"; break;
case 4: type = "vec4"; break;
case 9: type = "mat3"; break;
case 16: type = "mat4"; break;
default:
throw("unknown type for glsl value size");
}
}
else
throw("unknown type for glsl value: " + v.constructor);
}
switch(type)
{
case 'float': return v.toFixed(n); break;
case 'vec2': return "vec2(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + ")"; break;
case 'color3':
case 'vec3': return "vec3(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + ")"; break;
case 'color4':
case 'vec4': return "vec4(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + "," + v[3].toFixed(n) + ")"; break;
case 'mat3': return "mat3(1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)"; break; //not fully supported yet
case 'mat4': return "mat4(1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0)"; break;//not fully supported yet
default:
throw("unknown glsl type in valueToGLSL:", type);
}
return "";
}
//makes sure that a var is of a type, and if not, it converts it
var varToTypeGLSL = LiteGraph.varToTypeGLSL = function varToTypeGLSL( v, input_type, output_type )
{
if(input_type == output_type)
return v;
if(v == null)
switch(output_type)
{
case "float": return "0.0";
case "vec2": return "vec2(0.0)";
case "vec3": return "vec3(0.0)";
case "vec4": return "vec4(0.0,0.0,0.0,1.0)";
default: //null
return null;
}
if(!output_type)
throw("error: no output type specified");
if(output_type == "float")
{
switch(input_type)
{
//case "float":
case "vec2":
case "vec3":
case "vec4":
return v + ".x";
break;
default: //null
return "0.0";
break;
}
}
else if(output_type == "vec2")
{
switch(input_type)
{
case "float":
return "vec2("+v+")";
//case "vec2":
case "vec3":
case "vec4":
return v + ".xy";
default: //null
return "vec2(0.0)";
}
}
else if(output_type == "vec3")
{
switch(input_type)
{
case "float":
return "vec3("+v+")";
case "vec2":
return "vec3(" + v + ",0.0)";
//case "vec3":
case "vec4":
return v + ".xyz";
default: //null
return "vec3(0.0)";
}
}
else if(output_type == "vec4")
{
switch(input_type)
{
case "float":
return "vec4("+v+")";
case "vec2":
return "vec4(" + v + ",0.0,1.0)";
case "vec3":
return "vec4(" + v + ",1.0)";
default: //null
return "vec4(0.0,0.0,0.0,1.0)";
}
}
throw("type cannot be converted");
}
//used to plug incompatible stuff
var convertVarToGLSLType = LiteGraph.convertVarToGLSLType = function convertVarToGLSLType( varname, type, target_type )
{
if(type == target_type)
return varname;
if(type == "float")
return target_type + "(" + varname + ")";
if(target_type == "vec2") //works for vec2,vec3 and vec4
return "vec2(" + varname + ".xy)";
if(target_type == "vec3") //works for vec2,vec3 and vec4
{
if(type == "vec2")
return "vec3(" + varname + ",0.0)";
if(type == "vec4")
return "vec4(" + varname + ".xyz)";
}
if(target_type == "vec4")
{
if(type == "vec2")
return "vec4(" + varname + ",0.0,0.0)";
if(target_type == "vec3")
return "vec4(" + varname + ",1.0)";
}
return null;
}
//used to host a shader body **************************************
function LGShaderContext()
{
//to store the code template
this.vs_template = "";
this.fs_template = "";
//required so nodes now where to fetch the input data
this.buffer_names = {
uvs: "v_coord"
};
this.extra = {}; //to store custom info from the nodes (like if this shader supports a feature, etc)
this._functions = {};
this._uniforms = {};
this._codeparts = {};
this._uniform_value = null;
}
LGShaderContext.prototype.clear = function()
{
this._uniforms = {};
this._functions = {};
this._codeparts = {};
this._uniform_value = null;
this.extra = {};
}
LGShaderContext.prototype.addUniform = function( name, type, value )
{
this._uniforms[ name ] = type;
if(value != null)
{
if(!this._uniform_value)
this._uniform_value = {};
this._uniform_value[name] = value;
}
}
LGShaderContext.prototype.addFunction = function( name, code )
{
this._functions[name] = code;
}
LGShaderContext.prototype.addCode = function( hook, code, destinations )
{
destinations = destinations || {"":""};
for(var i in destinations)
{
var h = i ? i + "_" + hook : hook;
if(!this._codeparts[ h ])
this._codeparts[ h ] = code + "\n";
else
this._codeparts[ h ] += code + "\n";
}
}
//the system works by grabbing code fragments from every node and concatenating them in blocks depending on where must they be attached
LGShaderContext.prototype.computeCodeBlocks = function( graph, extra_uniforms )
{
//prepare context
this.clear();
//grab output nodes
var vertexout = graph.findNodesByType("shader::output/vertex");
vertexout = vertexout && vertexout.length ? vertexout[0] : null;
var fragmentout = graph.findNodesByType("shader::output/fragcolor");
fragmentout = fragmentout && fragmentout.length ? fragmentout[0] : null;
if(!fragmentout) //??
return null;
//propagate back destinations
graph.sendEventToAllNodes( "clearDestination" );
if(vertexout)
vertexout.propagateDestination("vs");
if(fragmentout)
fragmentout.propagateDestination("fs");
//gets code from graph
graph.sendEventToAllNodes("onGetCode", this );
var uniforms = "";
for(var i in this._uniforms)
uniforms += "uniform " + this._uniforms[i] + " " + i + ";\n";
if(extra_uniforms)
for(var i in extra_uniforms)
uniforms += "uniform " + extra_uniforms[i] + " " + i + ";\n";
var functions = "";
for(var i in this._functions)
functions += "//" + i + "\n" + this._functions[i] + "\n";
var blocks = this._codeparts;
blocks.uniforms = uniforms;
blocks.functions = functions;
return blocks;
}
//replaces blocks using the vs and fs template and returns the final codes
LGShaderContext.prototype.computeShaderCode = function( graph )
{
var blocks = this.computeCodeBlocks( graph );
var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, blocks );
var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, blocks );
return {
vs_code: vs_code,
fs_code: fs_code
};
}
//generates the shader code from the template and the
LGShaderContext.prototype.computeShader = function( graph, shader )
{
var finalcode = this.computeShaderCode( graph );
console.log( finalcode.vs_code, finalcode.fs_code );
if(!LiteGraph.catch_exceptions)
{
this._shader_error = true;
if(shader)
shader.updateShader( finalcode.vs_code, finalcode.fs_code );
else
shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code );
this._shader_error = false;
return shader;
}
try
{
if(shader)
shader.updateShader( finalcode.vs_code, finalcode.fs_code );
else
shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code );
this._shader_error = false;
return shader;
}
catch (err)
{
if(!this._shader_error)
{
console.error(err);
if(err.indexOf("Fragment shader") != -1)
console.log( finalcode.fs_code.split("\n").map(function(v,i){ return i + ".- " + v; }).join("\n") );
else
console.log( finalcode.vs_code );
}
this._shader_error = true;
return null;
}
return null;//never here
}
LGShaderContext.prototype.getShader = function( graph )
{
//if graph not changed?
if(this._shader && this._shader._version == graph._version)
return this._shader;
//compile shader
var shader = this.computeShader( graph, this._shader );
if(!shader)
return null;
this._shader = shader;
shader._version = graph._version;
return shader;
}
//some shader nodes could require to fill the box with some uniforms
LGShaderContext.prototype.fillUniforms = function( uniforms, param )
{
if(!this._uniform_value)
return;
for(var i in this._uniform_value)
{
var v = this._uniform_value[i];
if(v == null)
continue;
if(v.constructor === Function)
uniforms[i] = v.call( this, param );
else if(v.constructor === GL.Texture)
{
//todo...
}
else
uniforms[i] = v;
}
}
LiteGraph.ShaderContext = LiteGraph.Shaders.Context = LGShaderContext;
// LGraphShaderGraph *****************************
// applies a shader graph to texture, it can be uses as an example
function LGraphShaderGraph() {
//before inputs
this.subgraph = new LiteGraph.LGraph();
this.subgraph._subgraph_node = this;
this.subgraph._is_subgraph = true;
this.subgraph.filter = "shader";
this.addInput("in", "texture");
this.addOutput("out", "texture");
this.properties = { width: 0, height: 0, alpha: false, precision: typeof(LGraphTexture) != "undefined" ? LGraphTexture.DEFAULT : 2 };
var inputNode = this.subgraph.findNodesByType("shader::input/uniform")[0];
inputNode.pos = [200,300];
var sampler = LiteGraph.createNode("shader::texture/sampler2D");
sampler.pos = [400,300];
this.subgraph.add( sampler );
var outnode = LiteGraph.createNode("shader::output/fragcolor");
outnode.pos = [600,300];
this.subgraph.add( outnode );
inputNode.connect( 0, sampler );
sampler.connect( 0, outnode );
this.size = [180,60];
this.redraw_on_mouse = true; //force redraw
this._uniforms = {};
this._shader = null;
this._context = new LGShaderContext();
this._context.vs_template = "#define VERTEX\n" + GL.Shader.SCREEN_VERTEX_SHADER;
this._context.fs_template = LGraphShaderGraph.template;
}
LGraphShaderGraph.template = "\n\
#define FRAGMENT\n\
precision highp float;\n\
varying vec2 v_coord;\n\
{{varying}}\n\
{{uniforms}}\n\
{{functions}}\n\
{{fs_functions}}\n\
void main() {\n\n\
vec2 uv = v_coord;\n\
vec4 fragcolor = vec4(0.0);\n\
vec4 fragcolor1 = vec4(0.0);\n\
{{fs_code}}\n\
gl_FragColor = fragcolor;\n\
}\n\
";
LGraphShaderGraph.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphShaderGraph.title = "ShaderGraph";
LGraphShaderGraph.desc = "Builds a shader using a graph";
LGraphShaderGraph.input_node_type = "input/uniform";
LGraphShaderGraph.output_node_type = "output/fragcolor";
LGraphShaderGraph.title_color = SHADERNODES_COLOR;
LGraphShaderGraph.prototype.onSerialize = function(o)
{
o.subgraph = this.subgraph.serialize();
}
LGraphShaderGraph.prototype.onConfigure = function(o)
{
this.subgraph.configure(o.subgraph);
}
LGraphShaderGraph.prototype.onExecute = function() {
if (!this.isOutputConnected(0))
return;
//read input texture
var intex = this.getInputData(0);
if(intex && intex.constructor != GL.Texture)
intex = null;
var w = this.properties.width | 0;
var h = this.properties.height | 0;
if (w == 0) {
w = intex ? intex.width : gl.viewport_data[2];
} //0 means default
if (h == 0) {
h = intex ? intex.height : gl.viewport_data[3];
} //0 means default
var type = LGraphTexture.getTextureType( this.properties.precision, intex );
var texture = this._texture;
if ( !texture || texture.width != w || texture.height != h || texture.type != type ) {
texture = this._texture = new GL.Texture(w, h, {
type: type,
format: this.alpha ? gl.RGBA : gl.RGB,
filter: gl.LINEAR
});
}
var shader = this.getShader( this.subgraph );
if(!shader)
return;
var uniforms = this._uniforms;
this._context.fillUniforms( uniforms );
var tex_slot = 0;
if(this.inputs)
for(var i = 0; i < this.inputs.length; ++i)
{
var input = this.inputs[i];
var data = this.getInputData(i);
if(input.type == "texture")
{
if(!data)
data = GL.Texture.getWhiteTexture();
data = data.bind(tex_slot++);
}
if(data != null)
uniforms[ "u_" + input.name ] = data;
}
var mesh = GL.Mesh.getScreenQuad();
gl.disable( gl.DEPTH_TEST );
gl.disable( gl.BLEND );
texture.drawTo(function(){
shader.uniforms( uniforms );
shader.draw( mesh );
});
//use subgraph output
this.setOutputData(0, texture );
};
//add input node inside subgraph
LGraphShaderGraph.prototype.onInputAdded = function( slot_info )
{
var subnode = LiteGraph.createNode("shader::input/uniform");
subnode.setProperty("name",slot_info.name);
subnode.setProperty("type",slot_info.type);
this.subgraph.add( subnode );
}
//remove all
LGraphShaderGraph.prototype.onInputRemoved = function( slot, slot_info )
{
var nodes = this.subgraph.findNodesByType("shader::input/uniform");
for(var i = 0; i < nodes.length; ++i)
{
var node = nodes[i];
if(node.properties.name == slot_info.name )
this.subgraph.remove( node );
}
}
LGraphShaderGraph.prototype.computeSize = function()
{
var num_inputs = this.inputs ? this.inputs.length : 0;
var num_outputs = this.outputs ? this.outputs.length : 0;
return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT + 10];
}
LGraphShaderGraph.prototype.getShader = function()
{
var shader = this._context.getShader( this.subgraph );
if(!shader)
this.boxcolor = "red";
else
this.boxcolor = null;
return shader;
}
LGraphShaderGraph.prototype.onDrawBackground = function(ctx, graphcanvas, canvas, pos)
{
if(this.flags.collapsed)
return;
//allows to preview the node if the canvas is a webgl canvas
var tex = this.getOutputData(0);
var inputs_y = this.inputs ? this.inputs.length * LiteGraph.NODE_SLOT_HEIGHT : 0;
if (tex && ctx == tex.gl && this.size[1] > inputs_y + LiteGraph.NODE_TITLE_HEIGHT ) {
ctx.drawImage( tex, 10,y, this.size[0] - 20, this.size[1] - inputs_y - LiteGraph.NODE_TITLE_HEIGHT );
}
var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5;
//button
var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT);
ctx.fillStyle = over ? "#555" : "#222";
ctx.beginPath();
if (this._shape == LiteGraph.BOX_SHAPE)
ctx.rect(0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT);
else
ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8);
ctx.fill();
//button
ctx.textAlign = "center";
ctx.font = "24px Arial";
ctx.fillStyle = over ? "#DDD" : "#999";
ctx.fillText( "+", this.size[0] * 0.5, y + 24 );
}
LGraphShaderGraph.prototype.onMouseDown = function(e, localpos, graphcanvas)
{
var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5;
if(localpos[1] > y)
{
graphcanvas.showSubgraphPropertiesDialog(this);
}
}
LGraphShaderGraph.prototype.onDrawSubgraphBackground = function(graphcanvas)
{
//TODO
}
LGraphShaderGraph.prototype.getExtraMenuOptions = function(graphcanvas)
{
var that = this;
var options = [{ content: "Print Code", callback: function(){
var code = that._context.computeShaderCode();
console.log( code.vs_code, code.fs_code );
}}];
return options;
}
LiteGraph.registerNodeType( "texture/shaderGraph", LGraphShaderGraph );
function shaderNodeFromFunction( classname, params, return_type, code )
{
//TODO
}
//Shader Nodes ***********************************************************
//applies a shader graph to a code
function LGraphShaderUniform() {
this.addOutput("out", "");
this.properties = { name: "", type: "" };
}
LGraphShaderUniform.title = "Uniform";
LGraphShaderUniform.desc = "Input data for the shader";
LGraphShaderUniform.prototype.getTitle = function()
{
if( this.properties.name && this.flags.collapsed)
return this.properties.type + " " + this.properties.name;
return "Uniform";
}
LGraphShaderUniform.prototype.onPropertyChanged = function(name,value)
{
this.outputs[0].name = this.properties.type + " " + this.properties.name;
}
LGraphShaderUniform.prototype.onGetCode = function( context )
{
if(!this.shader_destination)
return;
var type = this.properties.type;
if( !type )
{
if( !context.onGetPropertyInfo )
return;
var info = context.onGetPropertyInfo( this.property.name );
if(!info)
return;
type = info.type;
}
if(type == "number")
type = "float";
else if(type == "texture")
type = "sampler2D";
if ( LGShaders.GLSL_types.indexOf(type) == -1 )
return;
context.addUniform( "u_" + this.properties.name, type );
this.setOutputData( 0, type );
}
LGraphShaderUniform.prototype.getOutputVarName = function(slot)
{
return "u_" + this.properties.name;
}
registerShaderNode( "input/uniform", LGraphShaderUniform );
function LGraphShaderAttribute() {
this.addOutput("out", "vec2");
this.properties = { name: "coord", type: "vec2" };
}
LGraphShaderAttribute.title = "Attribute";
LGraphShaderAttribute.desc = "Input data from mesh attribute";
LGraphShaderAttribute.prototype.getTitle = function()
{
return "att. " + this.properties.name;
}
LGraphShaderAttribute.prototype.onGetCode = function( context )
{
if(!this.shader_destination)
return;
var type = this.properties.type;
if( !type || LGShaders.GLSL_types.indexOf(type) == -1 )
return;
if(type == "number")
type = "float";
if( this.properties.name != "coord")
{
context.addCode( "varying", " varying " + type +" v_" + this.properties.name + ";" );
//if( !context.varyings[ this.properties.name ] )
//context.addCode( "vs_code", "v_" + this.properties.name + " = " + input_name + ";" );
}
this.setOutputData( 0, type );
}
LGraphShaderAttribute.prototype.getOutputVarName = function(slot)
{
return "v_" + this.properties.name;
}
registerShaderNode( "input/attribute", LGraphShaderAttribute );
function LGraphShaderSampler2D() {
this.addInput("tex", "sampler2D");
this.addInput("uv", "vec2");
this.addOutput("rgba", "vec4");
this.addOutput("rgb", "vec3");
}
LGraphShaderSampler2D.title = "Sampler2D";
LGraphShaderSampler2D.desc = "Reads a pixel from a texture";
LGraphShaderSampler2D.prototype.onGetCode = function( context )
{
if(!this.shader_destination)
return;
var texname = getInputLinkID( this, 0 );
var varname = getShaderNodeVarName(this);
var code = "vec4 " + varname + " = vec4(0.0);\n";
if(texname)
{
var uvname = getInputLinkID( this, 1 ) || context.buffer_names.uvs;
code += varname + " = texture2D("+texname+","+uvname+");\n";
}
var link0 = getOutputLinkID( this, 0 );
if(link0)
code += "vec4 " + getOutputLinkID( this, 0 ) + " = "+varname+";\n";
var link1 = getOutputLinkID( this, 1 );
if(link1)
code += "vec3 " + getOutputLinkID( this, 1 ) + " = "+varname+".xyz;\n";
context.addCode( "code", code, this.shader_destination );
this.setOutputData( 0, "vec4" );
this.setOutputData( 1, "vec3" );
}
registerShaderNode( "texture/sampler2D", LGraphShaderSampler2D );
//*********************************
function LGraphShaderConstant()
{
this.addOutput("","float");
this.properties = {
type: "float",
value: 0
};
this.addWidget("combo","type","float",null, { values: GLSL_types_const, property: "type" } );
this.updateWidgets();
}
LGraphShaderConstant.title = "const";
LGraphShaderConstant.prototype.getTitle = function()
{
if(this.flags.collapsed)
return valueToGLSL( this.properties.value, this.properties.type, 2 );
return "Const";
}
LGraphShaderConstant.prototype.onPropertyChanged = function(name,value)
{
var that = this;
if(name == "type")
{
if(this.outputs[0].type != value)
{
this.disconnectOutput(0);
this.outputs[0].type = value;
}
this.widgets.length = 1; //remove extra widgets
this.updateWidgets();
}
if(name == "value")
{
if(!value.length)
this.widgets[1].value = value;
else
{
this.widgets[1].value = value[1];
if(value.length > 2)
this.widgets[2].value = value[2];
if(value.length > 3)
this.widgets[3].value = value[3];
}
}
}
LGraphShaderConstant.prototype.updateWidgets = function( old_value )
{
var that = this;
var old_value = this.properties.value;
var options = { step: 0.01 };
switch(this.properties.type)
{
case 'float':
this.properties.value = 0;
this.addWidget("number","v",0,{ step:0.01, property: "value" });
break;
case 'vec2':
this.properties.value = old_value && old_value.length == 2 ? [old_value[0],old_value[1]] : [0,0,0];
this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options);
this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options);
break;
case 'vec3':
this.properties.value = old_value && old_value.length == 3 ? [old_value[0],old_value[1],old_value[2]] : [0,0,0];
this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options);
this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options);
this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options);
break;
case 'vec4':
this.properties.value = old_value && old_value.length == 4 ? [old_value[0],old_value[1],old_value[2],old_value[3]] : [0,0,0,0];
this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options);
this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options);
this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options);
this.addWidget("number","w",this.properties.value[3], function(v){ that.properties.value[3] = v; },options);
break;
default:
console.error("unknown type for constant");
}
}
LGraphShaderConstant.prototype.onGetCode = function( context )
{
if(!this.shader_destination)
return;
var value = valueToGLSL( this.properties.value, this.properties.type );
var link_name = getOutputLinkID(this,0);
if(!link_name) //not connected
return;
var code = " " + this.properties.type + " " + link_name + " = " + value + ";";
context.addCode( "code", code, this.shader_destination );
this.setOutputData( 0, this.properties.type );
}
registerShaderNode( "const/const", LGraphShaderConstant );
function LGraphShaderVec2()
{
this.addInput("xy","vec2");
this.addInput("x","float");
this.addInput("y","float");
this.addOutput("xy","vec2");
this.addOutput("x","float");
this.addOutput("y","float");
this.properties = { x: 0, y: 0 };
}
LGraphShaderVec2.title = "vec2";
LGraphShaderVec2.varmodes = ["xy","x","y"];
LGraphShaderVec2.prototype.onPropertyChanged = function()
{
if(this.graph)
this.graph._version++;
}
LGraphShaderVec2.prototype.onGetCode = function( context )
{
if(!this.shader_destination)
return;
var props = this.properties;
var varname = getShaderNodeVarName(this);
var code = " vec2 " + varname + " = " + valueToGLSL([props.x,props.y]) + ";\n";
for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i)
{
var varmode = LGraphShaderVec2.varmodes[i];
var inlink = getInputLinkID(this,i);
if(!inlink)
continue;
code += " " + varname + "."+varmode+" = " + inlink + ";\n";
}
for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i)
{
var varmode = LGraphShaderVec2.varmodes[i];
var outlink = getOutputLinkID(this,i);
if(!outlink)
continue;
var type = GLSL_types_const[varmode.length - 1];
code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n";
this.setOutputData( i, type );
}
context.addCode( "code", code, this.shader_destination );
}
registerShaderNode( "const/vec2", LGraphShaderVec2 );
function LGraphShaderVec3()
{
this.addInput("xyz","vec3");
this.addInput("x","float");
this.addInput("y","float");
this.addInput("z","float");
this.addInput("xy","vec2");
this.addInput("xz","vec2");
this.addInput("yz","vec2");
this.addOutput("xyz","vec3");
this.addOutput("x","float");
this.addOutput("y","float");
this.addOutput("z","float");
this.addOutput("xy","vec2");
this.addOutput("xz","vec2");
this.addOutput("yz","vec2");
this.properties = { x:0, y: 0, z: 0 };
}
LGraphShaderVec3.title = "vec3";
LGraphShaderVec3.varmodes = ["xyz","x","y","z","xy","xz","yz"];
LGraphShaderVec3.prototype.onPropertyChanged = function()
{
if(this.graph)
this.graph._version++;
}
LGraphShaderVec3.prototype.onGetCode = function( context )
{
if(!this.shader_destination)
return;
var props = this.properties;
var varname = getShaderNodeVarName(this);
var code = "vec3 " + varname + " = " + valueToGLSL([props.x,props.y,props.z]) + ";\n";
for(var i = 0;i < LGraphShaderVec3.varmodes.length; ++i)
{
var varmode = LGraphShaderVec3.varmodes[i];
var inlink = getInputLinkID(this,i);
if(!inlink)
continue;
code += " " + varname + "."+varmode+" = " + inlink + ";\n";
}
for(var i = 0; i < LGraphShaderVec3.varmodes.length; ++i)
{
var varmode = LGraphShaderVec3.varmodes[i];
var outlink = getOutputLinkID(this,i);
if(!outlink)
continue;
var type = GLSL_types_const[varmode.length - 1];
code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n";
this.setOutputData( i, type );
}
context.addCode( "code", code, this.shader_destination );
}
registerShaderNode( "const/vec3", LGraphShaderVec3 );
function LGraphShaderVec4()
{
this.addInput("xyzw","vec4");
this.addInput("xyz","vec3");
this.addInput("x","float");
this.addInput("y","float");
this.addInput("z","float");
this.addInput("w","float");
this.addInput("xy","vec2");
this.addInput("yz","vec2");
this.addInput("zw","vec2");
this.addOutput("xyzw","vec4");
this.addOutput("xyz","vec3");
this.addOutput("x","float");
this.addOutput("y","float");
this.addOutput("z","float");
this.addOutput("xy","vec2");
this.addOutput("yz","vec2");
this.addOutput("zw","vec2");
this.properties = { x:0, y: 0, z: 0, w: 0 };
}
LGraphShaderVec4.title = "vec4";
LGraphShaderVec4.varmodes = ["xyzw","xyz","x","y","z","w","xy","yz","zw"];
LGraphShaderVec4.prototype.onPropertyChanged = function()
{
if(this.graph)
this.graph._version++;
}
LGraphShaderVec4.prototype.onGetCode = function( context )
{
if(!this.shader_destination)
return;
var props = this.properties;
var varname = getShaderNodeVarName(this);
var code = "vec4 " + varname + " = " + valueToGLSL([props.x,props.y,props.z,props.w]) + ";\n";
for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i)
{
var varmode = LGraphShaderVec4.varmodes[i];
var inlink = getInputLinkID(this,i);
if(!inlink)
continue;
code += " " + varname + "."+varmode+" = " + inlink + ";\n";
}
for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i)
{
var varmode = LGraphShaderVec4.varmodes[i];
var outlink = getOutputLinkID(this,i);
if(!outlink)
continue;
var type = GLSL_types_const[varmode.length - 1];
code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n";
this.setOutputData( i, type );
}
context.addCode( "code", code, this.shader_destination );
}
registerShaderNode( "const/vec4", LGraphShaderVec4 );
//*********************************
function LGraphShaderFragColor() {
this.addInput("color", LGShaders.ALL_TYPES );
this.block_delete = true;
}
LGraphShaderFragColor.title = "FragColor";
LGraphShaderFragColor.desc = "Pixel final color";
LGraphShaderFragColor.prototype.onGetCode = function( context )
{
var link_name = getInputLinkID( this, 0 );
if(!link_name)
return;
var type = this.getInputData(0);
var code = varToTypeGLSL( link_name, type, "vec4" );
context.addCode("fs_code", "fragcolor = " + code + ";");
}
registerShaderNode( "output/fragcolor", LGraphShaderFragColor );
/*
function LGraphShaderDiscard()
{
this.addInput("v","T");
this.addInput("min","T");
this.properties = { min_value: 0.0 };
this.addWidget("number","min",0,{ step: 0.01, property: "min_value" });
}
LGraphShaderDiscard.title = "Discard";
LGraphShaderDiscard.prototype.onGetCode = function( context )
{
if(!this.isOutputConnected(0))
return;
var inlink = getInputLinkID(this,0);
var inlink1 = getInputLinkID(this,1);
if(!inlink && !inlink1) //not connected
return;
context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination );
this.setOutputData( 0, return_type );
}
registerShaderNode( "output/discard", LGraphShaderDiscard );
*/
// *************************************************
function LGraphShaderOperation()
{
this.addInput("A", LGShaders.ALL_TYPES );
this.addInput("B", LGShaders.ALL_TYPES );
this.addOutput("out","");
this.properties = {
operation: "*"
};
this.addWidget("combo","op.",this.properties.operation,{ property: "operation", values: LGraphShaderOperation.operations });
}
LGraphShaderOperation.title = "Operation";
LGraphShaderOperation.operations = ["+","-","*","/"];
LGraphShaderOperation.prototype.getTitle = function()
{
if(this.flags.collapsed)
return "A" + this.properties.operation + "B";
else
return "Operation";
}
LGraphShaderOperation.prototype.onGetCode = function( context )
{
if(!this.shader_destination)
return;
if(!this.isOutputConnected(0))
return;
var inlinks = [];
for(var i = 0; i < 3; ++i)
inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } );
var outlink = getOutputLinkID(this,0);
if(!outlink) //not connected
return;
//func_desc
var base_type = inlinks[0].type;
var return_type = base_type;
var op = this.properties.operation;
var params = [];
for(var i = 0; i < 2; ++i)
{
var param_code = inlinks[i].name;
if(param_code == null) //not plugged
{
param_code = p.value != null ? p.value : "(1.0)";
inlinks[i].type = "float";
}
//convert
if( inlinks[i].type != base_type )
{
if( inlinks[i].type == "float" && (op == "*" || op == "/") )
{
//I find hard to create the opposite condition now, so I prefeer an else
}
else
param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type );
}
params.push( param_code );
}
context.addCode("code", return_type + " " + outlink + " = "+ params[0] + op + params[1] + ";", this.shader_destination );
this.setOutputData( 0, return_type );
}
registerShaderNode( "math/operation", LGraphShaderOperation );
function LGraphShaderFunc()
{
this.addInput("A", LGShaders.ALL_TYPES );
this.addInput("B", LGShaders.ALL_TYPES );
this.addOutput("out","");
this.properties = {
func: "floor"
};
this._current = "floor";
this.addWidget("combo","func",this.properties.func,{ property: "func", values: GLSL_functions_name });
}
LGraphShaderFunc.title = "Func";
LGraphShaderFunc.prototype.onPropertyChanged = function(name,value)
{
if(this.graph)
this.graph._version++;
if(name == "func")
{
var func_desc = GLSL_functions[ value ];
if(!func_desc)
return;
//remove extra inputs
for(var i = func_desc.params.length; i < this.inputs.length; ++i)
this.removeInput(i);
//add and update inputs
for(var i = 0; i < func_desc.params.length; ++i)
{
var p = func_desc.params[i];
if( this.inputs[i] )
this.inputs[i].name = p.name + (p.value ? " (" + p.value + ")" : "");
else
this.addInput( p.name, LGShaders.ALL_TYPES );
}
}
}
LGraphShaderFunc.prototype.getTitle = function()
{
if(this.flags.collapsed)
return this.properties.func;
else
return "Func";
}
LGraphShaderFunc.prototype.onGetCode = function( context )
{
if(!this.shader_destination)
return;
if(!this.isOutputConnected(0))
return;
var inlinks = [];
for(var i = 0; i < 3; ++i)
inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } );
var outlink = getOutputLinkID(this,0);
if(!outlink) //not connected
return;
var func_desc = GLSL_functions[ this.properties.func ];
if(!func_desc)
return;
//func_desc
var base_type = inlinks[0].type;
var return_type = func_desc.return_type;
if( return_type == "T" )
return_type = base_type;
var params = [];
for(var i = 0; i < func_desc.params.length; ++i)
{
var p = func_desc.params[i];
var param_code = inlinks[i].name;
if(param_code == null) //not plugged
{
param_code = p.value != null ? p.value : "(1.0)";
inlinks[i].type = "float";
}
if( (p.type == "T" && inlinks[i].type != base_type) ||
(p.type != "T" && inlinks[i].type != base_type) )
param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type );
params.push( param_code );
}
context.addFunction("round","float round(float v){ return floor(v+0.5); }\nvec2 round(vec2 v){ return floor(v+vec2(0.5));}\nvec3 round(vec3 v){ return floor(v+vec3(0.5));}\nvec4 round(vec4 v){ return floor(v+vec4(0.5)); }\n");
context.addCode("code", return_type + " " + outlink + " = "+func_desc.func+"("+params.join(",")+");", this.shader_destination );
this.setOutputData( 0, return_type );
}
registerShaderNode( "math/func", LGraphShaderFunc );
function LGraphShaderSnippet()
{
this.addInput("A", LGShaders.ALL_TYPES );
this.addInput("B", LGShaders.ALL_TYPES );
this.addOutput("C","vec4");
this.properties = {
code:"C = A+B",
type: "vec4"
}
this.addWidget("text","code",this.properties.code,{ property: "code" });
this.addWidget("combo","type",this.properties.type,{ values:["float","vec2","vec3","vec4"], property: "type" });
}
LGraphShaderSnippet.title = "Snippet";
LGraphShaderSnippet.prototype.onPropertyChanged = function(name,value)
{
if(this.graph)
this.graph._version++;
if(name == "type"&& this.outputs[0].type != value)
{
this.disconnectOutput(0);
this.outputs[0].type = value;
}
}
LGraphShaderSnippet.prototype.getTitle = function()
{
if(this.flags.collapsed)
return this.properties.code;
else
return "Snippet";
}
LGraphShaderSnippet.prototype.onGetCode = function( context )
{
if(!this.shader_destination || !this.isOutputConnected(0))
return;
var inlinkA = getInputLinkID(this,0);
if(!inlinkA)
inlinkA = "1.0";
var inlinkB = getInputLinkID(this,1);
if(!inlinkB)
inlinkB = "1.0";
var outlink = getOutputLinkID(this,0);
if(!outlink) //not connected
return;
var inA_type = this.getInputData(0) || "float";
var inB_type = this.getInputData(1) || "float";
var return_type = this.properties.type;
//cannot resolve input
if(inA_type == "T" || inB_type == "T")
{
return null;
}
var funcname = "funcSnippet" + this.id;
var func_code = "\n" + return_type + " " + funcname + "( " + inA_type + " A, " + inB_type + " B) {\n";
func_code += " " + return_type + " C = " + return_type + "(0.0);\n";
func_code += " " + this.properties.code + ";\n";
func_code += " return C;\n}\n";
context.addCode("functions", func_code, this.shader_destination );
context.addCode("code", return_type + " " + outlink + " = "+funcname+"("+inlinkA+","+inlinkB+");", this.shader_destination );
this.setOutputData( 0, return_type );
}
registerShaderNode( "utils/snippet", LGraphShaderSnippet );
//************************************
function LGraphShaderRand()
{
this.addOutput("out","float");
}
LGraphShaderRand.title = "Rand";
LGraphShaderRand.prototype.onGetCode = function( context )
{
if(!this.shader_destination || !this.isOutputConnected(0))
return;
var outlink = getOutputLinkID(this,0);
context.addUniform( "u_rand" + this.id, "float", function(){ return Math.random(); });
context.addCode("code", "float " + outlink + " = u_rand" + this.id +";", this.shader_destination );
this.setOutputData( 0, "float" );
}
registerShaderNode( "input/rand", LGraphShaderRand );
//noise
//https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83
function LGraphShaderNoise()
{
this.addInput("out", LGShaders.ALL_TYPES );
this.addInput("scale", "float" );
this.addOutput("out","float");
this.properties = {
type: "noise",
scale: 1
};
this.addWidget("combo","type", this.properties.type, { property: "type", values: LGraphShaderNoise.NOISE_TYPES });
this.addWidget("number","scale", this.properties.scale, { property: "scale" });
}
LGraphShaderNoise.NOISE_TYPES = ["noise","rand"];
LGraphShaderNoise.title = "noise";
LGraphShaderNoise.prototype.onGetCode = function( context )
{
if(!this.shader_destination || !this.isOutputConnected(0))
return;
var inlink = getInputLinkID(this,0);
var outlink = getOutputLinkID(this,0);
var intype = this.getInputData(0);
if(!inlink)
{
intype = "vec2";
inlink = context.buffer_names.uvs;
}
context.addFunction("noise",LGraphShaderNoise.shader_functions);
context.addUniform( "u_noise_scale" + this.id, "float", this.properties.scale );
if( intype == "float" )
context.addCode("code", "float " + outlink + " = snoise( vec2(" + inlink +") * u_noise_scale" + this.id +");", this.shader_destination );
else if( intype == "vec2" || intype == "vec3" )
context.addCode("code", "float " + outlink + " = snoise(" + inlink +" * u_noise_scale" + this.id +");", this.shader_destination );
else if( intype == "vec4" )
context.addCode("code", "float " + outlink + " = snoise(" + inlink +".xyz * u_noise_scale" + this.id +");", this.shader_destination );
this.setOutputData( 0, "float" );
}
registerShaderNode( "math/noise", LGraphShaderNoise );
LGraphShaderNoise.shader_functions = "\n\
vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\n\
\n\
float snoise(vec2 v){\n\
const vec4 C = vec4(0.211324865405187, 0.366025403784439,-0.577350269189626, 0.024390243902439);\n\
vec2 i = floor(v + dot(v, C.yy) );\n\
vec2 x0 = v - i + dot(i, C.xx);\n\
vec2 i1;\n\
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n\
vec4 x12 = x0.xyxy + C.xxzz;\n\
x12.xy -= i1;\n\
i = mod(i, 289.0);\n\
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))\n\
+ i.x + vec3(0.0, i1.x, 1.0 ));\n\
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),dot(x12.zw,x12.zw)), 0.0);\n\
m = m*m ;\n\
m = m*m ;\n\
vec3 x = 2.0 * fract(p * C.www) - 1.0;\n\
vec3 h = abs(x) - 0.5;\n\
vec3 ox = floor(x + 0.5);\n\
vec3 a0 = x - ox;\n\
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );\n\
vec3 g;\n\
g.x = a0.x * x0.x + h.x * x0.y;\n\
g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n\
return 130.0 * dot(m, g);\n\
}\n\
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\n\
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\n\
\n\
float snoise(vec3 v){ \n\
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;\n\
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n\
\n\
// First corner\n\
vec3 i = floor(v + dot(v, C.yyy) );\n\
vec3 x0 = v - i + dot(i, C.xxx) ;\n\
\n\
// Other corners\n\
vec3 g = step(x0.yzx, x0.xyz);\n\
vec3 l = 1.0 - g;\n\
vec3 i1 = min( g.xyz, l.zxy );\n\
vec3 i2 = max( g.xyz, l.zxy );\n\
\n\
// x0 = x0 - 0. + 0.0 * C \n\
vec3 x1 = x0 - i1 + 1.0 * C.xxx;\n\
vec3 x2 = x0 - i2 + 2.0 * C.xxx;\n\
vec3 x3 = x0 - 1. + 3.0 * C.xxx;\n\
\n\
// Permutations\n\
i = mod(i, 289.0 ); \n\
vec4 p = permute( permute( permute( \n\
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))\n\
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 )) \n\
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));\n\
\n\
// Gradients\n\
// ( N*N points uniformly over a square, mapped onto an octahedron.)\n\
float n_ = 1.0/7.0; // N=7\n\
vec3 ns = n_ * D.wyz - D.xzx;\n\
\n\
vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N)\n\
\n\
vec4 x_ = floor(j * ns.z);\n\
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)\n\
\n\
vec4 x = x_ *ns.x + ns.yyyy;\n\
vec4 y = y_ *ns.x + ns.yyyy;\n\
vec4 h = 1.0 - abs(x) - abs(y);\n\
\n\
vec4 b0 = vec4( x.xy, y.xy );\n\
vec4 b1 = vec4( x.zw, y.zw );\n\
\n\
vec4 s0 = floor(b0)*2.0 + 1.0;\n\
vec4 s1 = floor(b1)*2.0 + 1.0;\n\
vec4 sh = -step(h, vec4(0.0));\n\
\n\
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;\n\
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;\n\
\n\
vec3 p0 = vec3(a0.xy,h.x);\n\
vec3 p1 = vec3(a0.zw,h.y);\n\
vec3 p2 = vec3(a1.xy,h.z);\n\
vec3 p3 = vec3(a1.zw,h.w);\n\
\n\
//Normalise gradients\n\
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n\
p0 *= norm.x;\n\
p1 *= norm.y;\n\
p2 *= norm.z;\n\
p3 *= norm.w;\n\
\n\
// Mix final noise value\n\
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n\
m = m * m;\n\
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),dot(p2,x2), dot(p3,x3) ) );\n\
}\n\
\n\
vec3 hash3( vec2 p ){\n\
vec3 q = vec3( dot(p,vec2(127.1,311.7)), \n\
dot(p,vec2(269.5,183.3)), \n\
dot(p,vec2(419.2,371.9)) );\n\
return fract(sin(q)*43758.5453);\n\
}\n\
vec4 hash4( vec3 p ){\n\
vec4 q = vec4( dot(p,vec3(127.1,311.7,257.3)), \n\
dot(p,vec3(269.5,183.3,335.1)), \n\
dot(p,vec3(314.5,235.1,467.3)), \n\
dot(p,vec3(419.2,371.9,114.9)) );\n\
return fract(sin(q)*43758.5453);\n\
}\n\
\n\
float iqnoise( in vec2 x, float u, float v ){\n\
vec2 p = floor(x);\n\
vec2 f = fract(x);\n\
\n\
float k = 1.0+63.0*pow(1.0-v,4.0);\n\
\n\
float va = 0.0;\n\
float wt = 0.0;\n\
for( int j=-2; j<=2; j++ )\n\
for( int i=-2; i<=2; i++ )\n\
{\n\
vec2 g = vec2( float(i),float(j) );\n\
vec3 o = hash3( p + g )*vec3(u,u,1.0);\n\
vec2 r = g - f + o.xy;\n\
float d = dot(r,r);\n\
float ww = pow( 1.0-smoothstep(0.0,1.414,sqrt(d)), k );\n\
va += o.z*ww;\n\
wt += ww;\n\
}\n\
\n\
return va/wt;\n\
}\n\
"
function LGraphShaderTime()
{
this.addOutput("out","float");
}
LGraphShaderTime.title = "Time";
LGraphShaderTime.prototype.onGetCode = function( context )
{
if(!this.shader_destination || !this.isOutputConnected(0))
return;
var outlink = getOutputLinkID(this,0);
context.addUniform( "u_time" + this.id, "float", function(){ return getTime() * 0.001; });
context.addCode("code", "float " + outlink + " = u_time" + this.id +";", this.shader_destination );
this.setOutputData( 0, "float" );
}
registerShaderNode( "input/time", LGraphShaderTime );
function LGraphShaderDither()
{
this.addInput("in","T");
this.addOutput("out","float");
}
LGraphShaderDither.title = "Dither";
LGraphShaderDither.prototype.onGetCode = function( context )
{
if(!this.shader_destination || !this.isOutputConnected(0))
return;
var inlink = getInputLinkID(this,0);
var return_type = "float";
var outlink = getOutputLinkID(this,0);
var intype = this.getInputData(0);
inlink = varToTypeGLSL( inlink, intype, "float" );
context.addFunction("dither8x8", LGraphShaderDither.dither_func);
context.addCode("code", return_type + " " + outlink + " = dither8x8("+ inlink +");", this.shader_destination );
this.setOutputData( 0, return_type );
}
LGraphShaderDither.dither_values = [0.515625,0.140625,0.640625,0.046875,0.546875,0.171875,0.671875,0.765625,0.265625,0.890625,0.390625,0.796875,0.296875,0.921875,0.421875,0.203125,0.703125,0.078125,0.578125,0.234375,0.734375,0.109375,0.609375,0.953125,0.453125,0.828125,0.328125,0.984375,0.484375,0.859375,0.359375,0.0625,0.5625,0.1875,0.6875,0.03125,0.53125,0.15625,0.65625,0.8125,0.3125,0.9375,0.4375,0.78125,0.28125,0.90625,0.40625,0.25,0.75,0.125,0.625,0.21875,0.71875,0.09375,0.59375,1.0001,0.5,0.875,0.375,0.96875,0.46875,0.84375,0.34375];
LGraphShaderDither.dither_func = "\n\
float dither8x8(float brightness) {\n\
vec2 position = vec2(0.0);\n\
#ifdef FRAGMENT\n\
position = gl_FragCoord.xy;\n\
#endif\n\
int x = int(mod(position.x, 8.0));\n\
int y = int(mod(position.y, 8.0));\n\
int index = x + y * 8;\n\
float limit = 0.0;\n\
if (x < 8) {\n\
if(index==0) limit = 0.015625;\n\
"+(LGraphShaderDither.dither_values.map( function(v,i){ return "else if(index== "+(i+1)+") limit = " + v + ";"}).join("\n"))+"\n\
}\n\
return brightness < limit ? 0.0 : 1.0;\n\
}\n",
registerShaderNode( "math/dither", LGraphShaderDither );
function LGraphShaderRemap()
{
this.addInput("", LGShaders.ALL_TYPES );
this.addOutput("","");
this.properties = {
min_value: 0,
max_value: 1,
min_value2: 0,
max_value2: 1
};
this.addWidget("number","min",0,{ step: 0.1, property: "min_value" });
this.addWidget("number","max",1,{ step: 0.1, property: "max_value" });
this.addWidget("number","min2",0,{ step: 0.1, property: "min_value2"});
this.addWidget("number","max2",1,{ step: 0.1, property: "max_value2"});
}
LGraphShaderRemap.title = "Remap";
LGraphShaderRemap.prototype.onPropertyChanged = function()
{
if(this.graph)
this.graph._version++;
}
LGraphShaderRemap.prototype.onConnectionsChange = function()
{
var return_type = this.getInputDataType(0);
this.outputs[0].type = return_type || "T";
}
LGraphShaderRemap.prototype.onGetCode = function( context )
{
if(!this.shader_destination || !this.isOutputConnected(0))
return;
var inlink = getInputLinkID(this,0);
var outlink = getOutputLinkID(this,0);
if(!inlink && !outlink) //not connected
return;
var return_type = this.getInputDataType(0);
this.outputs[0].type = return_type;
if(return_type == "T")
{
console.warn("node type is T and cannot be resolved");
return;
}
if(!inlink)
{
context.addCode("code"," " + return_type + " " + outlink + " = " + return_type + "(0.0);\n");
return;
}
var minv = valueToGLSL( this.properties.min_value );
var maxv = valueToGLSL( this.properties.max_value );
var minv2 = valueToGLSL( this.properties.min_value2 );
var maxv2 = valueToGLSL( this.properties.max_value2 );
context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination );
this.setOutputData( 0, return_type );
}
registerShaderNode( "math/remap", LGraphShaderRemap );
})(this);