提交 7d8278e0 编写于 作者: S sunag 提交者: Mr.doob

nodematerial r6 (#9636)

* add standard define

* fix auto set transparent

* add indirectSpecular and fix premultiplied_alpha, tonemapping, encodings, fog

* fix premultiplied_alpha, tonemapping, encodings, fog

* fix spherical reflection

* fix spherical reflection

* nodematerial r6

* added triangle-blur and custom-attribute examples

* fix reflection using in color slot

* support to expression and reserved-keywords

* int <-> float support

* fix possible initial space in eval

* description how to add global const or function

* bump map support

* sampleCube and slotId

* gammaInput

* add bump and spherical-reflection

* reduces and optimize NodeLib

* blur example, revision and cleanup

* fix normal-map offset

* cleanup

* up comments

* fix cache texture

* fix cache
上级 6280a452
/**
* @author sunag / http://www.sunag.com.br/
*/
THREE.AttributeNode = function( name, type ) {
THREE.InputNode.call( this, type, { shared: false } );
this.name = name;
};
THREE.AttributeNode.prototype = Object.create( THREE.InputNode.prototype );
THREE.AttributeNode.prototype.constructor = THREE.AttributeNode;
THREE.AttributeNode.prototype.getAttributeType = function( builder ) {
return typeof this.type === 'number' ? builder.getConstructorFromLength( this.type ) : this.type;
};
THREE.AttributeNode.prototype.getType = function( builder ) {
var type = this.getAttributeType( builder );
return builder.getTypeByFormat( type );
};
THREE.AttributeNode.prototype.generate = function( builder, output ) {
var type = this.getAttributeType( builder );
var attribute = builder.material.getAttribute( this.name, type );
return builder.format( attribute.varName, this.getType( builder ), output );
};
......@@ -2,11 +2,11 @@
* @author sunag / http://www.sunag.com.br/
*/
THREE.ConstNode = function( name, useDefine ) {
THREE.ConstNode = function( src, useDefine ) {
THREE.TempNode.call( this );
this.parse( name || THREE.ConstNode.PI, useDefine );
this.eval( src || THREE.ConstNode.PI, useDefine );
};
......@@ -20,31 +20,30 @@ THREE.ConstNode.EPSILON = 'EPSILON';
THREE.ConstNode.prototype = Object.create( THREE.TempNode.prototype );
THREE.ConstNode.prototype.constructor = THREE.ConstNode;
THREE.ConstNode.prototype.parse = function( src, useDefine ) {
THREE.ConstNode.prototype.getType = function( builder ) {
var name, type;
return builder.getTypeByFormat( this.type );
var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=(.*?)\;/i;
var match = src.match( rDeclaration );
};
if ( match && match.length > 1 ) {
THREE.ConstNode.prototype.eval = function( src, useDefine ) {
type = match[ 1 ];
name = match[ 2 ];
src = ( src || '' ).trim();
if ( useDefine ) {
var name, type, value;
this.src = '#define ' + name + ' ' + match[ 3 ];
var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=?\s?(.*?)(\;|$)/i;
var match = src.match( rDeclaration );
}
else {
this.useDefine = useDefine;
this.src = 'const ' + type + ' ' + name + ' = ' + match[ 3 ] + ';';
if ( match && match.length > 1 ) {
}
type = match[ 1 ];
name = match[ 2 ];
value = match[ 3 ];
}
else {
} else {
name = src;
type = 'fv1';
......@@ -53,6 +52,33 @@ THREE.ConstNode.prototype.parse = function( src, useDefine ) {
this.name = name;
this.type = type;
this.value = value;
};
THREE.ConstNode.prototype.build = function( builder, output ) {
if ( output === 'source' ) {
if ( this.value ) {
if ( this.useDefine ) {
return '#define ' + this.name + ' ' + this.value;
}
return 'const ' + this.type + ' ' + this.name + ' = ' + this.value + ';';
}
} else {
builder.include( this );
return builder.format( this.name, this.getType( builder ), output );
}
};
......
......@@ -2,21 +2,21 @@
* @author sunag / http://www.sunag.com.br/
*/
THREE.FunctionCallNode = function( value ) {
THREE.FunctionCallNode = function( func, inputs ) {
THREE.TempNode.call( this );
this.setFunction( value );
this.setFunction( func, inputs );
};
THREE.FunctionCallNode.prototype = Object.create( THREE.TempNode.prototype );
THREE.FunctionCallNode.prototype.constructor = THREE.FunctionCallNode;
THREE.FunctionCallNode.prototype.setFunction = function( val ) {
THREE.FunctionCallNode.prototype.setFunction = function( func, inputs ) {
this.inputs = [];
this.value = val;
this.value = func;
this.inputs = inputs || [];
};
......@@ -39,9 +39,7 @@ THREE.FunctionCallNode.prototype.generate = function( builder, output ) {
var type = this.getType( builder );
var func = this.value;
builder.include( func );
var code = func.name + '(';
var code = func.build( builder, output ) + '(';
var params = [];
for ( var i = 0; i < func.inputs.length; i ++ ) {
......
......@@ -3,37 +3,35 @@
* @thanks bhouston / https://clara.io/
*/
THREE.FunctionNode = function( src, includes, extensions ) {
THREE.FunctionNode = function( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions ) {
THREE.GLNode.call( this );
src = src || '';
this.parse( src || '', includes, extensions );
this.isMethod = typeof includesOrType !== "string";
this.useKeywords = true;
THREE.TempNode.call( this, this.isMethod ? null : includesOrType );
if ( this.isMethod ) this.eval( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions );
else this.eval( src, extensionsOrIncludes, keywordsOrExtensions );
};
THREE.FunctionNode.prototype = Object.create( THREE.GLNode.prototype );
THREE.FunctionNode.rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\((.*?)\)/i;
THREE.FunctionNode.rProperties = /[a-z_0-9]+/ig;
THREE.FunctionNode.prototype = Object.create( THREE.TempNode.prototype );
THREE.FunctionNode.prototype.constructor = THREE.FunctionNode;
THREE.FunctionNode.prototype.parseReference = function( name ) {
switch ( name ) {
case 'uv': return new THREE.UVNode().name;
case 'uv2': return new THREE.UVNode( 1 ).name;
case 'position': return new THREE.PositionNode().name;
case 'worldPosition': return new THREE.PositionNode( THREE.PositionNode.WORLD ).name;
case 'normal': return new THREE.NormalNode().name;
case 'normalPosition': return new THREE.NormalNode( THREE.NormalNode.WORLD ).name;
case 'viewPosition': return new THREE.PositionNode( THREE.NormalNode.VIEW ).name;
case 'viewNormal': return new THREE.NormalNode( THREE.NormalNode.VIEW ).name;
}
THREE.FunctionNode.prototype.isShared = function( builder, output ) {
return name;
return ! this.isMethod;
};
THREE.FunctionNode.prototype.getTypeNode = function( builder, type ) {
THREE.FunctionNode.prototype.getType = function( builder ) {
return builder.getTypeByFormat( type ) || type;
return builder.getTypeByFormat( this.type );
};
......@@ -50,13 +48,7 @@ THREE.FunctionNode.prototype.getInputByName = function( name ) {
};
THREE.FunctionNode.prototype.getType = function( builder ) {
return this.getTypeNode( builder, this.type );
};
THREE.FunctionNode.prototype.getInclude = function( name ) {
THREE.FunctionNode.prototype.getIncludeByName = function( name ) {
var i = this.includes.length;
......@@ -67,93 +59,145 @@ THREE.FunctionNode.prototype.getInclude = function( name ) {
}
return undefined;
};
THREE.FunctionNode.prototype.parse = function( src, includes, extensions ) {
THREE.FunctionNode.prototype.generate = function( builder, output ) {
var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\((.*?)\)/i;
var rProperties = /[a-z_0-9]+/ig;
var match, offset = 0, src = this.value;
this.includes = includes || [];
this.extensions = extensions || {};
while ( match = THREE.FunctionNode.rProperties.exec( this.value ) ) {
var match = src.match( rDeclaration );
var prop = match[ 0 ], isGlobal = this.isMethod ? ! this.getInputByName( prop ) : true;
var reference = prop;
this.inputs = [];
if ( this.keywords[ prop ] || ( this.useKeywords && isGlobal && THREE.NodeLib.containsKeyword( prop ) ) ) {
if ( match && match.length == 4 ) {
var node = this.keywords[ prop ];
this.type = match[ 1 ];
this.name = match[ 2 ];
if ( ! node ) {
var inputs = match[ 3 ].match( rProperties );
var keyword = THREE.NodeLib.getKeywordData( prop );
if ( inputs ) {
if ( keyword.cache ) node = builder.keywords[ prop ];
var i = 0;
node = node || THREE.NodeLib.getKeyword( prop, builder );
while ( i < inputs.length ) {
if ( keyword.cache ) builder.keywords[ prop ] = node;
var qualifier = inputs[ i ++ ];
var type, name;
}
if ( qualifier == 'in' || qualifier == 'out' || qualifier == 'inout' ) {
reference = node.build( builder );
type = inputs[ i ++ ];
}
}
else {
if ( prop != reference ) {
type = qualifier;
qualifier = '';
src = src.substring( 0, match.index + offset ) + reference + src.substring( match.index + prop.length + offset );
}
offset += reference.length - prop.length;
name = inputs[ i ++ ];
}
this.inputs.push( {
name : name,
type : type,
qualifier : qualifier
} );
if ( this.getIncludeByName( reference ) === undefined && THREE.NodeLib.contains( reference ) ) {
}
builder.include( THREE.NodeLib.get( reference ) );
}
var match, offset = 0;
}
while ( match = rProperties.exec( src ) ) {
if ( output === 'source' ) {
var prop = match[ 0 ];
var reference = this.parseReference( prop );
for ( var i = 0; i < this.includes.length; i ++ ) {
if ( prop != reference ) {
builder.include( this.includes[ i ], this );
src = src.substring( 0, match.index + offset ) + reference + src.substring( match.index + prop.length + offset );
}
offset += reference.length - prop.length;
for ( var ext in this.extensions ) {
}
builder.material.extensions[ ext ] = true;
if ( this.getInclude( reference ) === undefined && THREE.NodeLib.contains( reference ) ) {
}
this.includes.push( THREE.NodeLib.get( reference ) );
return src;
}
} else if ( this.isMethod ) {
}
builder.include( this, false, src );
return this.name;
} else {
this.src = src;
return builder.format( "(" + src + ")", this.getType( builder ), output );
}
else {
this.type = '';
this.name = '';
};
THREE.FunctionNode.prototype.eval = function( src, includes, extensions, keywords ) {
src = ( src || '' ).trim();
this.includes = includes || [];
this.extensions = extensions || {};
this.keywords = keywords || {};
if ( this.isMethod ) {
var match = src.match( THREE.FunctionNode.rDeclaration );
this.inputs = [];
if ( match && match.length == 4 ) {
this.type = match[ 1 ];
this.name = match[ 2 ];
var inputs = match[ 3 ].match( THREE.FunctionNode.rProperties );
if ( inputs ) {
var i = 0;
while ( i < inputs.length ) {
var qualifier = inputs[ i ++ ];
var type, name;
if ( qualifier == 'in' || qualifier == 'out' || qualifier == 'inout' ) {
type = inputs[ i ++ ];
} else {
type = qualifier;
qualifier = '';
}
name = inputs[ i ++ ];
this.inputs.push( {
name : name,
type : type,
qualifier : qualifier
} );
}
}
} else {
this.type = '';
this.name = '';
}
}
this.value = src;
};
......@@ -6,48 +6,54 @@ THREE.GLNode = function( type ) {
this.uuid = THREE.Math.generateUUID();
this.allow = {};
this.allows = {};
this.requestUpdate = false;
this.type = type;
};
THREE.GLNode.prototype.parse = function( builder, cache, requires ) {
THREE.GLNode.prototype.parse = function( builder, context ) {
context = context || {};
builder.parsing = true;
var material = builder.material;
this.build( builder.addCache( cache, requires ), 'v4' );
this.build( builder.addCache( context.cache, context.requires ).addSlot( context.slot ), 'v4' );
material.clearVertexNode();
material.clearFragmentNode();
builder.removeCache();
builder.removeCache().removeSlot();
builder.parsing = false;
};
THREE.GLNode.prototype.parseAndBuildCode = function( builder, output, cache, requires ) {
THREE.GLNode.prototype.parseAndBuildCode = function( builder, output, context ) {
context = context || {};
this.parse( builder, cache, requires );
this.parse( builder, context );
return this.buildCode( builder, output, cache, requires );
return this.buildCode( builder, output, context );
};
THREE.GLNode.prototype.buildCode = function( builder, output, cache, requires ) {
THREE.GLNode.prototype.buildCode = function( builder, output, context ) {
context = context || {};
var material = builder.material;
var data = { result : this.build( builder.addCache( cache, requires ), output ) };
var data = { result : this.build( builder.addCache( context.cache, context.requires ).addSlot( context.slot ), output ) };
if ( builder.isShader( 'vertex' ) ) data.code = material.clearVertexNode();
else data.code = material.clearFragmentNode();
builder.removeCache();
builder.removeCache().removeSlot();
return data;
......@@ -55,21 +61,21 @@ THREE.GLNode.prototype.buildCode = function( builder, output, cache, requires )
THREE.GLNode.prototype.build = function( builder, output, uuid ) {
var material = builder.material;
var data = material.getDataNode( uuid || this.uuid );
output = output || this.getType( builder, output );
var material = builder.material, data = material.getDataNode( uuid || this.uuid );
if ( builder.parsing ) this.appendDepsNode( builder, data, output );
if ( this.allow[ builder.shader ] === false ) {
if ( this.allows[ builder.shader ] === false ) {
throw new Error( 'Shader ' + shader + ' is not compatible with this node.' );
}
if ( this.requestUpdate && ! data.requestUpdate ) {
if ( this.requestUpdate && material.requestUpdate.indexOf( this ) === - 1 ) {
material.requestUpdate.push( this );
data.requestUpdate = true;
}
......@@ -83,7 +89,7 @@ THREE.GLNode.prototype.appendDepsNode = function( builder, data, output ) {
var outputLen = builder.getFormatLength( output );
if ( outputLen > ( data.outputMax || 0 ) || this.getType( builder ) ) {
if ( outputLen > ( data.outputMax || 0 ) || this.getType( builder, output ) ) {
data.outputMax = outputLen;
data.output = output;
......@@ -92,8 +98,8 @@ THREE.GLNode.prototype.appendDepsNode = function( builder, data, output ) {
};
THREE.GLNode.prototype.getType = function( builder ) {
THREE.GLNode.prototype.getType = function( builder, output ) {
return this.type;
return output === 'sampler2D' || output === 'samplerCube' ? output : this.type;
};
......@@ -4,6 +4,9 @@
THREE.InputNode = function( type, params ) {
params = params || {};
params.shared = params.shared !== undefined ? params.shared : false;
THREE.TempNode.call( this, type, params );
};
......
......@@ -2,21 +2,48 @@
* @author sunag / http://www.sunag.com.br/
*/
THREE.BuilderNode = function( material ) {
THREE.NodeBuilder = function( material ) {
this.material = material;
this.caches = [];
this.slots = [];
this.keywords = {};
this.parsing = false;
this.optimize = true;
this.addCache();
this.update();
};
THREE.NodeBuilder.type = {
float : 'fv1',
vec2 : 'v2',
vec3 : 'v3',
vec4 : 'v4',
mat4 : 'v4',
int : 'iv1'
};
THREE.BuilderNode.prototype = {
constructor: THREE.BuilderNode,
THREE.NodeBuilder.constructors = [
'float',
'vec2',
'vec3',
'vec4'
];
THREE.NodeBuilder.elements = [
'x',
'y',
'z',
'w'
];
THREE.NodeBuilder.prototype = {
constructor: THREE.NodeBuilder,
addCache : function( name, requires ) {
......@@ -25,7 +52,7 @@ THREE.BuilderNode.prototype = {
requires : requires || {}
} );
return this.updateCache();
return this.update();
},
......@@ -33,7 +60,25 @@ THREE.BuilderNode.prototype = {
this.caches.pop();
return this.updateCache();
return this.update();
},
addSlot : function( name ) {
this.slots.push( {
name : name || '',
} );
return this.update();
},
removeSlot : function() {
this.slots.pop();
return this.update();
},
......@@ -51,12 +96,28 @@ THREE.BuilderNode.prototype = {
},
updateCache : function() {
isSlot : function( name ) {
var i = this.slots.length;
while ( i -- ) {
if ( this.slots[ i ].name == name ) return true;
}
return false;
},
update : function() {
var cache = this.caches[ this.caches.length - 1 ];
var slot = this.slots[ this.slots.length - 1 ];
this.cache = cache.name;
this.requires = cache.requires;
this.slot = slot ? slot.name : '';
this.cache = cache ? cache.name : '';
this.requires = cache ? cache.requires : {};
return this;
......@@ -70,9 +131,9 @@ THREE.BuilderNode.prototype = {
},
include : function( func ) {
include : function( node, parent, source ) {
this.material.include( this.shader, func );
this.material.include( this, node, parent, source );
return this;
......@@ -84,15 +145,15 @@ THREE.BuilderNode.prototype = {
},
getFormatConstructor : function( len ) {
getConstructorFromLength : function( len ) {
return THREE.BuilderNode.constructors[ len - 1 ];
return THREE.NodeBuilder.constructors[ len - 1 ];
},
getFormatName : function( format ) {
return format.replace( /c/g, 'v3' ).replace( /fv1|iv1/g, 'v1' );
return format.replace( /c/g, 'v3' ).replace( /fv1/g, 'v1' ).replace( /iv1/g, 'i' );
},
......@@ -108,7 +169,7 @@ THREE.BuilderNode.prototype = {
},
getFormatByLength : function( len ) {
getFormatFromLength : function( len ) {
if ( len == 1 ) return 'fv1';
......@@ -118,24 +179,35 @@ THREE.BuilderNode.prototype = {
format : function( code, from, to ) {
var format = this.getFormatName( from + '=' + to );
var format = this.getFormatName( to + '=' + from );
switch ( format ) {
case 'v1=v2': return 'vec2(' + code + ')';
case 'v1=v3': return 'vec3(' + code + ')';
case 'v1=v4': return 'vec4(' + code + ')';
case 'v2=v1': return code + '.x';
case 'v2=v3': return 'vec3(' + code + ',0.0)';
case 'v2=v4': return 'vec4(' + code + ',0.0,1.0)';
case 'v1=v2': return code + '.x';
case 'v1=v3': return code + '.x';
case 'v1=v4': return code + '.x';
case 'v1=i': return 'float(' + code + ')';
case 'v2=v1': return 'vec2(' + code + ')';
case 'v2=v3': return code + '.xy';
case 'v2=v4': return code + '.xy';
case 'v2=i': return 'vec2(float(' + code + '))';
case 'v3=v1': return code + '.x';
case 'v3=v2': return code + '.xy';
case 'v3=v4': return 'vec4(' + code + ',1.0)';
case 'v3=v1': return 'vec3(' + code + ')';
case 'v3=v2': return 'vec3(' + code + ',0.0)';
case 'v3=v4': return code + '.xyz';
case 'v3=i': return 'vec2(float(' + code + '))';
case 'v4=v1': return 'vec4(' + code + ')';
case 'v4=v2': return 'vec4(' + code + ',0.0,1.0)';
case 'v4=v3': return 'vec4(' + code + ',1.0)';
case 'v4=i': return 'vec4(float(' + code + '))';
case 'i=v1': return 'int(' + code + ')';
case 'i=v2': return 'int(' + code + '.x)';
case 'i=v3': return 'int(' + code + '.x)';
case 'i=v4': return 'int(' + code + '.x)';
case 'v4=v1': return code + '.x';
case 'v4=v2': return code + '.xy';
case 'v4=v3': return code + '.xyz';
}
return code;
......@@ -144,7 +216,7 @@ THREE.BuilderNode.prototype = {
getTypeByFormat : function( format ) {
return THREE.BuilderNode.type[ format ];
return THREE.NodeBuilder.type[ format ] || format;
},
......@@ -160,13 +232,13 @@ THREE.BuilderNode.prototype = {
getElementByIndex : function( index ) {
return THREE.BuilderNode.elements[ index ];
return THREE.NodeBuilder.elements[ index ];
},
getIndexByElement : function( elm ) {
return THREE.BuilderNode.elements.indexOf( elm );
return THREE.NodeBuilder.elements.indexOf( elm );
},
......@@ -184,25 +256,3 @@ THREE.BuilderNode.prototype = {
}
};
THREE.BuilderNode.type = {
'float' : 'fv1',
vec2 : 'v2',
vec3 : 'v3',
vec4 : 'v4',
mat4 : 'v4'
};
THREE.BuilderNode.constructors = [
'',
'vec2',
'vec3',
'vec4'
];
THREE.BuilderNode.elements = [
'x',
'y',
'z',
'w'
];
......@@ -3,51 +3,131 @@
*/
THREE.NodeLib = {
nodes: {},
keywords: {},
add: function( node ) {
this.nodes[ node.name ] = node;
},
addKeyword: function( name, callback, cache ) {
cache = cache !== undefined ? cache : true;
this.keywords[ name ] = { callback : callback, cache : cache };
},
remove: function( node ) {
delete this.nodes[ node.name ];
},
removeKeyword: function( name ) {
delete this.keywords[ node ];
},
get: function( name ) {
return this.nodes[ name ];
},
getKeyword: function( name, material ) {
return this.keywords[ name ].callback.call( this, material );
},
getKeywordData: function( name ) {
return this.keywords[ name ];
},
contains: function( name ) {
return this.nodes[ name ] != undefined;
},
containsKeyword: function( name ) {
return this.keywords[ name ] != undefined;
}
};
//
// Luma
// Keywords
//
THREE.NodeLib.add( new THREE.ConstNode( "vec3 LUMA = vec3(0.2125, 0.7154, 0.0721);" ) );
THREE.NodeLib.addKeyword( 'uv', function() {
//
// DepthColor
//
return new THREE.UVNode();
THREE.NodeLib.add( new THREE.FunctionNode( [
"float depthcolor( float mNear, float mFar ) {",
} );
"#ifdef USE_LOGDEPTHBUF_EXT",
"float depth = gl_FragDepthEXT / gl_FragCoord.w;",
"#else",
"float depth = gl_FragCoord.z / gl_FragCoord.w;",
"#endif",
THREE.NodeLib.addKeyword( 'uv2', function() {
"return 1.0 - smoothstep( mNear, mFar, depth );",
"}"
].join( "\n" ) ) );
return new THREE.UVNode( 1 );
} );
THREE.NodeLib.addKeyword( 'position', function() {
return new THREE.PositionNode();
} );
THREE.NodeLib.addKeyword( 'worldPosition', function() {
return new THREE.PositionNode( THREE.PositionNode.WORLD );
} );
THREE.NodeLib.addKeyword( 'normal', function() {
return new THREE.NormalNode();
} );
THREE.NodeLib.addKeyword( 'worldNormal', function() {
return new THREE.NormalNode( THREE.NormalNode.WORLD );
} );
THREE.NodeLib.addKeyword( 'viewPosition', function() {
return new THREE.PositionNode( THREE.NormalNode.VIEW );
} );
THREE.NodeLib.addKeyword( 'viewNormal', function() {
return new THREE.NormalNode( THREE.NormalNode.VIEW );
} );
THREE.NodeLib.addKeyword( 'time', function() {
return new THREE.TimerNode();
} );
//
// Luma
//
THREE.NodeLib.add( new THREE.ConstNode( "vec3 LUMA vec3(0.2125, 0.7154, 0.0721)" ) );
//
// NormalMap
......@@ -57,17 +137,17 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
// Per-Pixel Tangent Space Normal Mapping
// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
"vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 map, vec2 mUv, vec2 scale ) {",
"vec3 q0 = dFdx( eye_pos );",
"vec3 q1 = dFdy( eye_pos );",
"vec2 st0 = dFdx( mUv.st );",
"vec2 st1 = dFdy( mUv.st );",
"vec3 S = normalize( q0 * st1.t - q1 * st0.t );",
"vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
"vec3 N = normalize( surf_norm );",
"vec3 mapN = map * 2.0 - 1.0;",
"mapN.xy = scale * mapN.xy;",
"mat3 tsn = mat3( S, T, N );",
"return normalize( tsn * mapN );",
" vec3 q0 = dFdx( eye_pos );",
" vec3 q1 = dFdy( eye_pos );",
" vec2 st0 = dFdx( mUv.st );",
" vec2 st1 = dFdy( mUv.st );",
" vec3 S = normalize( q0 * st1.t - q1 * st0.t );",
" vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
" vec3 N = normalize( surf_norm );",
" vec3 mapN = map * 2.0 - 1.0;",
" mapN.xy = scale * mapN.xy;",
" mat3 tsn = mat3( S, T, N );",
" return normalize( tsn * mapN );",
"}"
].join( "\n" ), null, { derivatives: true } ) );
......@@ -77,7 +157,7 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
THREE.NodeLib.add( new THREE.FunctionNode( [
"float snoise(vec2 co) {",
"return fract( sin( dot(co.xy, vec2(12.9898,78.233) ) ) * 43758.5453 );",
" return fract( sin( dot(co.xy, vec2(12.9898,78.233) ) ) * 43758.5453 );",
"}"
].join( "\n" ) ) );
......@@ -87,12 +167,12 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
THREE.NodeLib.add( new THREE.FunctionNode( [
"vec3 hue_rgb(vec3 rgb, float adjustment) {",
"const mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);",
"const mat3 YIQtoRGB = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.107, 1.7046);",
"vec3 yiq = RGBtoYIQ * rgb;",
"float hue = atan(yiq.z, yiq.y) + adjustment;",
"float chroma = sqrt(yiq.z * yiq.z + yiq.y * yiq.y);",
"return YIQtoRGB * vec3(yiq.x, chroma * cos(hue), chroma * sin(hue));",
" const mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);",
" const mat3 YIQtoRGB = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.107, 1.7046);",
" vec3 yiq = RGBtoYIQ * rgb;",
" float hue = atan(yiq.z, yiq.y) + adjustment;",
" float chroma = sqrt(yiq.z * yiq.z + yiq.y * yiq.y);",
" return YIQtoRGB * vec3(yiq.x, chroma * cos(hue), chroma * sin(hue));",
"}"
].join( "\n" ) ) );
......@@ -103,8 +183,8 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
THREE.NodeLib.add( new THREE.FunctionNode( [
// Algorithm from Chapter 16 of OpenGL Shading Language
"vec3 saturation_rgb(vec3 rgb, float adjustment) {",
"vec3 intensity = vec3(dot(rgb, LUMA));",
"return mix(intensity, rgb, adjustment);",
" vec3 intensity = vec3(dot(rgb, LUMA));",
" return mix(intensity, rgb, adjustment);",
"}"
].join( "\n" ) ) );
......@@ -115,7 +195,7 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
THREE.NodeLib.add( new THREE.FunctionNode( [
// Algorithm from Chapter 10 of Graphics Shaders
"float luminance_rgb(vec3 rgb) {",
"return dot(rgb, LUMA);",
" return dot(rgb, LUMA);",
"}"
].join( "\n" ) ) );
......@@ -126,9 +206,9 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
THREE.NodeLib.add( new THREE.FunctionNode( [
// Shader by Evan Wallace adapted by @lo-th
"vec3 vibrance_rgb(vec3 rgb, float adjustment) {",
"float average = (rgb.r + rgb.g + rgb.b) / 3.0;",
"float mx = max(rgb.r, max(rgb.g, rgb.b));",
"float amt = (mx - average) * (-3.0 * adjustment);",
"return mix(rgb.rgb, vec3(mx), amt);",
" float average = (rgb.r + rgb.g + rgb.b) / 3.0;",
" float mx = max(rgb.r, max(rgb.g, rgb.b));",
" float amt = (mx - average) * (-3.0 * adjustment);",
" return mix(rgb.rgb, vec3(mx), amt);",
"}"
].join( "\n" ) ) );
......@@ -64,11 +64,11 @@ THREE.NodeMaterial.addShortcuts = function( proto, prop, list ) {
THREE.NodeMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
THREE.NodeMaterial.prototype.constructor = THREE.NodeMaterial;
THREE.NodeMaterial.prototype.updateAnimation = function( delta ) {
THREE.NodeMaterial.prototype.updateFrame = function( delta ) {
for ( var i = 0; i < this.requestUpdate.length; ++ i ) {
this.requestUpdate[ i ].updateAnimation( delta );
this.requestUpdate[ i ].updateFrame( delta );
}
......@@ -80,6 +80,7 @@ THREE.NodeMaterial.prototype.build = function() {
this.defines = {};
this.uniforms = {};
this.attributes = {};
this.nodeData = {};
......@@ -96,7 +97,7 @@ THREE.NodeMaterial.prototype.build = function() {
this.requestUpdate = [];
this.requestAttrib = {
this.requestAttribs = {
uv: [],
color: []
};
......@@ -130,12 +131,12 @@ THREE.NodeMaterial.prototype.build = function() {
"#endif"
].join( "\n" );
var builder = new THREE.BuilderNode( this );
var builder = new THREE.NodeBuilder( this );
vertex = this.vertex.build( builder.setShader( 'vertex' ), 'v4' );
fragment = this.fragment.build( builder.setShader( 'fragment' ), 'v4' );
if ( this.requestAttrib.uv[ 0 ] ) {
if ( this.requestAttribs.uv[ 0 ] ) {
this.addVertexPars( 'varying vec2 vUv;' );
this.addFragmentPars( 'varying vec2 vUv;' );
......@@ -144,7 +145,7 @@ THREE.NodeMaterial.prototype.build = function() {
}
if ( this.requestAttrib.uv[ 1 ] ) {
if ( this.requestAttribs.uv[ 1 ] ) {
this.addVertexPars( 'varying vec2 vUv2; attribute vec2 uv2;' );
this.addFragmentPars( 'varying vec2 vUv2;' );
......@@ -153,7 +154,7 @@ THREE.NodeMaterial.prototype.build = function() {
}
if ( this.requestAttrib.color[ 0 ] ) {
if ( this.requestAttribs.color[ 0 ] ) {
this.addVertexPars( 'varying vec4 vColor; attribute vec4 color;' );
this.addFragmentPars( 'varying vec4 vColor;' );
......@@ -162,7 +163,7 @@ THREE.NodeMaterial.prototype.build = function() {
}
if ( this.requestAttrib.color[ 1 ] ) {
if ( this.requestAttribs.color[ 1 ] ) {
this.addVertexPars( 'varying vec4 vColor2; attribute vec4 color2;' );
this.addFragmentPars( 'varying vec4 vColor2;' );
......@@ -171,7 +172,7 @@ THREE.NodeMaterial.prototype.build = function() {
}
if ( this.requestAttrib.position ) {
if ( this.requestAttribs.position ) {
this.addVertexPars( 'varying vec3 vPosition;' );
this.addFragmentPars( 'varying vec3 vPosition;' );
......@@ -180,7 +181,7 @@ THREE.NodeMaterial.prototype.build = function() {
}
if ( this.requestAttrib.worldPosition ) {
if ( this.requestAttribs.worldPosition ) {
// for future update replace from the native "varying vec3 vWorldPosition" for optimization
......@@ -191,7 +192,7 @@ THREE.NodeMaterial.prototype.build = function() {
}
if ( this.requestAttrib.normal ) {
if ( this.requestAttribs.normal ) {
this.addVertexPars( 'varying vec3 vObjectNormal;' );
this.addFragmentPars( 'varying vec3 vObjectNormal;' );
......@@ -200,7 +201,7 @@ THREE.NodeMaterial.prototype.build = function() {
}
if ( this.requestAttrib.worldNormal ) {
if ( this.requestAttribs.worldNormal ) {
this.addVertexPars( 'varying vec3 vWNormal;' );
this.addFragmentPars( 'varying vec3 vWNormal;' );
......@@ -209,8 +210,8 @@ THREE.NodeMaterial.prototype.build = function() {
}
this.lights = this.requestAttrib.light;
this.transparent = this.requestAttrib.transparent || (this.blendMode !== THREE.NormalBlending && this.blendMode !== THREE.NoBlending);
this.lights = this.requestAttribs.light;
this.transparent = this.requestAttribs.transparent || this.blendMode > THREE.NormalBlending;
this.vertexShader = [
this.prefixCode,
......@@ -318,11 +319,32 @@ THREE.NodeMaterial.prototype.getFragmentTemp = function( uuid, type, ns ) {
};
THREE.NodeMaterial.prototype.getIncludes = function( incs ) {
THREE.NodeMaterial.prototype.getAttribute = function( name, type ) {
if ( ! this.attributes[ name ] ) {
var varName = 'nV' + name;
this.addVertexPars( 'varying ' + type + ' ' + varName + ';' );
this.addFragmentPars( 'varying ' + type + ' ' + varName + ';' );
this.addVertexPars( 'attribute ' + type + ' ' + name + ';' );
this.addVertexCode( varName + ' = ' + name + ';' );
this.attributes[ name ] = { varName : varName, name : name, type : type };
}
return this.attributes[ name ];
};
THREE.NodeMaterial.prototype.getIncludes = function() {
function sortByPosition( a, b ) {
return b.deps - a.deps;
return a.deps.length - b.deps.length;
}
......@@ -330,12 +352,11 @@ THREE.NodeMaterial.prototype.getIncludes = function( incs ) {
if ( ! incs ) return '';
var code = '';
var incs = incs.sort( sortByPosition );
var code = '', incs = incs.sort( sortByPosition );
for ( var i = 0; i < incs.length; i ++ ) {
code += incs[ i ].node.src + '\n';
if ( incs[ i ].src ) code += incs[ i ].src + '\n';
}
......@@ -377,9 +398,9 @@ THREE.NodeMaterial.prototype.addVertexNode = function( code ) {
THREE.NodeMaterial.prototype.clearVertexNode = function() {
var code = this.fragmentNode;
var code = this.vertexNode;
this.fragmentNode = '';
this.vertexNode = '';
return code;
......@@ -459,7 +480,7 @@ THREE.NodeMaterial.prototype.getDataNode = function( uuid ) {
};
THREE.NodeMaterial.prototype.include = function( shader, node ) {
THREE.NodeMaterial.prototype.include = function( builder, node, parent, source ) {
var includes;
......@@ -467,37 +488,51 @@ THREE.NodeMaterial.prototype.include = function( shader, node ) {
if ( node instanceof THREE.FunctionNode ) {
for ( var i = 0; i < node.includes.length; i ++ ) {
includes = this.functions[ builder.shader ] = this.functions[ builder.shader ] || [];
this.include( shader, node.includes[ i ] );
} else if ( node instanceof THREE.ConstNode ) {
}
includes = this.functions[ shader ] = this.functions[ shader ] || [];
includes = this.consts[ builder.shader ] = this.consts[ builder.shader ] || [];
}
else if ( node instanceof THREE.ConstNode ) {
includes = this.consts[ shader ] = this.consts[ shader ] || [];
var included = includes[ node.name ];
if ( ! included ) {
included = includes[ node.name ] = {
node : node,
deps : []
};
includes.push( included );
included.src = node.build( builder, 'source' );
}
if ( includes[ node.name ] === undefined ) {
if ( node instanceof THREE.FunctionNode && parent && includes[ parent.name ].deps.indexOf( node ) == - 1 ) {
includes[ parent.name ].deps.push( node );
for ( var ext in node.extensions ) {
if ( node.includes && node.includes.length ) {
this.extensions[ ext ] = true;
var i = 0;
do {
this.include( builder, node.includes[ i ++ ], parent );
} while ( i < node.includes.length );
}
includes[ node.name ] = {
node : node,
deps : 1
};
}
if ( source ) {
includes.push( includes[ node.name ] );
included.src = source;
}
else ++ includes[ node.name ].deps;
};
......@@ -25,8 +25,7 @@ THREE.GLNode.prototype.generate = function( builder ) {
code += 'gl_Position = ' + data.result + ';';
}
else {
} else {
code += 'gl_FragColor = ' + data.result + ';';
......
......@@ -19,11 +19,13 @@ THREE.TempNode.prototype.constructor = THREE.TempNode;
THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
output = output || this.getType( builder );
var material = builder.material;
if ( this.isShared() ) {
if ( this.isShared( builder, output ) ) {
var isUnique = this.isUnique();
var isUnique = this.isUnique( builder, output );
if ( isUnique && this.constructor.uuid === undefined ) {
......@@ -47,8 +49,7 @@ THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
return THREE.GLNode.prototype.build.call( this, builder, output, uuid );
}
else if ( ! builder.optimize || data.deps == 1 ) {
} else if ( ! builder.optimize || data.deps == 1 ) {
return THREE.GLNode.prototype.build.call( this, builder, output, uuid );
......@@ -63,8 +64,7 @@ THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
return builder.format( name, type, output );
}
else {
} else {
name = THREE.TempNode.prototype.generate.call( this, builder, output, uuid, data.output, ns );
......@@ -78,21 +78,18 @@ THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
}
}
else {
return builder.format( this.generate( builder, this.getType( builder ), uuid ), this.getType( builder ), output );
}
return THREE.GLNode.prototype.build.call( this, builder, output, uuid );
};
THREE.TempNode.prototype.isShared = function() {
THREE.TempNode.prototype.isShared = function( builder, output ) {
return this.shared;
return output !== 'sampler2D' && output !== 'samplerCube' && this.shared;
};
THREE.TempNode.prototype.isUnique = function() {
THREE.TempNode.prototype.isUnique = function( builder, output ) {
return this.unique;
......@@ -121,7 +118,7 @@ THREE.TempNode.prototype.getTemp = function( builder, uuid ) {
THREE.TempNode.prototype.generate = function( builder, output, uuid, type, ns ) {
if ( ! this.isShared() ) console.error( "THREE.TempNode is not shared!" );
if ( ! this.isShared( builder, output ) ) console.error( "THREE.TempNode is not shared!" );
uuid = uuid || this.uuid;
......
......@@ -11,6 +11,17 @@ THREE.CameraNode = function( scope, camera ) {
};
THREE.CameraNode.fDepthColor = new THREE.FunctionNode( [
"float depthColor( float mNear, float mFar ) {",
" #ifdef USE_LOGDEPTHBUF_EXT",
" float depth = gl_FragDepthEXT / gl_FragCoord.w;",
" #else",
" float depth = gl_FragCoord.z / gl_FragCoord.w;",
" #endif",
" return 1.0 - smoothstep( mNear, mFar, depth );",
"}"
].join( "\n" ) );
THREE.CameraNode.POSITION = 'position';
THREE.CameraNode.DEPTH = 'depth';
THREE.CameraNode.TO_VERTEX = 'toVertex';
......@@ -102,9 +113,11 @@ THREE.CameraNode.prototype.generate = function( builder, output ) {
case THREE.CameraNode.DEPTH:
builder.include( 'depthcolor' );
var func = THREE.CameraNode.fDepthColor;
builder.include( func );
result = 'depthcolor(' + this.near.build( builder, 'fv1' ) + ',' + this.far.build( builder, 'fv1' ) + ')';
result = func.name + '(' + this.near.build( builder, 'fv1' ) + ',' + this.far.build( builder, 'fv1' ) + ')';
break;
......@@ -120,7 +133,7 @@ THREE.CameraNode.prototype.generate = function( builder, output ) {
};
THREE.CameraNode.prototype.updateAnimation = function( delta ) {
THREE.CameraNode.prototype.updateFrame = function( delta ) {
switch ( this.scope ) {
......
......@@ -4,7 +4,7 @@
THREE.ColorsNode = function( index ) {
THREE.TempNode.call( this, 'v4', { share: false } );
THREE.TempNode.call( this, 'v4', { shared: false } );
this.index = index || 0;
......@@ -21,7 +21,7 @@ THREE.ColorsNode.prototype.generate = function( builder, output ) {
var material = builder.material;
var result;
material.requestAttrib.color[ this.index ] = true;
material.requestAttribs.color[ this.index ] = true;
if ( builder.isShader( 'vertex' ) ) result = THREE.ColorsNode.vertexDict[ this.index ];
else result = THREE.ColorsNode.fragmentDict[ this.index ];
......
......@@ -17,8 +17,7 @@ THREE.LightNode.prototype.generate = function( builder, output ) {
return builder.format( 'reflectedLight.directDiffuse', this.getType( builder ), output )
}
else {
} else {
console.warn( "THREE.LightNode is only compatible in \"light\" channel." );
......
......@@ -37,7 +37,7 @@ THREE.NormalNode.prototype.generate = function( builder, output ) {
case THREE.NormalNode.LOCAL:
material.requestAttrib.normal = true;
material.requestAttribs.normal = true;
if ( builder.isShader( 'vertex' ) ) result = 'normal';
else result = 'vObjectNormal';
......@@ -46,7 +46,7 @@ THREE.NormalNode.prototype.generate = function( builder, output ) {
case THREE.NormalNode.WORLD:
material.requestAttrib.worldNormal = true;
material.requestAttribs.worldNormal = true;
if ( builder.isShader( 'vertex' ) ) result = '( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz';
else result = 'vWNormal';
......
......@@ -50,7 +50,7 @@ THREE.PositionNode.prototype.generate = function( builder, output ) {
case THREE.PositionNode.LOCAL:
material.requestAttrib.position = true;
material.requestAttribs.position = true;
if ( builder.isShader( 'vertex' ) ) result = 'transformed';
else result = 'vPosition';
......@@ -59,7 +59,7 @@ THREE.PositionNode.prototype.generate = function( builder, output ) {
case THREE.PositionNode.WORLD:
material.requestAttrib.worldPosition = true;
material.requestAttribs.worldPosition = true;
if ( builder.isShader( 'vertex' ) ) result = 'vWPosition';
else result = 'vWPosition';
......
......@@ -20,7 +20,7 @@ THREE.ReflectNode.prototype.constructor = THREE.ReflectNode;
THREE.ReflectNode.prototype.getType = function( builder ) {
switch ( this.scope ) {
case THREE.CameraNode.SPHERE:
case THREE.ReflectNode.SPHERE:
return 'v2';
}
......@@ -36,7 +36,7 @@ THREE.ReflectNode.prototype.generate = function( builder, output ) {
case THREE.ReflectNode.VECTOR:
builder.material.addFragmentNode( 'vec3 reflectVec = inverseTransformDirection( reflect( -geometry.viewDir, geometry.normal ), viewMatrix );' );
builder.material.addFragmentNode( 'vec3 reflectVec = inverseTransformDirection( reflect( -normalize( vViewPosition ), normal ), viewMatrix );' );
result = 'reflectVec';
......@@ -56,7 +56,7 @@ THREE.ReflectNode.prototype.generate = function( builder, output ) {
var reflectVec = new THREE.ReflectNode( THREE.ReflectNode.VECTOR ).build( builder, 'v3' );
builder.material.addFragmentNode( 'vec3 reflectSphereVec = normalize((viewMatrix * vec4(' + reflectVec + ', 0.0 )).xyz + vec3(0.0,0.0,1.0)).xy * 0.5 + 0.5;' );
builder.material.addFragmentNode( 'vec2 reflectSphereVec = normalize((viewMatrix * vec4(' + reflectVec + ', 0.0 )).xyz + vec3(0.0,0.0,1.0)).xy * 0.5 + 0.5;' );
result = 'reflectSphereVec';
......
......@@ -22,8 +22,7 @@ THREE.ScreenUVNode.prototype.generate = function( builder, output ) {
result = '(gl_FragCoord.xy/' + this.resolution.build( builder, 'v2' ) + ')';
}
else {
} else {
console.warn( "THREE.ScreenUVNode is not compatible with " + builder.shader + " shader." );
......
......@@ -21,7 +21,7 @@ THREE.UVNode.prototype.generate = function( builder, output ) {
var material = builder.material;
var result;
material.requestAttrib.uv[ this.index ] = true;
material.requestAttribs.uv[ this.index ] = true;
if ( builder.isShader( 'vertex' ) ) result = THREE.UVNode.vertexDict[ this.index ];
else result = THREE.UVNode.fragmentDict[ this.index ];
......
......@@ -4,7 +4,7 @@
THREE.ColorNode = function( color ) {
THREE.InputNode.call( this, 'c', { share: false } );
THREE.InputNode.call( this, 'c', { shared: false } );
this.value = new THREE.Color( color || 0 );
......
......@@ -4,7 +4,7 @@
THREE.CubeTextureNode = function( value, coord, bias ) {
THREE.InputNode.call( this, 'v4' );
THREE.InputNode.call( this, 'v4', { shared : true } );
this.value = value;
this.coord = coord || new THREE.ReflectNode();
......@@ -23,6 +23,12 @@ THREE.CubeTextureNode.prototype.getTexture = function( builder, output ) {
THREE.CubeTextureNode.prototype.generate = function( builder, output ) {
if ( output === 'samplerCube' ) {
return this.getTexture( builder, output );
}
var cubetex = this.getTexture( builder, output );
var coord = this.coord.build( builder, 'v3' );
var bias = this.bias ? this.bias.build( builder, 'fv1' ) : undefined;
......@@ -38,6 +44,20 @@ THREE.CubeTextureNode.prototype.generate = function( builder, output ) {
if ( bias ) code = 'texCubeBias(' + cubetex + ',' + coord + ',' + bias + ')';
else code = 'texCube(' + cubetex + ',' + coord + ')';
if ( builder.isSlot( 'color' ) ) {
code = 'mapTexelToLinear(' + code + ')';
} else if ( builder.isSlot( 'emissive' ) ) {
code = 'emissiveMapTexelToLinear(' + code + ')';
} else if ( builder.isSlot( 'environment' ) ) {
code = 'envMapTexelToLinear(' + code + ')';
}
return builder.format( code, this.type, output );
};
......@@ -4,7 +4,7 @@
THREE.FloatNode = function( value ) {
THREE.InputNode.call( this, 'fv1', { share: false } );
THREE.InputNode.call( this, 'fv1', { shared: false } );
this.value = [ value || 0 ];
......
......@@ -4,7 +4,7 @@
THREE.IntNode = function( value ) {
THREE.InputNode.call( this, 'fv1', { share: false } );
THREE.InputNode.call( this, 'iv1', { shared: false } );
this.value = [ Math.floor( value || 0 ) ];
......
......@@ -4,7 +4,7 @@
THREE.Matrix4Node = function( matrix ) {
THREE.InputNode.call( this, 'm4', { share: false } );
THREE.InputNode.call( this, 'm4', { shared: false } );
this.value = matrix || new THREE.Matrix4();
......
......@@ -27,12 +27,15 @@ THREE.MirrorNode.prototype.generate = function( builder, output ) {
this.coordResult.a = this.offset;
this.texture.coord = this.offset ? this.coordResult : this.coord;
var coord = this.texture.build( builder, this.type );
if ( output === 'sampler2D' ) {
return builder.format( coord, this.type, output );
return this.texture.build( builder, output );
}
else {
}
return builder.format( this.texture.build( builder, this.type ), this.type, output );
} else {
console.warn( "THREE.MirrorNode is not compatible with " + builder.shader + " shader." );
......
......@@ -4,7 +4,7 @@
THREE.TextureNode = function( value, coord, bias, project ) {
THREE.InputNode.call( this, 'v4' );
THREE.InputNode.call( this, 'v4', { shared : true } );
this.value = value;
this.coord = coord || new THREE.UVNode();
......@@ -24,6 +24,12 @@ THREE.TextureNode.prototype.getTexture = function( builder, output ) {
THREE.TextureNode.prototype.generate = function( builder, output ) {
if ( output === 'sampler2D' ) {
return this.getTexture( builder, output );
}
var tex = this.getTexture( builder, output );
var coord = this.coord.build( builder, this.project ? 'v4' : 'v2' );
var bias = this.bias ? this.bias.build( builder, 'fv1' ) : undefined;
......@@ -42,6 +48,20 @@ THREE.TextureNode.prototype.generate = function( builder, output ) {
if ( bias ) code = method + '(' + tex + ',' + coord + ',' + bias + ')';
else code = method + '(' + tex + ',' + coord + ')';
if ( builder.isSlot( 'color' ) ) {
code = 'mapTexelToLinear(' + code + ')';
} else if ( builder.isSlot( 'emissive' ) ) {
code = 'emissiveMapTexelToLinear(' + code + ')';
} else if ( builder.isSlot( 'environment' ) ) {
code = 'envMapTexelToLinear(' + code + ')';
}
return builder.format( code, this.type, output );
};
......@@ -4,7 +4,7 @@
THREE.Vector2Node = function( x, y ) {
THREE.InputNode.call( this, 'v2', { share: false } );
THREE.InputNode.call( this, 'v2', { shared: false } );
this.value = new THREE.Vector2( x, y );
......
......@@ -4,7 +4,7 @@
THREE.Vector3Node = function( x, y, z ) {
THREE.InputNode.call( this, 'v3', { share: false } );
THREE.InputNode.call( this, 'v3', { shared: false } );
this.type = 'v3';
this.value = new THREE.Vector3( x, y, z );
......
......@@ -4,7 +4,7 @@
THREE.Vector4Node = function( x, y, z, w ) {
THREE.InputNode.call( this, 'v4', { share: false } );
THREE.InputNode.call( this, 'v4', { shared: false } );
this.value = new THREE.Vector4( x, y, z, w );
......
......@@ -23,11 +23,11 @@ THREE.PhongNode.prototype.build = function( builder ) {
material.define( 'PHONG' );
material.define( 'ALPHATEST', '0.0' );
material.requestAttrib.light = true;
material.requestAttribs.light = true;
if ( builder.isShader( 'vertex' ) ) {
var transform = this.transform ? this.transform.parseAndBuildCode( builder, 'v3', 'transform' ) : undefined;
var transform = this.transform ? this.transform.parseAndBuildCode( builder, 'v3', { cache : 'transform' } ) : undefined;
material.mergeUniform( THREE.UniformsUtils.merge( [
......@@ -98,47 +98,47 @@ THREE.PhongNode.prototype.build = function( builder ) {
// parse all nodes to reuse generate codes
this.color.parse( builder );
this.color.parse( builder, { slot : 'color' } );
this.specular.parse( builder );
this.shininess.parse( builder );
if ( this.alpha ) this.alpha.parse( builder );
if ( this.light ) this.light.parse( builder, 'light' );
if ( this.light ) this.light.parse( builder, { cache : 'light' } );
if ( this.ao ) this.ao.parse( builder );
if ( this.ambient ) this.ambient.parse( builder );
if ( this.shadow ) this.shadow.parse( builder );
if ( this.emissive ) this.emissive.parse( builder );
if ( this.emissive ) this.emissive.parse( builder, { slot : 'emissive' } );
if ( this.normal ) this.normal.parse( builder );
if ( this.normalScale && this.normal ) this.normalScale.parse( builder );
if ( this.environment ) this.environment.parse( builder );
if ( this.environment ) this.environment.parse( builder, { slot : 'environment' } );
if ( this.environmentAlpha && this.environment ) this.environmentAlpha.parse( builder );
// build code
var color = this.color.buildCode( builder, 'c' );
var color = this.color.buildCode( builder, 'c', { slot : 'color' } );
var specular = this.specular.buildCode( builder, 'c' );
var shininess = this.shininess.buildCode( builder, 'fv1' );
var alpha = this.alpha ? this.alpha.buildCode( builder, 'fv1' ) : undefined;
var light = this.light ? this.light.buildCode( builder, 'v3', 'light' ) : undefined;
var light = this.light ? this.light.buildCode( builder, 'v3', { cache : 'light' } ) : undefined;
var ao = this.ao ? this.ao.buildCode( builder, 'fv1' ) : undefined;
var ambient = this.ambient ? this.ambient.buildCode( builder, 'c' ) : undefined;
var shadow = this.shadow ? this.shadow.buildCode( builder, 'c' ) : undefined;
var emissive = this.emissive ? this.emissive.buildCode( builder, 'c' ) : undefined;
var emissive = this.emissive ? this.emissive.buildCode( builder, 'c', { slot : 'emissive' } ) : undefined;
var normal = this.normal ? this.normal.buildCode( builder, 'v3' ) : undefined;
var normalScale = this.normalScale && this.normal ? this.normalScale.buildCode( builder, 'v2' ) : undefined;
var environment = this.environment ? this.environment.buildCode( builder, 'c' ) : undefined;
var environment = this.environment ? this.environment.buildCode( builder, 'c', { slot : 'environment' } ) : undefined;
var environmentAlpha = this.environmentAlpha && this.environment ? this.environmentAlpha.buildCode( builder, 'fv1' ) : undefined;
material.requestAttrib.transparent = alpha != undefined;
material.requestAttribs.transparent = alpha != undefined;
material.addFragmentPars( [
THREE.ShaderChunk[ "common" ],
......@@ -275,7 +275,7 @@ THREE.PhongNode.prototype.build = function( builder ) {
output.push(
environmentAlpha.code,
"outgoingLight = mix(" + 'outgoingLight' + "," + environment.result + "," + environmentAlpha.result + ");"
"outgoingLight = mix( outgoingLight, " + environment.result + ", " + environmentAlpha.result + " );"
);
}
......@@ -287,11 +287,6 @@ THREE.PhongNode.prototype.build = function( builder ) {
}
output.push(
THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ]
);
if ( alpha ) {
output.push( "gl_FragColor = vec4( outgoingLight, " + alpha.result + " );" );
......@@ -303,6 +298,13 @@ THREE.PhongNode.prototype.build = function( builder ) {
}
output.push(
THREE.ShaderChunk[ "premultiplied_alpha_fragment" ],
THREE.ShaderChunk[ "tonemapping_fragment" ],
THREE.ShaderChunk[ "encodings_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ]
);
code = output.join( "\n" );
}
......
......@@ -24,11 +24,11 @@ THREE.StandardNode.prototype.build = function( builder ) {
material.define( 'PHYSICAL' );
material.define( 'ALPHATEST', '0.0' );
material.requestAttrib.light = true;
material.requestAttribs.light = true;
if ( builder.isShader( 'vertex' ) ) {
var transform = this.transform ? this.transform.parseAndBuildCode( builder, 'v3', 'transform' ) : undefined;
var transform = this.transform ? this.transform.parseAndBuildCode( builder, 'v3', { cache : 'transform' } ) : undefined;
material.mergeUniform( THREE.UniformsUtils.merge( [
......@@ -107,27 +107,27 @@ THREE.StandardNode.prototype.build = function( builder ) {
// parse all nodes to reuse generate codes
this.color.parse( builder );
this.color.parse( builder, { slot : 'color' } );
this.roughness.parse( builder );
this.metalness.parse( builder );
if ( this.alpha ) this.alpha.parse( builder );
if ( this.light ) this.light.parse( builder, 'light' );
if ( this.light ) this.light.parse( builder, { cache : 'light' } );
if ( this.ao ) this.ao.parse( builder );
if ( this.ambient ) this.ambient.parse( builder );
if ( this.shadow ) this.shadow.parse( builder );
if ( this.emissive ) this.emissive.parse( builder );
if ( this.emissive ) this.emissive.parse( builder, { slot : 'emissive' } );
if ( this.normal ) this.normal.parse( builder );
if ( this.normalScale && this.normal ) this.normalScale.parse( builder );
if ( this.environment ) this.environment.parse( builder, 'env', requires ); // isolate environment from others inputs ( see TextureNode, CubeTextureNode )
if ( this.environment ) this.environment.parse( builder, { cache : 'env', requires : requires, slot : 'environment' } ); // isolate environment from others inputs ( see TextureNode, CubeTextureNode )
// build code
var color = this.color.buildCode( builder, 'c' );
var color = this.color.buildCode( builder, 'c', { slot : 'color' } );
var roughness = this.roughness.buildCode( builder, 'fv1' );
var metalness = this.metalness.buildCode( builder, 'fv1' );
......@@ -135,19 +135,19 @@ THREE.StandardNode.prototype.build = function( builder ) {
var alpha = this.alpha ? this.alpha.buildCode( builder, 'fv1' ) : undefined;
var light = this.light ? this.light.buildCode( builder, 'v3', 'light' ) : undefined;
var light = this.light ? this.light.buildCode( builder, 'v3', { cache : 'light' } ) : undefined;
var ao = this.ao ? this.ao.buildCode( builder, 'fv1' ) : undefined;
var ambient = this.ambient ? this.ambient.buildCode( builder, 'c' ) : undefined;
var shadow = this.shadow ? this.shadow.buildCode( builder, 'c' ) : undefined;
var emissive = this.emissive ? this.emissive.buildCode( builder, 'c' ) : undefined;
var emissive = this.emissive ? this.emissive.buildCode( builder, 'c', { slot : 'emissive' } ) : undefined;
var normal = this.normal ? this.normal.buildCode( builder, 'v3' ) : undefined;
var normalScale = this.normalScale && this.normal ? this.normalScale.buildCode( builder, 'v2' ) : undefined;
var environment = this.environment ? this.environment.buildCode( builder, 'c', 'env', requires ) : undefined;
var environment = this.environment ? this.environment.buildCode( builder, 'c', { cache : 'env', requires : requires, slot : 'environment' } ) : undefined;
material.requestAttrib.transparent = alpha != undefined;
material.requestAttribs.transparent = alpha != undefined;
material.addFragmentPars( [
......@@ -266,7 +266,9 @@ THREE.StandardNode.prototype.build = function( builder ) {
output.push(
ao.code,
"reflectedLight.indirectDiffuse *= " + ao.result + ";"
"reflectedLight.indirectDiffuse *= " + ao.result + ";",
"float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );",
"reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, " + ao.result + ", material.specularRoughness );"
);
}
......@@ -310,11 +312,6 @@ THREE.StandardNode.prototype.build = function( builder ) {
output.push( "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;" );
output.push(
THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ]
);
if ( alpha ) {
output.push( "gl_FragColor = vec4( outgoingLight, " + alpha.result + " );" );
......@@ -326,6 +323,13 @@ THREE.StandardNode.prototype.build = function( builder ) {
}
output.push(
THREE.ShaderChunk[ "premultiplied_alpha_fragment" ],
THREE.ShaderChunk[ "tonemapping_fragment" ],
THREE.ShaderChunk[ "encodings_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ]
);
code = output.join( "\n" );
}
......
......@@ -30,7 +30,7 @@ THREE.Math3Node.prototype.getType = function( builder ) {
if ( a > b && a > c ) return this.a.getType( builder );
else if ( b > c ) return this.b.getType( builder );
return this.c.getType( builder );
};
......
......@@ -24,14 +24,14 @@ THREE.OperatorNode.prototype.getType = function( builder ) {
var a = this.a.getType( builder );
var b = this.b.getType( builder );
if ( builder.isFormatMatrix( a ) ) {
return 'v4';
}
// use the greater length vector
else if ( builder.getFormatLength( b ) > builder.getFormatLength( a ) ) {
} else if ( builder.getFormatLength( b ) > builder.getFormatLength( a ) ) {
// use the greater length vector
return b;
......@@ -43,11 +43,11 @@ THREE.OperatorNode.prototype.getType = function( builder ) {
THREE.OperatorNode.prototype.generate = function( builder, output ) {
var material = builder.material,
var material = builder.material,
data = material.getDataNode( this.uuid );
var type = this.getType( builder );
var a = this.a.build( builder, type );
var b = this.b.build( builder, type );
......
/**
* @author sunag / http://www.sunag.com.br/
*/
THREE.BlurNode = function( value, coord, radius, size ) {
THREE.TempNode.call( this, 'v4' );
this.requestUpdate = true;
this.value = value;
this.coord = coord || new THREE.UVNode();
this.radius = new THREE.Vector2Node( 1, 1 );
this.size = size;
this.blurX = true;
this.blurY = true;
this.horizontal = new THREE.FloatNode( 1 / 64 );
this.vertical = new THREE.FloatNode( 1 / 64 );
};
THREE.BlurNode.fBlurX = new THREE.FunctionNode( [
"vec4 blurX( sampler2D texture, vec2 uv, float s ) {",
" vec4 sum = vec4( 0.0 );",
" sum += texture2D( texture, vec2( uv.x - 4.0 * s, uv.y ) ) * 0.051;",
" sum += texture2D( texture, vec2( uv.x - 3.0 * s, uv.y ) ) * 0.0918;",
" sum += texture2D( texture, vec2( uv.x - 2.0 * s, uv.y ) ) * 0.12245;",
" sum += texture2D( texture, vec2( uv.x - 1.0 * s, uv.y ) ) * 0.1531;",
" sum += texture2D( texture, vec2( uv.x, uv.y ) ) * 0.1633;",
" sum += texture2D( texture, vec2( uv.x + 1.0 * s, uv.y ) ) * 0.1531;",
" sum += texture2D( texture, vec2( uv.x + 2.0 * s, uv.y ) ) * 0.12245;",
" sum += texture2D( texture, vec2( uv.x + 3.0 * s, uv.y ) ) * 0.0918;",
" sum += texture2D( texture, vec2( uv.x + 4.0 * s, uv.y ) ) * 0.051;",
" return sum;",
"}"
].join( "\n" ) );
THREE.BlurNode.fBlurY = new THREE.FunctionNode( [
"vec4 blurY( sampler2D texture, vec2 uv, float s ) {",
" vec4 sum = vec4( 0.0 );",
" sum += texture2D( texture, vec2( uv.x, uv.y - 4.0 * s ) ) * 0.051;",
" sum += texture2D( texture, vec2( uv.x, uv.y - 3.0 * s ) ) * 0.0918;",
" sum += texture2D( texture, vec2( uv.x, uv.y - 2.0 * s ) ) * 0.12245;",
" sum += texture2D( texture, vec2( uv.x, uv.y - 1.0 * s ) ) * 0.1531;",
" sum += texture2D( texture, vec2( uv.x, uv.y ) ) * 0.1633;",
" sum += texture2D( texture, vec2( uv.x, uv.y + 1.0 * s ) ) * 0.1531;",
" sum += texture2D( texture, vec2( uv.x, uv.y + 2.0 * s ) ) * 0.12245;",
" sum += texture2D( texture, vec2( uv.x, uv.y + 3.0 * s ) ) * 0.0918;",
" sum += texture2D( texture, vec2( uv.x, uv.y + 4.0 * s ) ) * 0.051;",
" return sum;",
"}"
].join( "\n" ) );
THREE.BlurNode.prototype = Object.create( THREE.TempNode.prototype );
THREE.BlurNode.prototype.constructor = THREE.BlurNode;
THREE.BlurNode.prototype.updateFrame = function( delta ) {
if ( this.size ) {
this.horizontal.number = this.radius.x / this.size.x;
this.vertical.number = this.radius.y / this.size.y;
} else if ( this.value.value && this.value.value.image ) {
var image = this.value.value.image;
this.horizontal.number = this.radius.x / image.width;
this.vertical.number = this.radius.y / image.height;
}
};
THREE.BlurNode.prototype.generate = function( builder, output ) {
var material = builder.material, blurX = THREE.BlurNode.fBlurX, blurY = THREE.BlurNode.fBlurY;
builder.include( blurX );
builder.include( blurY );
if ( builder.isShader( 'fragment' ) ) {
var blurCode = [], code;
if ( this.blurX ) {
blurCode.push( blurX.name + '(' + this.value.build( builder, 'sampler2D' ) + ',' + this.coord.build( builder, 'v2' ) + ',' + this.horizontal.build( builder, 'fv1' ) + ')' );
}
if ( this.blurY ) {
blurCode.push( blurY.name + '(' + this.value.build( builder, 'sampler2D' ) + ',' + this.coord.build( builder, 'v2' ) + ',' + this.vertical.build( builder, 'fv1' ) + ')' );
}
if ( blurCode.length == 2 ) code = '(' + blurCode.join( '+' ) + '/2.0)';
else if ( blurCode.length ) code = '(' + blurCode[ 0 ] + ')';
else code = 'vec4( 0.0 )';
return builder.format( code, this.getType( builder ), output );
} else {
console.warn( "THREE.BlurNode is not compatible with " + builder.shader + " shader." );
return builder.format( 'vec4( 0.0 )', this.getType( builder ), output );
}
};
/**
* @author sunag / http://www.sunag.com.br/
*/
THREE.BumpNode = function( value, coord, scale ) {
THREE.TempNode.call( this, 'v3' );
this.value = value;
this.coord = coord || new THREE.UVNode();
this.scale = scale || new THREE.Vector2Node( 1, 1 );
};
THREE.BumpNode.fBumpToNormal = new THREE.FunctionNode( [
"vec3 bumpToNormal( sampler2D bumpMap, vec2 uv, vec2 scale ) {",
" vec2 dSTdx = dFdx( uv );",
" vec2 dSTdy = dFdy( uv );",
" float Hll = texture2D( bumpMap, uv ).x;",
" float dBx = texture2D( bumpMap, uv + dSTdx ).x - Hll;",
" float dBy = texture2D( bumpMap, uv + dSTdy ).x - Hll;",
" return vec3( .5 + ( dBx * scale.x ), .5 + ( dBy * scale.y ), 1.0 );",
"}"
].join( "\n" ), null, { derivatives: true } );
THREE.BumpNode.prototype = Object.create( THREE.TempNode.prototype );
THREE.BumpNode.prototype.constructor = THREE.BumpNode;
THREE.BumpNode.prototype.generate = function( builder, output ) {
var material = builder.material, func = THREE.BumpNode.fBumpToNormal;
builder.include( func );
if ( builder.isShader( 'fragment' ) ) {
return builder.format( func.name + '(' + this.value.build( builder, 'sampler2D' ) + ',' +
this.coord.build( builder, 'v2' ) + ',' +
this.scale.build( builder, 'v2' ) + ')', this.getType( builder ), output );
} else {
console.warn( "THREE.BumpNode is not compatible with " + builder.shader + " shader." );
return builder.format( 'vec3( 0.0 )', this.getType( builder ), output );
}
};
......@@ -40,7 +40,7 @@ THREE.JoinNode.prototype.getNumElements = function() {
THREE.JoinNode.prototype.getType = function( builder ) {
return builder.getFormatByLength( this.getNumElements() );
return builder.getFormatFromLength( this.getNumElements() );
};
......@@ -56,13 +56,13 @@ THREE.JoinNode.prototype.generate = function( builder, output ) {
for ( var i = 0; i < length; i ++ ) {
var elm = this[ inputs[ i ]];
var elm = this[ inputs[ i ] ];
outputs.push( elm ? elm.build( builder, 'fv1' ) : '0.' );
}
var code = builder.getFormatConstructor( length ) + '(' + outputs.join( ',' ) + ')';
var code = ( length > 1 ? builder.getConstructorFromLength( length ) : '' ) + '(' + outputs.join( ',' ) + ')';
return builder.format( code, type, output );
......
......@@ -31,8 +31,7 @@ THREE.NormalMapNode.prototype.generate = function( builder, output ) {
this.value.coord.build( builder, 'v2' ) + ',' +
this.scale.build( builder, 'v2' ) + ')', this.getType( builder ), output );
}
else {
} else {
console.warn( "THREE.NormalMapNode is not compatible with " + builder.shader + " shader." );
......
......@@ -15,7 +15,7 @@ THREE.ResolutionNode = function( renderer ) {
THREE.ResolutionNode.prototype = Object.create( THREE.Vector2Node.prototype );
THREE.ResolutionNode.prototype.constructor = THREE.ResolutionNode;
THREE.ResolutionNode.prototype.updateAnimation = function( delta ) {
THREE.ResolutionNode.prototype.updateFrame = function( delta ) {
var size = this.renderer.getSize();
......
......@@ -37,8 +37,7 @@ THREE.RoughnessToBlinnExponentNode.prototype.generate = function( builder, outpu
return builder.format( 'getSpecularMIPLevel( Material_BlinnShininessExponent( material ), 8 )', this.type, output );
}
else {
} else {
console.warn( "THREE.RoughnessToBlinnExponentNode is only compatible with PhysicalMaterial." );
......@@ -46,8 +45,7 @@ THREE.RoughnessToBlinnExponentNode.prototype.generate = function( builder, outpu
}
}
else {
} else {
console.warn( "THREE.RoughnessToBlinnExponentNode is not compatible with " + builder.shader + " shader." );
......
......@@ -16,7 +16,7 @@ THREE.SwitchNode.prototype.constructor = THREE.SwitchNode;
THREE.SwitchNode.prototype.getType = function( builder ) {
return builder.getFormatByLength( this.components.length );
return builder.getFormatFromLength( this.components.length );
};
......
......@@ -15,7 +15,7 @@ THREE.TimerNode = function( value, scale ) {
THREE.TimerNode.prototype = Object.create( THREE.FloatNode.prototype );
THREE.TimerNode.prototype.constructor = THREE.TimerNode;
THREE.TimerNode.prototype.updateAnimation = function( delta ) {
THREE.TimerNode.prototype.updateFrame = function( delta ) {
this.number += delta * this.scale;
......
......@@ -21,7 +21,7 @@ THREE.VelocityNode = function( target, params ) {
THREE.VelocityNode.prototype = Object.create( THREE.Vector3Node.prototype );
THREE.VelocityNode.prototype.constructor = THREE.VelocityNode;
THREE.VelocityNode.prototype.updateAnimation = function( delta ) {
THREE.VelocityNode.prototype.updateFrame = function( delta ) {
this.velocity.subVectors( this.target.position, this.position );
this.position.copy( this.target.position );
......
......@@ -47,7 +47,8 @@
<script src="js/nodes/ConstNode.js"></script>
<script src="js/nodes/FunctionNode.js"></script>
<script src="js/nodes/FunctionCallNode.js"></script>
<script src="js/nodes/BuilderNode.js"></script>
<script src="js/nodes/AttributeNode.js"></script>
<script src="js/nodes/NodeBuilder.js"></script>
<script src="js/nodes/NodeLib.js"></script>
<script src="js/nodes/NodeMaterial.js"></script>
......@@ -87,6 +88,8 @@
<script src="js/nodes/utils/ColorAdjustmentNode.js"></script>
<script src="js/nodes/utils/NoiseNode.js"></script>
<script src="js/nodes/utils/ResolutionNode.js"></script>
<script src="js/nodes/utils/BumpNode.js"></script>
<script src="js/nodes/utils/BlurNode.js"></script>
<!-- Phong Material -->
<script src="js/nodes/materials/PhongNode.js"></script>
......@@ -106,18 +109,31 @@
var move = false;
var rtTexture, rtMaterial;
var gui, guiElements = [];
var textures = {
brick : { url : 'textures/brick_diffuse.jpg' },
grass : { url : 'textures/terrain/grasslight-big.jpg' },
grassNormal : { url : 'textures/terrain/grasslight-big-nm.jpg' },
decalDiffuse : { url : 'textures/decal/decal-diffuse.png' },
cloud : { url : 'textures/lava/cloud.png' },
spherical : { url : 'textures/envmap.png' }
};
var param = { example: 'standard' };
var brick = new THREE.TextureLoader().load( 'textures/brick_diffuse.jpg' );
var grass = new THREE.TextureLoader().load( 'textures/terrain/grasslight-big.jpg' );
var grassNormal = new THREE.TextureLoader().load( 'textures/terrain/grasslight-big-nm.jpg' );
function getTexture( name ) {
var decalDiffuse = new THREE.TextureLoader().load( 'textures/decal/decal-diffuse.png' );
decalDiffuse.wrapS = decalDiffuse.wrapT = THREE.RepeatWrapping;
var texture = textures[ name ].texture;
var cloud = new THREE.TextureLoader().load( 'textures/lava/cloud.png' );
cloud.wrapS = cloud.wrapT = THREE.RepeatWrapping;
if ( ! texture ) {
texture = textures[ name ].texture = new THREE.TextureLoader().load( textures[ name ].url );
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
}
return texture;
}
var cubemap = function() {
......@@ -193,6 +209,9 @@
'basic / layers': 'layers',
'basic / rim': 'rim',
'basic / color-adjustment': 'color-adjustment',
'basic / bump': 'bump',
'basic / blur': 'blur',
'basic / spherical-reflection': 'spherical-reflection',
'adv / fresnel': 'fresnel',
'adv / saturation': 'saturation',
'adv / top-bottom': 'top-bottom',
......@@ -205,10 +224,14 @@
'adv / camera-depth': 'camera-depth',
'adv / soft-body': 'soft-body',
'adv / wave': 'wave',
'adv / triangle-blur' : 'triangle-blur',
'adv / expression' : 'expression',
'adv / sss' : 'sss',
'adv / translucent' : 'translucent',
'misc / smoke' : 'smoke',
'misc / firefly' : 'firefly'
'misc / firefly' : 'firefly',
'misc / reserved-keywords' : 'reserved-keywords',
'misc / custom-attribute' : 'custom-attribute'
} ).onFinishChange( function() {
updateMaterial();
......@@ -306,14 +329,14 @@
//mtl.environmentAlpha = // environment alpha (float)
//mtl.transform = // vertex transformation (vec3)
var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
var mask = new THREE.SwitchNode( new THREE.TextureNode( getTexture( "decalDiffuse" ) ), 'w' );
mtl.color = new THREE.TextureNode( grass );
mtl.color = new THREE.TextureNode( getTexture( "grass" ) );
mtl.specular = new THREE.FloatNode( .5 );
mtl.shininess = new THREE.FloatNode( 15 );
mtl.environment = new THREE.CubeTextureNode( cubemap );
mtl.environmentAlpha = mask;
mtl.normal = new THREE.TextureNode( grassNormal );
mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
mtl.normalScale = new THREE.Math1Node( mask, THREE.Math1Node.INVERT );
break;
......@@ -338,7 +361,7 @@
//mtl.environment = // reflection/refraction (vec3)
//mtl.transform = // vertex transformation (vec3)
var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
var mask = new THREE.SwitchNode( new THREE.TextureNode( getTexture( "decalDiffuse" ) ), 'w' );
var normalScale = new THREE.FloatNode( .3 );
......@@ -372,7 +395,7 @@
mtl.roughness = roughness;
mtl.metalness = metalness;
mtl.environment = new THREE.CubeTextureNode( cubemap );
mtl.normal = new THREE.TextureNode( grassNormal );
mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
mtl.normalScale = normalMask;
// GUI
......@@ -619,7 +642,7 @@
mtl = new THREE.PhongNodeMaterial();
var texture = new THREE.TextureNode( brick );
var texture = new THREE.TextureNode( getTexture( "brick" ) );
var hue = new THREE.FloatNode();
var sataturation = new THREE.FloatNode( 1 );
......@@ -669,6 +692,82 @@
break;
case 'bump':
// MATERIAL
mtl = new THREE.PhongNodeMaterial();
var diffuse = new THREE.TextureNode( getTexture( "brick" ) );
var bump = new THREE.BumpNode( new THREE.TextureNode( getTexture( "brick" ) ) );
bump.scale = new THREE.Vector2Node( - 1.5, - 1.5 );
mtl.color = diffuse;
mtl.normal = bump;
// GUI
addGui( 'scaleX', bump.scale.x, function( val ) {
bump.scale.x = val;
}, false, - 2, 2 );
addGui( 'scaleY', bump.scale.y, function( val ) {
bump.scale.y = val;
}, false, - 2, 2 );
addGui( 'color', true, function( val ) {
mtl.color = val ? diffuse : new THREE.ColorNode( 0xEEEEEE );
mtl.build();
} );
break;
case 'blur':
// MATERIAL
mtl = new THREE.PhongNodeMaterial();
var diffuse = new THREE.TextureNode( getTexture( "brick" ) );
var blur = new THREE.BlurNode( new THREE.TextureNode( getTexture( "brick" ) ) );
mtl.color = blur;
// GUI
addGui( 'radiusX', blur.radius.x, function( val ) {
blur.radius.x = val;
}, false, 0, 15 );
addGui( 'radiusY', blur.radius.y, function( val ) {
blur.radius.y = val;
}, false, 0, 15 );
break;
case 'spherical-reflection':
// MATERIAL
mtl = new THREE.PhongNodeMaterial();
mtl.environment = new THREE.TextureNode( getTexture( "spherical" ), new THREE.ReflectNode( THREE.ReflectNode.SPHERE ) );
break;
case 'fresnel':
// MATERIAL
......@@ -729,8 +828,8 @@
mtl = new THREE.PhongNodeMaterial();
var tex1 = new THREE.TextureNode( grass );
var tex2 = new THREE.TextureNode( brick );
var tex1 = new THREE.TextureNode( getTexture( "grass" ) );
var tex2 = new THREE.TextureNode( getTexture( "brick" ) );
var offset = new THREE.FloatNode( 0 );
var scale = new THREE.FloatNode( 1 );
......@@ -748,7 +847,7 @@
THREE.OperatorNode.MUL
);
var mask = new THREE.TextureNode( decalDiffuse, uvScale );
var mask = new THREE.TextureNode( getTexture( "decalDiffuse" ), uvScale );
var maskAlphaChannel = new THREE.SwitchNode( mask, 'w' );
var blend = new THREE.Math3Node(
......@@ -782,14 +881,14 @@
mtl = new THREE.StandardNodeMaterial();
var tex = new THREE.TextureNode( brick );
var tex = new THREE.TextureNode( getTexture( "brick" ) );
var sat = new THREE.FloatNode( 0 );
var satrgb = new THREE.FunctionNode( [
"vec3 satrgb(vec3 rgb, float adjustment) {",
//"const vec3 W = vec3(0.2125, 0.7154, 0.0721);", // LUMA
"vec3 intensity = vec3(dot(rgb, LUMA));",
"return mix(intensity, rgb, adjustment);",
//" const vec3 W = vec3(0.2125, 0.7154, 0.0721);", // LUMA
" vec3 intensity = vec3(dot(rgb, LUMA));",
" return mix(intensity, rgb, adjustment);",
"}"
].join( "\n" ) );
......@@ -821,8 +920,8 @@
mtl = new THREE.PhongNodeMaterial();
var top = new THREE.TextureNode( grass );
var bottom = new THREE.TextureNode( brick );
var top = new THREE.TextureNode( getTexture( "grass" ) );
var bottom = new THREE.TextureNode( getTexture( "brick" ) );
var normal = new THREE.NormalNode( THREE.NormalNode.WORLD );
var normalY = new THREE.SwitchNode( normal, 'y' );
......@@ -890,7 +989,7 @@
THREE.OperatorNode.ADD
);
var tex = new THREE.TextureNode( cloud, displaceOffset );
var tex = new THREE.TextureNode( getTexture( "cloud" ), displaceOffset );
var texArea = new THREE.SwitchNode( tex, 'w' );
var displace = new THREE.OperatorNode(
......@@ -983,8 +1082,8 @@
THREE.OperatorNode.ADD
);
var cloudA = new THREE.TextureNode( cloud, uvOffsetA );
var cloudB = new THREE.TextureNode( cloud, uvOffsetB );
var cloudA = new THREE.TextureNode( getTexture( "cloud" ), uvOffsetA );
var cloudB = new THREE.TextureNode( getTexture( "cloud" ), uvOffsetB );
var clouds = new THREE.OperatorNode(
cloudA,
......@@ -1062,45 +1161,45 @@
var hash2 = new THREE.FunctionNode( [
"vec2 hash2(vec2 p) {",
"return fract(sin(vec2(dot(p, vec2(123.4, 748.6)), dot(p, vec2(547.3, 659.3))))*5232.85324);",
" return fract(sin(vec2(dot(p, vec2(123.4, 748.6)), dot(p, vec2(547.3, 659.3))))*5232.85324);",
"}"
].join( "\n" ) );
var voronoi = new THREE.FunctionNode( [
// Based off of iq's described here: http://www.iquilezles.org/www/articles/voronoili
"float voronoi(vec2 p, in float time) {",
"vec2 n = floor(p);",
"vec2 f = fract(p);",
"float md = 5.0;",
"vec2 m = vec2(0.0);",
"for (int i = -1; i <= 1; i++) {",
"for (int j = -1; j <= 1; j++) {",
"vec2 g = vec2(i, j);",
"vec2 o = hash2(n + g);",
"o = 0.5 + 0.5 * sin(time + 5.038 * o);",
"vec2 r = g + o - f;",
"float d = dot(r, r);",
"if (d < md) {",
"md = d;",
"m = n+g+o;",
"}",
"}",
"}",
"return md;",
" vec2 n = floor(p);",
" vec2 f = fract(p);",
" float md = 5.0;",
" vec2 m = vec2(0.0);",
" for (int i = -1; i <= 1; i++) {",
" for (int j = -1; j <= 1; j++) {",
" vec2 g = vec2(i, j);",
" vec2 o = hash2(n + g);",
" o = 0.5 + 0.5 * sin(time + 5.038 * o);",
" vec2 r = g + o - f;",
" float d = dot(r, r);",
" if (d < md) {",
" md = d;",
" m = n+g+o;",
" }",
" }",
" }",
" return md;",
"}"
].join( "\n" ), [ hash2 ] ); // define hash2 as dependencies
var voronoiLayers = new THREE.FunctionNode( [
// based on https://www.shadertoy.com/view/4tXSDf
"float voronoiLayers(vec2 p, in float time) {",
"float v = 0.0;",
"float a = 0.4;",
"for (int i = 0; i < 3; i++) {",
"v += voronoi(p, time) * a;",
"p *= 2.0;",
"a *= 0.5;",
"}",
"return v;",
" float v = 0.0;",
" float a = 0.4;",
" for (int i = 0; i < 3; i++) {",
" v += voronoi(p, time) * a;",
" p *= 2.0;",
" a *= 0.5;",
" }",
" return v;",
"}"
].join( "\n" ), [ voronoi ] ); // define voronoi as dependencies
......@@ -1340,7 +1439,7 @@
);
mtl.color = color;
mtl.normal = new THREE.TextureNode( grassNormal );
mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
mtl.normalScale = furScale;
mtl.environment = mildnessColor;
mtl.environmentAlpha = new THREE.Math1Node( viewZ, THREE.Math1Node.INVERT );
......@@ -1424,8 +1523,7 @@
mtl.roughness = new THREE.FloatNode( .5 );
mtl.environment = new THREE.CubeTextureNode( cubemap );
}
else {
} else {
// PhongNodeMaterial
......@@ -1579,6 +1677,155 @@
break;
case 'custom-attribute':
// GEOMETRY
// add "position" buffer to "custom" attribute
teapot.attributes[ 'custom' ] = teapot.attributes[ 'position' ];
// MATERIAL
mtl = new THREE.PhongNodeMaterial();
mtl.color = new THREE.AttributeNode( "custom", 3 );
// or
//mtl.color = new THREE.AttributeNode( "custom", "vec3" );
break;
case 'expression':
// MATERIAL
mtl = new THREE.PhongNodeMaterial();
var speed = new THREE.FloatNode( .5 );
mtl.color = new THREE.FunctionNode( "myCustomUv + (sin(time*speed)*.5) + (position * .05)", "vec3" );
mtl.color.keywords[ "speed" ] = speed;
mtl.transform = new THREE.FunctionNode( "mod(time*speed,1.0) < 0.5 ? position + (worldNormal*(1.0+sin(time*speed*1.0))*3.0) : position + sin( position.x * sin(time*speed*2.0))", "vec3" );
mtl.transform.keywords[ "speed" ] = speed;
// add global keyword ( variable or const )
THREE.NodeLib.addKeyword( 'myCustomUv', function( builder ) {
return new THREE.ReflectNode();
} );
// GUI
addGui( 'speed', speed.number, function( val ) {
speed.number = val;
}, false, 0, 1 );
break;
case 'reserved-keywords':
// MATERIAL
mtl = new THREE.PhongNodeMaterial();
var keywordsexample = new THREE.FunctionNode( [
// use "uv" reserved keyword
"vec4 keywordsexample( sampler2D texture ) {",
" return texture2D( texture, myUV ) + vec4( position * myAlpha, 0.0 );",
"}"
].join( "\n" ) );
// add local keyword ( const only )
keywordsexample.keywords[ "myAlpha" ] = new THREE.ConstNode( "float myAlpha .05" );
// add global keyword ( const only )
THREE.NodeLib.addKeyword( 'myUV', function( builder ) {
return new THREE.UVNode();
} );
// add global const or function
//THREE.NodeLib.add( new THREE.ConstNode("float MY_CONST .05") )
// reserved keywords
console.log( THREE.NodeLib.keywords );
// keywords conflit? use this to disable:
//blurtexture.useKeywords = false; // ( true is default )
mtl.color = new THREE.FunctionCallNode( keywordsexample, [ new THREE.TextureNode( getTexture( "brick" ) ) ] );
break;
case 'triangle-blur':
// MATERIAL
mtl = new THREE.PhongNodeMaterial();
var delta = new THREE.Vector2Node( .5, .25 );
var alpha = new THREE.FloatNode( 1 );
var blurtexture = new THREE.FunctionNode( [
// Reference: TriangleBlurShader.js
"vec4 blurtexture(sampler2D texture, vec2 uv, vec2 delta) {",
" vec4 color = vec4( 0.0 );",
" float total = 0.0;",
// randomize the lookup values to hide the fixed number of samples
" float offset = rand( uv );",
" for ( float t = -BLUR_ITERATIONS; t <= BLUR_ITERATIONS; t ++ ) {",
" float percent = ( t + offset - 0.5 ) / BLUR_ITERATIONS;",
" float weight = 1.0 - abs( percent );",
" color += texture2D( texture, uv + delta * percent ) * weight;",
" total += weight;",
" }",
" return color / total;",
"}"
].join( "\n" ), [ new THREE.ConstNode( "float BLUR_ITERATIONS 10.0" ) ] );
var blurredTexture = new THREE.FunctionCallNode( blurtexture, {
texture : new THREE.TextureNode( getTexture( "brick" ) ),
delta : delta,
uv : new THREE.UVNode()
} );
var color = new THREE.Math3Node(
new THREE.TextureNode( getTexture( "brick" ) ),
blurredTexture,
alpha,
THREE.Math3Node.MIX
);
mtl.color = color;
// GUI
addGui( 'alpha', alpha.number, function( val ) {
alpha.number = val;
}, false, 0, 1 );
addGui( 'deltaX', delta.x, function( val ) {
delta.x = val;
}, false, 0, 1 );
addGui( 'deltaY', delta.x, function( val ) {
delta.y = val;
}, false, 0, 1 );
break;
case 'firefly':
// MATERIAL
......@@ -1727,8 +1974,7 @@
mtl.light = light;
mtl.environment = color;
}
else {
} else {
frontColor = new THREE.ColorNode( 0xd04327 );
backColor = new THREE.ColorNode( 0x1a0e14 );
......@@ -1810,8 +2056,7 @@
mesh.position.z = Math.cos( time ) * 10;
mesh.position.y = Math.sin( time ) * 10;
}
else {
} else {
mesh.position.z = mesh.position.y = 0;
......@@ -1820,7 +2065,7 @@
//mesh.rotation.z += .01;
// update material animation and/or gpu calcs (pre-renderer)
mesh.material.updateAnimation( delta );
mesh.material.updateFrame( delta );
// render to texture for sss/translucent material only
if ( rtTexture ) {
......
......@@ -35,6 +35,7 @@
</div>
<script src="../build/three.js"></script>
<script src="js/libs/dat.gui.min.js"></script>
<script src="js/Mirror.js"></script>
<script src="js/controls/OrbitControls.js"></script>
......@@ -46,7 +47,7 @@
<script src="js/nodes/ConstNode.js"></script>
<script src="js/nodes/FunctionNode.js"></script>
<script src="js/nodes/FunctionCallNode.js"></script>
<script src="js/nodes/BuilderNode.js"></script>
<script src="js/nodes/NodeBuilder.js"></script>
<script src="js/nodes/NodeLib.js"></script>
<script src="js/nodes/NodeMaterial.js"></script>
......@@ -88,6 +89,7 @@
<script src="js/nodes/utils/ColorAdjustmentNode.js"></script>
<script src="js/nodes/utils/NoiseNode.js"></script>
<script src="js/nodes/utils/ResolutionNode.js"></script>
<script src="js/nodes/utils/BlurNode.js"></script>
<!-- Phong Material -->
<script src="js/nodes/materials/PhongNode.js"></script>
......@@ -111,8 +113,11 @@
decalDiffuse.wrapS = decalDiffuse.wrapT = THREE.RepeatWrapping;
var camera, scene, renderer;
var clock = new THREE.Clock();
var cameraControls;
var gui = new dat.GUI();
var groundMirror;
var sphereGroup, smallSphere;
......@@ -181,8 +186,20 @@
THREE.Math3Node.MIX
);
var blurMirror = new THREE.BlurNode( mirror );
blurMirror.size = new THREE.Vector2( WIDTH, HEIGHT );
blurMirror.coord = new THREE.FunctionNode( "(projCoord.xyz / projCoord.q)", "vec3" );
blurMirror.coord.keywords[ "projCoord" ] = new THREE.OperatorNode( mirror.offset, mirror.coord, THREE.OperatorNode.ADD );
blurMirror.radius.x = blurMirror.radius.y = 0;
gui.add( { blur : blurMirror.radius.x }, "blur", 0, 25 ).onChange( function(v) {
blurMirror.radius.x = blurMirror.radius.y = v;
} );
groundMirrorMaterial = new THREE.PhongNodeMaterial();
groundMirrorMaterial.environment = mirror;
groundMirrorMaterial.environment = blurMirror; // or add "mirror" variable to disable blur
groundMirrorMaterial.environmentAlpha = mask;
groundMirrorMaterial.normal = normal;
//groundMirrorMaterial.normalScale = new THREE.FloatNode( 1 );
......@@ -281,6 +298,7 @@
requestAnimationFrame( update );
var delta = clock.getDelta();
var timer = Date.now() * 0.01;
sphereGroup.rotation.y -= 0.002;
......@@ -293,6 +311,8 @@
smallSphere.rotation.y = ( Math.PI / 2 ) - timer * 0.1;
smallSphere.rotation.z = timer * 0.8;
groundMirrorMaterial.updateFrame( delta );
cameraControls.update();
render();
......
......@@ -49,7 +49,7 @@
<script src="js/nodes/ConstNode.js"></script>
<script src="js/nodes/FunctionNode.js"></script>
<script src="js/nodes/FunctionCallNode.js"></script>
<script src="js/nodes/BuilderNode.js"></script>
<script src="js/nodes/NodeBuilder.js"></script>
<script src="js/nodes/NodeLib.js"></script>
<script src="js/nodes/NodeMaterial.js"></script>
......@@ -81,6 +81,7 @@
<script src="js/nodes/utils/JoinNode.js"></script>
<script src="js/nodes/utils/TimerNode.js"></script>
<script src="js/nodes/utils/ColorAdjustmentNode.js"></script>
<script src="js/nodes/utils/BlurNode.js"></script>
<!-- Post-Processing -->
<script src="js/nodes/postprocessing/NodePass.js"></script>
......@@ -90,6 +91,7 @@
var camera, scene, renderer, composer;
var object, light, nodepass;
var gui, guiElements = [];
var clock = new THREE.Clock();
var param = { example: 'color-adjustment' };
......@@ -117,7 +119,8 @@
'basic / invert': 'invert',
'adv / saturation': 'saturation',
'adv / refraction': 'refraction',
'adv / mosaic': 'mosaic'
'adv / mosaic': 'mosaic',
'adv / blur': 'blur'
} ).onFinishChange( function() {
updateMaterial();
......@@ -464,6 +467,33 @@
} );
break;
case 'blur':
// PASS
var size = renderer.getSize();
var blurScreen = new THREE.BlurNode( new THREE.ScreenNode() );
blurScreen.size = new THREE.Vector2( size.width, size.height );
nodepass.value = blurScreen;
// GUI
addGui( 'blurX', blurScreen.radius.x, function( val ) {
blurScreen.radius.x = val;
}, false, 0, 15 );
addGui( 'blurY', blurScreen.radius.y, function( val ) {
blurScreen.radius.y = val;
}, false, 0, 15 );
break;
}
......@@ -541,9 +571,13 @@
requestAnimationFrame( animate );
var delta = clock.getDelta();
object.rotation.x += 0.005;
object.rotation.y += 0.01;
nodepass.node.updateFrame( delta );
composer.render();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册