(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);