Fresnel Effect on PBRMaterial Without Replacing Original Material

Hey everyone!

I’m working on a two-tone colored car model and trying to add a Fresnel effect to give it a more dynamic look.

This is the car before applying the effect:

This is after i have applied that effect.

I’ve written a custom shader and applied it via a ShaderMaterial, and visually it works how I want. However, the problem is:

Once I apply my ShaderMaterial to the mesh, it completely overrides the existing PBRMaterial—so I lose all the PBR features like metallic/roughness, reflections, and environmental lighting.

Here’s PlayGround, where i have recreated the issue.

Is there any way that, I can achieve the same effect more efficient way ? or am I missing something obvious here? :face_with_peeking_eye:

Thanks in advance for any help or suggestions! I really appreciate your time. :heart:

Hello :slight_smile:

A few hints :

  • Instead of using ShaderMaterial, use PBRCustomMaterial.
  • With a PBRCustomMaterial you won’t need to rewrite 100% of the code. For example, you can just insert some code such as :
const customPBR = new PBRCustomMaterial("customPBR", scene);
customPBR.Fragment_Before_FragColor(`
    // some stuff
    finalColor.rgb = whatever you compute as rgb color;
`);

Here is an example with custom (lower brighness) PBR


EDIT : @DarkHorse in your case, best would be to edit surfaceAlbedo using the Fragment_Custom_Albedo method :

3 Likes

You can also use a material plugin, for better integration with the material system (possibility to enable/disable the plugin, clone the material, etc):

1 Like

@Evgeni_Popov I just noticed that PBRCustomMaterial has no method for setting a uniform, such as setFloat or setVector3 … Is it intended ? :thinking:

You must use the observable material.onBindObservable to define the uniforms on the material effect. PBRCustomMaterial and CustomMaterial are the old way of customizing a material (but can be useful for quick prototyping); the preferred method is now to use a material plugin.

2 Likes

Hello there,

Thank you @Tricotou for your response!

I’ve implemented the PBRCustomMaterial solution as you suggested and it’s working great. Here’s the updated PlayGround version.

As @Evgeni_Popov mentioned, I used the onBindObservable observable to define uniforms.

I’ll definitely try using the Material plugin approach too and will update this thread once I have that working.

Thank you both so much for the help! :heart:

2 Likes

Hey guys,

Update: I’ve tried using the Material plugin approach! Here’s the PlayGround.

It’ll be a bit challenging to integrate with our system, but I’m up for the challenge! :flexed_biceps:
If I run into any issues, I might need to bug you again. :winking_face_with_tongue:

Thank you again @Tricotou and @Evgeni_Popov for all your help! :raising_hands: Really appreciate it!

1 Like

Hey folks,
Looks like I need your help a bit sooner than expected!

I’ve integrated the Material Plugin into our system, and it’s working beautifully. However, in this particular scene, we’re also using Depth of Field, which seems to be causing an issue — as you can see in the screenshot below:

When I disable Depth of Field, everything works smoothly, but I don’t think turning it off is the ideal solution here.
Any thoughts on what might be causing this?

Tagging @Tricotou and @Evgeni_Popov for their insights.

Are you sure the depth texture is correctly passed to the shader? For debugging purpose, you should try to display (gl_FragColor) what you read from the depth texture. Also, Spector can help you find your problem. If you are able to reproduce the problem in the Playground, we can try to have a look.

Thanks for the quick reply!

Just to clarify — I’m not using a custom shader for Depth of Field. I’m relying on Babylon’s built-in DefaultRenderingPipeline.

Here’s how I’m enabling and configuring the DoF effect in code:

// Depth of Field
this.postProcess.depthOfFieldEnabled = ppp.dof.enabled;

if (ppp.dof.enabled) {
  this.postProcess.depthOfField.focalLength = ppp.dof.focalLength;
  this.postProcess.depthOfField.fStop = ppp.dof.fStop;
  this.postProcess.depthOfField.lensSize = ppp.dof.lensSize;
  this.postProcess.depthOfFieldBlurLevel = ppp.dof.blurLevel;

  if (ppp.dof.dynamicDistance) {
    this.adjustDynamicDoF(true);
  } else {
    this.postProcess.depthOfField.focusDistance = ppp.dof.distance;
  }
} else {
  this.adjustDynamicDoF(false);
}

Note: You can ignore the adjustDynamicDoF function — it’s just updating the focusDistance depending on the active camera.

Everything works as expected until Depth of Field is turned on. When enabled, the material plugin’s output starts misbehaving (as shown in the screenshot I shared earlier). Disabling DoF immediately resolves the issue.

I haven’t directly worked with the depth texture, so I’m wondering if there’s an internal conflict between DoF’s depth texture and the Material Plugin.

I think best would be that you reproduce the same issue (with DOF) in a Playground, so that we can have a look :slight_smile:

just spit balling here, but everytime smth like this has happened to me it’s usually smth related to passing negative values thru frag color.

try clamping stuff outputColor = max(vec4(0.), finalColor); in the plugin

Why not use iridescence instead? glTF example here, with the “pearly” variant.