How to make frosted glass (MeshPhysicMaterial) refraction each other

I’m trying to make a scene contains 3 or 4 frosted disc glass,they can refraction each other in diffrent viewPosition.

At first I used MeshBasicMaterial,then in material.onBeforeCompile I added some blur & chromatic aberration effect.It looks like this

What I actually want to use is MeshPhysicsMaterial,I pasted the same code,then I noticed I need make a new uniform input for refractionMap(which generated by using WebGLCubeRenderTarget & CubeCamera). but things get weird,it only works for the 1st custom object in scene.reafraction texture in other objects appears to missing texture.it looks like the cubemap only loaded 3 or 4 faces,the others are missing.

the imgur gif link here,as u can see,some faces are missing,also when the viewposition changed to the specified position,the display of cubemap would become wrong.

can anyone tell me how to fix this? This twitter feed also illustrates the problem

the cubeMap&cubeRenderTarget’s code

    cubeRenderTarget = new THREE.WebGLCubeRenderTarget( renderSize, { 
        generateMipmaps: true, 
        minFilter:THREE.NearestMipmapLinearFilter,
        magFilter:THREE.NearestMipmapLinearFilter,
    } );

    cubeRenderTarget.texture.mapping = THREE.CubeRefractionMapping

the material’s code

refractMaterial = new THREE.MeshPhysicalMaterial({
    transmission: options.transmission,
    thickness: options.thickness,
    roughness:options.roughness,
    envMap: bgTexture,
    envMapIntensity: options.envMapIntensity,
    clearcoat: options.clearcoat,
    clearcoatRoughness: options.clearcoatRoughness,
    normalScale: new THREE.Vector2(options.normalScale),
    normalMap: normalMapTexture,
    clearcoatNormalMap: normalMapTexture,
    clearcoatNormalScale: new THREE.Vector2(options.clearcoatNormalScale),
})

  refractMaterial.onBeforeCompile = shader => {
      shader.uniforms.refractionMap = { value: cubeRenderTarget.texture };
      shader.uniforms.refractionFactor = { value: 0.985 };
      shader.fragmentShader = prefixFrag + shader.fragmentShader;
      shader.fragmentShader = shader.fragmentShader.replace(`#include <output_fragment>`,`
      //#include <envmap_fragment>
      vec3 cameraToFrag = normalize( vWorldPosition - cameraPosition );
      vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );
      vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionFactor ); //refractionRatio
      //vec3 reflectVec = vReflect;
      //vec4 envColor = textureCube( refractionMap, reflectVec,0. );


    // modified by orig frag shader
    float lodLvl = 0.0;
    float flipEnvMap = 1.0;
    vec3 dir = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );

    // iteration this in blur
    vec4 envColor = textureLod( refractionMap, dir,lodLvl); //textureCube

    // ##  chromatic aberration
    vec4 refractedColor = vec4( 1.0 );
    refractedColor.r = textureLod( refractionMap, vec3( flipEnvMap * vRefract[0].x, vRefract[0].yz ), lodLvl ).r;
    refractedColor.g = textureLod( refractionMap, vec3( flipEnvMap * vRefract[1].x, vRefract[1].yz ), lodLvl ).g;
    refractedColor.b = textureLod( refractionMap, vec3( flipEnvMap * vRefract[2].x, vRefract[2].yz ), lodLvl ).b;

    // ##  blur para
    float blurRadius = 0.015;
    int SAMPLING_RATE = 16;
    float bloomV = 1.;
    float r = blurRadius;
    vec3 vss = dir;
    int maxS = SAMPLING_RATE;
    vec3 c = envColor.rgb;
    vec3 rv;
    vec3 offset;
    float w, tw = 0.0;
    // ##  chromatic aberration
    vec3 d = refractedColor.rgb;
    vec3 rw0,rw1,rw2;
    float x0, tx0,x1,tx1,x2,tx2 = 0.0;

    // implement
    //.----------------
    //| RANDOM SAMPLING - look up texture maxS^2 times in a radius r around v
    for (int i=0; i<maxS*maxS; i++)
    {
        offset = randVec3(i) * r;
        // ##  blur
        rv = vss + offset;
        tw = length(rv); // account for sampling distance
        c += textureLod(refractionMap, rv,lodLvl).rgb * tw;
        w += tw*bloomV; // values less than 1 like 0.85 fake bloom of bright areas
                 // could be handy for specularity

        // ##  chromatic aberration
        rw0 = vec3( flipEnvMap * vRefract[0].x, vRefract[0].yz ) + offset;
        rw1 = vec3( flipEnvMap * vRefract[1].x, vRefract[1].yz ) + offset;
        rw2 = vec3( flipEnvMap * vRefract[2].x, vRefract[2].yz ) + offset;
        tx0 = length(rw0);
        tx1 = length(rw1);
        tx2 = length(rw2);
    
        vec4 resetColor = vec4(1.);
        resetColor.r = textureLod( refractionMap, rw0, lodLvl ).r;
        resetColor.g = textureLod( refractionMap, rw1, lodLvl ).g;
        resetColor.b = textureLod( refractionMap, rw2, lodLvl ).b;
    
        d += resetColor.rgb * vec3(tx0,tx1,tx2);
        x0 += tx0*bloomV;
        x1 += tx1*bloomV;
        x2 += tx2*bloomV;
    }
    envColor.rgb = c/(w+1.0);
    // ##  chromatic aberration
    refractedColor.rgb = vec3(d.x/(x0+1.),d.y/(x1+1.),d.z/(x2+1.));
    envColor = mix( refractedColor, envColor, clamp( vReflectionFactor, 0.0, 1.0 ) );

    float specularStrength = 1.0;
    float reflectivity = 0.95;

      #ifdef ENVMAP_BLENDING_MULTIPLY
        outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );
      #elif defined( ENVMAP_BLENDING_MIX )
        outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );
      #elif defined( ENVMAP_BLENDING_ADD )
        outgoingLight += envColor.xyz * specularStrength * reflectivity;
      #endif

    outgoingLight = mix( outgoingLight, envColor.xyz, 0.95 );
      #ifdef OPAQUE
      diffuseColor.a = 1.0;
      #endif
      #ifdef USE_TRANSMISSION
      diffuseColor.a *= transmissionAlpha + 0.1;
      #endif
      gl_FragColor = vec4( outgoingLight, envColor.a ); //vec4( outgoingLight, diffuseColor.a );
      `)

      shader.vertexShader = prefixVert + shader.vertexShader;
      shader.vertexShader = shader.vertexShader.replace(`#include <fog_vertex>`,`
      vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );
      vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );
      vReflect = refract( cameraToVertex, worldNormal, refractionFactor ); //refractionRatio
      
      float mRefractionRatio = 1.02;
      float mFresnelBias = 0.1;
      float mFresnelPower = 2.0;
      float mFresnelScale = 1.0;
      vRefract[0] = refract( normalize( cameraToVertex ), worldNormal, mRefractionRatio );
			vRefract[1] = refract( normalize( cameraToVertex ), worldNormal, mRefractionRatio * 0.99 );
			vRefract[2] = refract( normalize( cameraToVertex ), worldNormal, mRefractionRatio * 0.98 );
      vReflectionFactor = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( cameraToVertex ), worldNormal ), mFresnelPower );

      #include <fog_vertex>
      `)

      refractMaterial.userData.shader = shader;

here is what I do in render() function for two objects

    refractSphere.visible = false;
    cubeCamera.position.copy( camera.position );
    cubeCamera.update( renderer, scene );

    if ( refractSphere.material.userData.shader ) {
      refractSphere.material.userData.shader.uniforms.refractionMap.value = cubeRenderTarget.texture;

    }

    refractSphere2.visible = false;
    cubeCamera2.position.copy( camera.position );
    cubeCamera2.update( renderer, scene );
    
    if ( refractSphere2.material.userData.shader ) {
      refractSphere2.material.userData.shader.uniforms.refractionMap.value = cubeRenderTarget2.texture;

    }
    
    refractSphere.visible = true;
    refractSphere2.visible = true;

A bit of a wild guess - but it seems like parts of cube camera output are appearing / disappearing depending on the viewer position in the scene. Is there a chance your refraction calculations are modifying / reaching beyond the currently rendered camera frustum?

(May also be a bit easier to help if you could reproduce the issue on codepen, not necessarily the entire thing, just some minimal example.)

I’ve also found that cuberendertargets don’t work when transmission is enabled. Try generating your cubemaps before enabling transmission on the materials - that worked for me.

1 Like