Material with emissionMap and fresnel shader

Hello!

Is there a possibility to have a material with both emission map and a Fresnel shader? I am working on a project where there is a mesh with an animated emissiveMap which, in order to look perfect, would need to have a Fresnel shader applied.

Is this possible somehow? Something like:

var awesomeMaterial = new THREE.AwesomeMaterial( {
            			uniforms: uniforms,
            			vertexShader: shader.vertexShader,
            			fragmentShader: shader.fragmentShader,
                        emissiveMap: animatedEmission[counter],
                        emissive: new THREE.Color(0xffffff),
                        emissiveIntensity : 1,
            		} );

I have been trying with ShaderMaterial which allows me to have the Fresnel shader but not the emission, and MeshStandardMaterial which allows the opposite, emission but not Fresnel.

Additionally I have been playing around and I came up with the following approach:

custom_material = new THREE.MeshStandardMaterial( {
    emissiveMap: animatedEmission[50], // array of emission textures for later animation
    emissive: new THREE.Color(0xffffff),
    emissiveIntensity : 1,
} );

custom_material.onBeforeCompile = function ( shader ) {

    var fresnelshader = THREE.FresnelShader;
    var fresnelshaderuniforms = THREE.UniformsUtils.clone( shader.uniforms );
    
    shader.uniforms.tCube= fresnelshaderuniforms.tcube;
    shader.uniforms.mFresnelBias = fresnelshaderuniforms.mFresnelBias;
    shader.uniforms.mFresnelPower = fresnelshaderuniforms.mFresnelPower;
    shader.uniforms.mFresnelScale = fresnelshaderuniforms.mFresnelScale;
    shader.uniforms.mRefractionRatio = fresnelshaderuniforms.mRefractionRatio;
    shader.vertexShader = fresnelshader.vertexShader;
    shader.fragmentShader = fresnelshader.fragmentShader;
};

This implementation indeed adds the fresnel shader to the material BUT it breaks the emissiveMap, emissive and emissiveIntensity.

Any idea why this happens and how to solve it?

Thanks!

It seems you are not using Material.onBeforeCompile() correctly. If you write code like so shader.vertexShader = fresnelshader.vertexShader;, then you completely overwrite the existing vertex shader with the one from THREE.FresnelShader. You always have to add code snippets to existing shader source or replace them like demonstrated in the example:

https://threejs.org/examples/webgl_materials_modified

But I’m afraid this will not work in your case since THREE.FresnelShader is a complete shader definition that you can’t add to MeshStandardMaterial. I suggest you try to enhance the code of THREE.FresnelShader instead.

Hello! Thank you very much for reply,

I have been playing around I came with the following code, which still, has problems. I have simplified the solution that I want to achieve dividing the shader that I apply to my mesh in 2 based on the Y position, one half with the classic emission and the other with the fresnel shader:

custom_material = new THREE.MeshStandardMaterial( {
    emissiveMap: texture_array[counter], // animated later on
    emissive: new THREE.Color(0x123fff),
    emissiveIntensity : 1,
} );

