diff --git a/examples/js/effects/OutlineEffect.js b/examples/js/effects/OutlineEffect.js index bbffa58edb7edb62a57ed966531ef411dab4d281..ea093d4f50505589ca433697670a2a50a0d0f5a5 100644 --- a/examples/js/effects/OutlineEffect.js +++ b/examples/js/effects/OutlineEffect.js @@ -54,9 +54,6 @@ * visible: true, * keepAlive: true * }; - * - * TODO - * - support shader material without objectNormal in its vertexShader */ THREE.OutlineEffect = function ( renderer, parameters ) { @@ -90,58 +87,57 @@ THREE.OutlineEffect = function ( renderer, parameters ) { //this.cache = cache; // for debug - // copied from WebGLPrograms and removed some materials - var shaderIDs = { - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'phong', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical' - }; - - var uniformsChunk = { + var uniformsOutline = { outlineThickness: { value: defaultThickness }, outlineColor: { value: defaultColor }, outlineAlpha: { value: defaultAlpha } }; - var vertexShaderChunk = [ + var vertexShader = [ + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", "uniform float outlineThickness;", - "vec4 calculateOutline( vec4 pos, vec3 objectNormal, vec4 skinned ) {", - + "vec4 calculateOutline( vec4 pos, vec3 normal, vec4 skinned ) {", " float thickness = outlineThickness;", " const float ratio = 1.0;", // TODO: support outline thickness ratio for each vertex - " vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + objectNormal, 1.0 );", + " vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + normal, 1.0 );", // NOTE: subtract pos2 from pos because BackSide objectNormal is negative " vec4 norm = normalize( pos - pos2 );", " return pos + norm * thickness * pos.w * ratio;", + "}", - "}" + "void main() {", - ].join( "\n" ); + " #include ", - var vertexShaderChunk2 = [ + " #include ", + " #include ", + " #include ", + " #include ", - "#if ! defined( LAMBERT ) && ! defined( PHONG ) && ! defined( TOON ) && ! defined( STANDARD )", - " #ifndef USE_ENVMAP", - " vec3 objectNormal = normalize( normal );", - " #endif", - "#endif", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", - "#ifdef FLIP_SIDED", - " objectNormal = -objectNormal;", - "#endif", + " vec3 outlineNormal = - objectNormal;", // the outline material is always rendered with THREE.BackSide - "#ifdef DECLARE_TRANSFORMED", - " vec3 transformed = vec3( position );", - "#endif", + " gl_Position = calculateOutline( gl_Position, outlineNormal, vec4( transformed, 1.0 ) );", - "gl_Position = calculateOutline( gl_Position, objectNormal, vec4( transformed, 1.0 ) );", + " #include ", + " #include ", + " #include ", - "#include " + "}", ].join( "\n" ); @@ -149,92 +145,39 @@ THREE.OutlineEffect = function ( renderer, parameters ) { "#include ", "#include ", + "#include ", + "#include ", "uniform vec3 outlineColor;", "uniform float outlineAlpha;", "void main() {", + " #include ", + " #include ", + " gl_FragColor = vec4( outlineColor, outlineAlpha );", + " #include ", + " #include ", + " #include ", " #include ", "}" ].join( "\n" ); - function createInvisibleMaterial() { - - return new THREE.ShaderMaterial( { name: 'invisible', visible: false } ); - - } - - function createMaterial( originalMaterial ) { - - var shaderID = shaderIDs[ originalMaterial.type ]; - var originalUniforms, originalVertexShader; - - if ( shaderID !== undefined ) { - - var shader = THREE.ShaderLib[ shaderID ]; - originalUniforms = shader.uniforms; - originalVertexShader = shader.vertexShader; - - } else if ( originalMaterial.isRawShaderMaterial === true ) { - - originalUniforms = originalMaterial.uniforms; - originalVertexShader = originalMaterial.vertexShader; - - if ( ! /attribute\s+vec3\s+position\s*;/.test( originalVertexShader ) || - ! /attribute\s+vec3\s+normal\s*;/.test( originalVertexShader ) ) { - - console.warn( 'THREE.OutlineEffect requires both vec3 position and normal attributes in vertex shader, ' + - 'does not draw outline for ' + originalMaterial.name + '(uuid:' + originalMaterial.uuid + ') material.' ); - - return createInvisibleMaterial(); - - } - - } else if ( originalMaterial.isShaderMaterial === true ) { - - originalUniforms = originalMaterial.uniforms; - originalVertexShader = originalMaterial.vertexShader; - - } else { - - return createInvisibleMaterial(); - - } - - var uniforms = Object.assign( {}, originalUniforms, uniformsChunk ); - - var vertexShader = originalVertexShader - // put vertexShaderChunk right before "void main() {...}" - .replace( /void\s+main\s*\(\s*\)/, vertexShaderChunk + '\nvoid main()' ) - // put vertexShaderChunk2 the end of "void main() {...}" - // Note: here assums originalVertexShader ends with "}" of "void main() {...}" - .replace( /\}\s*$/, vertexShaderChunk2 + '\n}' ) - // remove any light related lines - // Note: here is very sensitive to originalVertexShader - // TODO: consider safer way - .replace( /#include\s+<[\w_]*light[\w_]*>/g, '' ); - - var defines = {}; - - if ( ! /vec3\s+transformed\s*=/.test( originalVertexShader ) && - ! /#include\s+/.test( originalVertexShader ) ) defines.DECLARE_TRANSFORMED = true; + function createMaterial() { return new THREE.ShaderMaterial( { - defines: defines, - uniforms: uniforms, + uniforms: THREE.UniformsUtils.merge( [ + THREE.UniformsLib[ 'fog' ], + THREE.UniformsLib[ 'displacementmap' ], + uniformsOutline + ] ), vertexShader: vertexShader, fragmentShader: fragmentShader, - side: THREE.BackSide, - //wireframe: true, - skinning: false, - morphTargets: false, - morphNormals: false, - fog: false + side: THREE.BackSide } ); } @@ -246,7 +189,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) { if ( data === undefined ) { data = { - material: createMaterial( originalMaterial ), + material: createMaterial(), used: true, keepAlive: defaultKeepAlive, count: 0 @@ -274,9 +217,32 @@ THREE.OutlineEffect = function ( renderer, parameters ) { } + function isCompatible( object ) { + + var geometry = object.geometry; + var hasNormals = false; + + if ( object.geometry !== undefined ) { + + if ( geometry.isBufferGeometry ) { + + hasNormals = geometry.attributes.normal !== undefined; + + } else { + + hasNormals = true; // the renderer always produces a normal attribute for Geometry + + } + + } + + return ( object.isMesh === true && object.material !== undefined && hasNormals === true ); + + } + function setOutlineMaterial( object ) { - if ( object.material === undefined ) return; + if ( isCompatible( object ) === false ) return; if ( Array.isArray( object.material ) ) { @@ -299,7 +265,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) { function restoreOriginalMaterial( object ) { - if ( object.material === undefined ) return; + if ( isCompatible( object ) === false ) return; if ( Array.isArray( object.material ) ) { @@ -344,6 +310,14 @@ THREE.OutlineEffect = function ( renderer, parameters ) { } + if ( originalMaterial.displacementMap ) { + + material.uniforms.displacementMap.value = originalMaterial.displacementMap; + material.uniforms.displacementScale.value = originalMaterial.displacementScale; + material.uniforms.displacementBias.value = originalMaterial.displacementBias; + + } + } function updateOutlineMaterial( material, originalMaterial ) { @@ -356,6 +330,9 @@ THREE.OutlineEffect = function ( renderer, parameters ) { material.morphTargets = originalMaterial.morphTargets; material.morphNormals = originalMaterial.morphNormals; material.fog = originalMaterial.fog; + material.toneMapped = originalMaterial.toneMapped; + material.premultipliedAlpha = originalMaterial.premultipliedAlpha; + material.displacementMap = originalMaterial.displacementMap; if ( outlineParameters !== undefined ) { @@ -382,6 +359,18 @@ THREE.OutlineEffect = function ( renderer, parameters ) { if ( originalMaterial.wireframe === true || originalMaterial.depthTest === false ) material.visible = false; + if ( originalMaterial.clippingPlanes ) { + + material.clipping = true; + + material.clippingPlanes = originalMaterial.clippingPlanes; + material.clipIntersection = originalMaterial.clipIntersection; + material.clipShadows = originalMaterial.clipShadows; + + } + + material.version = originalMaterial.version; // update outline material if necessary + } function cleanupCache() { diff --git a/examples/jsm/effects/OutlineEffect.js b/examples/jsm/effects/OutlineEffect.js index 655430e5d327d6ea0581fc570e35628aabdd880b..2aea152778a6ed9f202305f2c940435d18fde820 100644 --- a/examples/jsm/effects/OutlineEffect.js +++ b/examples/jsm/effects/OutlineEffect.js @@ -54,16 +54,14 @@ * visible: true, * keepAlive: true * }; - * - * TODO - * - support shader material without objectNormal in its vertexShader */ import { BackSide, Color, - ShaderLib, - ShaderMaterial + ShaderMaterial, + UniformsLib, + UniformsUtils } from "../../../build/three.module.js"; var OutlineEffect = function ( renderer, parameters ) { @@ -97,58 +95,57 @@ var OutlineEffect = function ( renderer, parameters ) { //this.cache = cache; // for debug - // copied from WebGLPrograms and removed some materials - var shaderIDs = { - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'phong', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical' - }; - - var uniformsChunk = { + var uniformsOutline = { outlineThickness: { value: defaultThickness }, outlineColor: { value: defaultColor }, outlineAlpha: { value: defaultAlpha } }; - var vertexShaderChunk = [ + var vertexShader = [ + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", "uniform float outlineThickness;", - "vec4 calculateOutline( vec4 pos, vec3 objectNormal, vec4 skinned ) {", - + "vec4 calculateOutline( vec4 pos, vec3 normal, vec4 skinned ) {", " float thickness = outlineThickness;", " const float ratio = 1.0;", // TODO: support outline thickness ratio for each vertex - " vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + objectNormal, 1.0 );", + " vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + normal, 1.0 );", // NOTE: subtract pos2 from pos because BackSide objectNormal is negative " vec4 norm = normalize( pos - pos2 );", " return pos + norm * thickness * pos.w * ratio;", + "}", - "}" + "void main() {", - ].join( "\n" ); + " #include ", - var vertexShaderChunk2 = [ + " #include ", + " #include ", + " #include ", + " #include ", - "#if ! defined( LAMBERT ) && ! defined( PHONG ) && ! defined( TOON ) && ! defined( STANDARD )", - " #ifndef USE_ENVMAP", - " vec3 objectNormal = normalize( normal );", - " #endif", - "#endif", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", - "#ifdef FLIP_SIDED", - " objectNormal = -objectNormal;", - "#endif", + " vec3 outlineNormal = - objectNormal;", // the outline material is always rendered with BackSide - "#ifdef DECLARE_TRANSFORMED", - " vec3 transformed = vec3( position );", - "#endif", + " gl_Position = calculateOutline( gl_Position, outlineNormal, vec4( transformed, 1.0 ) );", - "gl_Position = calculateOutline( gl_Position, objectNormal, vec4( transformed, 1.0 ) );", + " #include ", + " #include ", + " #include ", - "#include " + "}", ].join( "\n" ); @@ -156,92 +153,39 @@ var OutlineEffect = function ( renderer, parameters ) { "#include ", "#include ", + "#include ", + "#include ", "uniform vec3 outlineColor;", "uniform float outlineAlpha;", "void main() {", + " #include ", + " #include ", + " gl_FragColor = vec4( outlineColor, outlineAlpha );", + " #include ", + " #include ", + " #include ", " #include ", "}" ].join( "\n" ); - function createInvisibleMaterial() { - - return new ShaderMaterial( { name: 'invisible', visible: false } ); - - } - - function createMaterial( originalMaterial ) { - - var shaderID = shaderIDs[ originalMaterial.type ]; - var originalUniforms, originalVertexShader; - - if ( shaderID !== undefined ) { - - var shader = ShaderLib[ shaderID ]; - originalUniforms = shader.uniforms; - originalVertexShader = shader.vertexShader; - - } else if ( originalMaterial.isRawShaderMaterial === true ) { - - originalUniforms = originalMaterial.uniforms; - originalVertexShader = originalMaterial.vertexShader; - - if ( ! /attribute\s+vec3\s+position\s*;/.test( originalVertexShader ) || - ! /attribute\s+vec3\s+normal\s*;/.test( originalVertexShader ) ) { - - console.warn( 'THREE.OutlineEffect requires both vec3 position and normal attributes in vertex shader, ' + - 'does not draw outline for ' + originalMaterial.name + '(uuid:' + originalMaterial.uuid + ') material.' ); - - return createInvisibleMaterial(); - - } - - } else if ( originalMaterial.isShaderMaterial === true ) { - - originalUniforms = originalMaterial.uniforms; - originalVertexShader = originalMaterial.vertexShader; - - } else { - - return createInvisibleMaterial(); - - } - - var uniforms = Object.assign( {}, originalUniforms, uniformsChunk ); - - var vertexShader = originalVertexShader - // put vertexShaderChunk right before "void main() {...}" - .replace( /void\s+main\s*\(\s*\)/, vertexShaderChunk + '\nvoid main()' ) - // put vertexShaderChunk2 the end of "void main() {...}" - // Note: here assums originalVertexShader ends with "}" of "void main() {...}" - .replace( /\}\s*$/, vertexShaderChunk2 + '\n}' ) - // remove any light related lines - // Note: here is very sensitive to originalVertexShader - // TODO: consider safer way - .replace( /#include\s+<[\w_]*light[\w_]*>/g, '' ); - - var defines = {}; - - if ( ! /vec3\s+transformed\s*=/.test( originalVertexShader ) && - ! /#include\s+/.test( originalVertexShader ) ) defines.DECLARE_TRANSFORMED = true; + function createMaterial() { return new ShaderMaterial( { - defines: defines, - uniforms: uniforms, + uniforms: UniformsUtils.merge( [ + UniformsLib[ 'fog' ], + UniformsLib[ 'displacementmap' ], + uniformsOutline + ] ), vertexShader: vertexShader, fragmentShader: fragmentShader, - side: BackSide, - //wireframe: true, - skinning: false, - morphTargets: false, - morphNormals: false, - fog: false + side: BackSide } ); } @@ -253,7 +197,7 @@ var OutlineEffect = function ( renderer, parameters ) { if ( data === undefined ) { data = { - material: createMaterial( originalMaterial ), + material: createMaterial(), used: true, keepAlive: defaultKeepAlive, count: 0 @@ -281,9 +225,32 @@ var OutlineEffect = function ( renderer, parameters ) { } + function isCompatible( object ) { + + var geometry = object.geometry; + var hasNormals = false; + + if ( object.geometry !== undefined ) { + + if ( geometry.isBufferGeometry ) { + + hasNormals = geometry.attributes.normal !== undefined; + + } else { + + hasNormals = true; // the renderer always produces a normal attribute for Geometry + + } + + } + + return ( object.isMesh === true && object.material !== undefined && hasNormals === true ); + + } + function setOutlineMaterial( object ) { - if ( object.material === undefined ) return; + if ( isCompatible( object ) === false ) return; if ( Array.isArray( object.material ) ) { @@ -306,7 +273,7 @@ var OutlineEffect = function ( renderer, parameters ) { function restoreOriginalMaterial( object ) { - if ( object.material === undefined ) return; + if ( isCompatible( object ) === false ) return; if ( Array.isArray( object.material ) ) { @@ -351,6 +318,14 @@ var OutlineEffect = function ( renderer, parameters ) { } + if ( originalMaterial.displacementMap ) { + + material.uniforms.displacementMap.value = originalMaterial.displacementMap; + material.uniforms.displacementScale.value = originalMaterial.displacementScale; + material.uniforms.displacementBias.value = originalMaterial.displacementBias; + + } + } function updateOutlineMaterial( material, originalMaterial ) { @@ -363,6 +338,9 @@ var OutlineEffect = function ( renderer, parameters ) { material.morphTargets = originalMaterial.morphTargets; material.morphNormals = originalMaterial.morphNormals; material.fog = originalMaterial.fog; + material.toneMapped = originalMaterial.toneMapped; + material.premultipliedAlpha = originalMaterial.premultipliedAlpha; + material.displacementMap = originalMaterial.displacementMap; if ( outlineParameters !== undefined ) { @@ -389,6 +367,18 @@ var OutlineEffect = function ( renderer, parameters ) { if ( originalMaterial.wireframe === true || originalMaterial.depthTest === false ) material.visible = false; + if ( originalMaterial.clippingPlanes ) { + + material.clipping = true; + + material.clippingPlanes = originalMaterial.clippingPlanes; + material.clipIntersection = originalMaterial.clipIntersection; + material.clipShadows = originalMaterial.clipShadows; + + } + + material.version = originalMaterial.version; // update outline material if necessary + } function cleanupCache() {