diff --git a/examples/files.js b/examples/files.js index fb8ce946c12344..4248d0f545ca0c 100644 --- a/examples/files.js +++ b/examples/files.js @@ -179,6 +179,7 @@ var files = { "webgl_materials_video", "webgl_materials_video_webcam", "webgl_materials_wireframe", + 'webgl_materials_shader_override', "webgl_mirror", "webgl_mirror_nodes", "webgl_modifier_simplifier", diff --git a/examples/webgl_materials_modified.html b/examples/webgl_materials_modified.html index 0cd0ec48609841..84838a133684a7 100644 --- a/examples/webgl_materials_modified.html +++ b/examples/webgl_materials_modified.html @@ -91,29 +91,59 @@ scene = new THREE.Scene(); var material = new THREE.MeshNormalMaterial(); - material.onBeforeCompile = function ( shader ) { - // console.log( shader ) + // BEGIN custom shader injection ---- - shader.uniforms.time = { value: 0 }; + var myShader = [ + 'float theta = sin( time + position.y ) / 2.0;', + 'float c = cos( theta );', + 'float s = sin( theta );', + 'mat3 m = mat3( c, 0, s, 0, 1, 0, -s, 0, c );', + 'vec3 transformed = vec3( position ) * m;', + 'vNormal = vNormal * m;' + ].join( '\n' ); - shader.vertexShader = 'uniform float time;\n' + shader.vertexShader; - shader.vertexShader = shader.vertexShader.replace( - '#include ', - [ - 'float theta = sin( time + position.y ) / 2.0;', - 'float c = cos( theta );', - 'float s = sin( theta );', - 'mat3 m = mat3( c, 0, s, 0, 1, 0, -s, 0, c );', - 'vec3 transformed = vec3( position ) * m;', - 'vNormal = vNormal * m;' - ].join( '\n' ) - ); + material.shaderIncludes = { - materialShader = shader; + begin_vertex: myShader, + uv_pars_vertex: THREE.ShaderChunk.uv_pars_vertex + '\nuniform float time;\n' + + }; + + material.shaderUniforms = { + time: { value: 0 } }; + //OR + + // material.shaderIncludes = { + + // begin_vertex: myShader, + + // }; + + // material.shaderUniforms = { + // time: { value: 0 , type:'float' } //if type is provided the uniform will be injected automatically + // }; + + materialShader = material; + + // material.onBeforeCompile = function ( shader ) { + + // shader.uniforms.time = { value: 0 }; + + // shader.vertexShader = 'uniform float time;\n' + shader.vertexShader; + + // shader.vertexShader = shader.vertexShader.replace( + // '#include ', + // myShader + // ); + + // materialShader = shader; + + // }; + loader = new THREE.JSONLoader(); loader.load( "obj/leeperrysmith/LeePerrySmith.js", function( geometry ) { @@ -188,7 +218,8 @@ if ( materialShader ) { - materialShader.uniforms.time.value = performance.now() / 1000; + // materialShader.uniforms.time.value = performance.now() / 1000; + materialShader.shaderUniforms.time.value = performance.now() / 1000; } diff --git a/examples/webgl_materials_shader_override.html b/examples/webgl_materials_shader_override.html new file mode 100644 index 00000000000000..1f9f163c53552d --- /dev/null +++ b/examples/webgl_materials_shader_override.html @@ -0,0 +1,333 @@ + + + + three.js webgl - shader include override example + + + + + +
+ three.js - Material shader override example by pailhead +
+ + + + + + + + + + + + + + + + + + + + diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 80c55d8e9b1413..3f1cfe04876d9f 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -1500,9 +1500,13 @@ function WebGLRenderer( parameters ) { var shader = ShaderLib[ parameters.shaderID ]; + var computedUniforms = undefined !== material.shaderUniforms ? + UniformsUtils.merge([ UniformsUtils.clone( shader.uniforms ), material.shaderUniforms ]) : + UniformsUtils.clone( shader.uniforms ); + materialProperties.shader = { name: material.type, - uniforms: UniformsUtils.clone( shader.uniforms ), + uniforms: computedUniforms, vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader }; @@ -1822,6 +1826,16 @@ function WebGLRenderer( parameters ) { } + //2. follow the same pattern with common uniforms - + // refreshUniformsCommon looks at some known uniforms and refreshes them + // refreshUniformsCustom should look at unknown uniforms but in a known place + + if ( undefined !== material.shaderUniforms ) { + + refreshUniformsCustom( m_uniforms, material ); + + } + // refresh uniforms common to several materials if ( fog && material.fog ) { @@ -2063,6 +2077,17 @@ function WebGLRenderer( parameters ) { } + //2. same pattern as refreshUniformsFoobar + function refreshUniformsCustom( uniforms, material ) { + + for ( var uniform in material.shaderUniforms ) { + + uniforms[ uniform ].value = material.shaderUniforms[ uniform ].value; + + } + + } + function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value = material.color; diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index f8915534a354f7..a4883a00644c81 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -150,13 +150,15 @@ function replaceLightNums( string, parameters ) { } -function parseIncludes( string ) { +//3. allow parsing of a custom dictionary, not just THREE.ShaderChunk +function parseIncludes( string, materialIncludes ) { var pattern = /^[ \t]*#include +<([\w\d.]+)>/gm; function replace( match, include ) { - var replace = ShaderChunk[ include ]; + //3. if there are material includes provided use those instead of the default chunks: + var replace = undefined !== materialIncludes[ include ] ? materialIncludes[ include ] : ShaderChunk[ include ]; if ( replace === undefined ) { @@ -200,6 +202,8 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters var defines = material.defines; + var materialIncludes = material.shaderIncludes; //custom chunks + var vertexShader = shader.vertexShader; var fragmentShader = shader.fragmentShader; @@ -281,6 +285,8 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters var customDefines = generateDefines( defines ); + var customIncludes = undefined !== materialIncludes ? materialIncludes : {}; //user is not aware of this feature, fine + // var program = gl.createProgram(); @@ -500,10 +506,11 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters } - vertexShader = parseIncludes( vertexShader ); + //3. provide optional chunk dictionary customIncludes + vertexShader = parseIncludes( vertexShader, customIncludes ); vertexShader = replaceLightNums( vertexShader, parameters ); - fragmentShader = parseIncludes( fragmentShader ); + fragmentShader = parseIncludes( fragmentShader, customIncludes ); fragmentShader = replaceLightNums( fragmentShader, parameters ); if ( ! material.isShaderMaterial ) { diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js index 633a3397e7c7a1..3456cbe0364642 100644 --- a/src/renderers/webgl/WebGLPrograms.js +++ b/src/renderers/webgl/WebGLPrograms.js @@ -241,10 +241,20 @@ function WebGLPrograms( renderer, extensions, capabilities ) { } - array.push( material.onBeforeCompile.toString() ); + //4. if the user is aware of the feature and added a dictionary + if ( material.shaderIncludes !== undefined ) { - array.push( renderer.gammaOutput ); + for ( var include in material.shaderIncludes ) { + + //4. hash with chunks? + array.push( material.shaderIncludes[ include ] ); + + } + } + + array.push( renderer.gammaOutput ); + return array.join(); };