custom_material.onBeforeCompile = function ( shader ) {
    var a = shader.uniforms;
    var b = {
        "mRefractionRatio": { value: 1.02 },
        "mFresnelBias": { value: 0.1 },
        "mFresnelPower": { value: 2.0 },
        "mFresnelScale": { value: 1.0 },
        "tCube": { value: null }
    }
    shader.uniforms = THREE.UniformsUtils.merge( [ a, b ] );


    shader.vertexShader =
        [
            "#define PHYSICAL",
            "varying vec3 vViewPosition;",
            "#ifndef FLAT_SHADED",
                "varying vec3 vNormal;",
            "#endif",
            "#include <common>",
            "#include <uv_pars_vertex>",
            "#include <uv2_pars_vertex>",
            "#include <displacementmap_pars_vertex>",
            "#include <color_pars_vertex>",
            "#include <fog_pars_vertex>",
            "#include <morphtarget_pars_vertex>",
            "#include <skinning_pars_vertex>",
            "#include <shadowmap_pars_vertex>",
            "#include <logdepthbuf_pars_vertex>",
            "#include <clipping_planes_pars_vertex>",

            // FRESNEL SHADER
            "uniform float mRefractionRatio;",
            "uniform float mFresnelBias;",
            "uniform float mFresnelScale;",
            "uniform float mFresnelPower;",

            "varying vec3 vReflect;",
            "varying vec3 vRefract[3];",
            "varying float vReflectionFactor;",
            ///////

            "void main() {",


                "#include <begin_vertex>",
                "#include <project_vertex>",

                "vViewPosition = - mvPosition.xyz;",

                "if(vViewPosition.y < 0.0){",

                    // FRESNEL SHADER
                    "mvPosition = modelViewMatrix * vec4( position, 1.0 );",
                    "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",

                    "vec3 worldNormal = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );",

                    "vec3 I = worldPosition.xyz - cameraPosition;",

                    "vReflect = reflect( I, worldNormal );",
                    "vRefract[0] = refract( normalize( I ), worldNormal, mRefractionRatio );",
                    "vRefract[1] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.99 );",
                    "vRefract[2] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.98 );",
                    "vReflectionFactor = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( I ), worldNormal ), mFresnelPower );",

                    "gl_Position = projectionMatrix * mvPosition;",
                    
                    //////////////////
                "}",
                "else{",
                    "#include <displacementmap_vertex>",
                    "#include <morphtarget_vertex>",
                    "#include <skinning_vertex>",
                    "#include <logdepthbuf_vertex>",
                    "#include <clipping_planes_vertex>",
                    "#include <uv_vertex>",
                    "#include <uv2_vertex>",
                    "#include <color_vertex>",
                    "#include <beginnormal_vertex>",
                    "#include <morphnormal_vertex>",
                    "#include <skinbase_vertex>",
                    "#include <skinnormal_vertex>",
                    "#include <defaultnormal_vertex>",
                    "#ifndef FLAT_SHADED",
                        "vNormal = normalize( transformedNormal );",
                    "#endif",
                    "#include <worldpos_vertex>",
                    "#include <shadowmap_vertex>",
                    "#include <fog_vertex>",
                "}",

            "}",
        ].join( '\n' )

    shader.fragmentShader =
        [
            "#define PHYSICAL",
            "uniform vec3 diffuse;",
            "uniform vec3 emissive;",
            "uniform float roughness;",
            "uniform float metalness;",
            "uniform float opacity;",
            "#ifndef STANDARD",
                "uniform float clearCoat;",
                "uniform float clearCoatRoughness;",
            "#endif",
            "varying vec3 vViewPosition;",
            "#ifndef FLAT_SHADED",
                "varying vec3 vNormal;",
            "#endif",

            "#include <common>",
            "#include <packing>",
            "#include <dithering_pars_fragment>",
            "#include <color_pars_fragment>",
            "#include <uv_pars_fragment>",
            "#include <uv2_pars_fragment>",
            "#include <map_pars_fragment>",
            "#include <alphamap_pars_fragment>",
            "#include <aomap_pars_fragment>",
            "#include <lightmap_pars_fragment>",
            "#include <emissivemap_pars_fragment>",
            "#include <bsdfs>",
            "#include <cube_uv_reflection_fragment>",
            "#include <envmap_pars_fragment>",
            "#include <envmap_physical_pars_fragment>",
            "#include <fog_pars_fragment>",
            "#include <lights_pars_begin>",
            "#include <lights_physical_pars_fragment>",
            "#include <shadowmap_pars_fragment>",
            "#include <bumpmap_pars_fragment>",
            "#include <normalmap_pars_fragment>",
            "#include <roughnessmap_pars_fragment>",
            "#include <metalnessmap_pars_fragment>",
            "#include <logdepthbuf_pars_fragment>",
            "#include <clipping_planes_pars_fragment>",

            // FRESNEL SHADER
            "uniform samplerCube tCube;",

            "varying vec3 vReflect;",
            "varying vec3 vRefract[3];",
            "varying float vReflectionFactor;",
            ////////////////

            "void main() {",

                "if(vViewPosition.y < 0.0){",

                    // FRESNEL SHADER
                    "vec4 reflectedColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );",
                    "vec4 refractedColor = vec4( 1.0 );",

                    "refractedColor.r = textureCube( tCube, vec3( -vRefract[0].x, vRefract[0].yz ) ).r;",
                    "refractedColor.g = textureCube( tCube, vec3( -vRefract[1].x, vRefract[1].yz ) ).g;",
                    "refractedColor.b = textureCube( tCube, vec3( -vRefract[2].x, vRefract[2].yz ) ).b;",

                    "gl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) );",
                    //////
                "}",
                "else{",
                    "#include <clipping_planes_fragment>",
                    "vec4 diffuseColor = vec4( diffuse, opacity );",
                    "ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
                    "vec3 totalEmissiveRadiance = emissive;",
                    "#include <logdepthbuf_fragment>",
                    "#include <map_fragment>",
                    "#include <color_fragment>",
                    "#include <alphamap_fragment>",
                    "#include <alphatest_fragment>",
                    "#include <roughnessmap_fragment>",
                    "#include <metalnessmap_fragment>",
                    "#include <normal_fragment_begin>",
                    "#include <normal_fragment_maps>",
                    "#include <emissivemap_fragment>",
                    "#include <lights_physical_fragment>",
                    "#include <lights_fragment_begin>",
                    "#include <lights_fragment_maps>",
                    "#include <lights_fragment_end>",
                    "#include <aomap_fragment>",

                    "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;",
                    "gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
                    "#include <tonemapping_fragment>",
                    "#include <encodings_fragment>",
                    "#include <fog_fragment>",
                    "#include <premultiplied_alpha_fragment>",
                    "#include <dithering_fragment>",
                "}",
            "}"
        ].join( '\n' )

};

This setup breaks completely the fresnel shader and displays the half mesh with pure black and throws the following error more than 200 times: [.WebGL-0x80eb5969c00]RENDER WARNING: there is no texture bound to the unit 1

My ideal solution though would be to have a combination with them both which outputs the gl_FragColor combining both fresnel and standard. This way, when the emission channel has something inside outputs the fresnel shader with shine, and when the emission channel has nothing to apply, then the fresnel shader is purely shown:

fresnel_gl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) );
standard_gl_FragColor = vec4( outgoingLight, diffuseColor.a );
// and here the magic
gl_FragColor = fresnel_gl_FragColor + standard_gl_FragColor (combining them both somehow)

But I don know how to combine the color the fresnel outputs + the emission the standard material has, which sometimes is full emision and sometimes is cero (which will show purely the fresnel).

This is my first time using GLSL and I feel quite lost in this regard.

I attach some pictures to illustrate better the issue:

This picture shows the current situation of the code: top half of the mesh should have the fresnel shader but is black and the bottom part of the mesh shows the correct emissiveMap over no map at all, that is why is white
top_black_bottom_emission_over_white

The following picture shows the fresnel shader working correctly when I eliminate the outgoingLight from the bottom part, therefore removing the emissiveMap and leaving only the pure white material.
top_fresnel_shader_bottom_no_emission%20

As said, the perfect solution would be combining them boht, have the fresnel shader applied to the entire mesh and the emission map, which is animated, will pass sometimes in certain areas with full emission in another color and will leave the mesh like with the effect of a scanner light scanning it.

Sorry for the long post and thanks!