How to create simple fresnel shader in emissive

I’m trying to create a simple fresnel effect in the emissive channel of the material, using a shader chunk. It seems like something like this should work for a very basic effect:

EmissiveFresnel.prototype.initialize = function() {
    var m = this.entity.model.meshInstances[parseInt(this.MeshInstance)].material;

    m.chunks.emissivePS = 
        "uniform sampler2D texture_emissiveMap;\n" +
        "\n" +
        "vec3 getEmission() {\n" +
        "    vec3 emission = max(dot(dNormalW, dViewDirW), 0.0);" + 
        "    return emission;\n" +
        "}\n\n";
    m.update();
};

But when I run it I get this error: “Cannot read property ‘samplers’ of null”

Obviously, I’m missing something fundamental here, what’s going on?

Hi @steve_wk,

Not sure why you get such a message, the code seems mostly right. I’d say if you aren’t using the texture_emissiveMap sampley uniform then remove that from your code.

If you keep getting that error try sharing a sample project url to take a look.

I tried removing that line, but getting the same error.
Here is an example project: https://playcanvas.com/project/736718/overview/emissivefresnel

Ah so, the error I am getting is a dimension mismatch, and it makes sense. Since the result of your max/dot operation results in a float which can’t be assigned to a vec3.

image

A simple fix is to get a homogenous vec3 is this:

vec3 emission = vec3(max(dot(dNormalW, dViewDirW), 0.0));

Ah, OK, makes sense.

However, that doesn’t seem to actually produce any result. I’ve deleted the light in the scene so I can see the emissive easily, and there’s no emissive.

Simply putting something like:

return vec3(1.0, 0.0, 0.0);

Works fine, so the shader chunk is working. Am I using the wrong variables to get the dot between normal and view direction?

I think it does, it just depends from what angle you see it. Disabling all lights though I think it makes the material shader revert to a simpler pipeline that doesn’t use this chunk.

Try lowering your light intensity to 0.01 and you will get something like this:

Ah, that was it. Didn’t realise the absence of an active light in the scene would change the rendering path. That’s good to know!

Final question: is there any way to preview shaders built like this in the editor, without needing to launch?

Right there isn’t an official way of doing this right now, although editor scripting is in the official roadmap.

I’ve built an editor scripting SDK that can do that, in the meantime, take a look at this repo on how to enable it:

Oh nice, that looks amazing, will definitely check it out, thanks!

1 Like

Coming back to this as i am trying to get my head around shader chunks as well :slight_smile:
I ended up with this shader chunk as emissivePS:

vec3 getEmission() {
    float fresnel = 0.0 + 1.0 * pow( 1.0 + dot(dViewDirW, dNormalW), 5.0);
    vec3 emission1 = vec3(1.0, 1.0, 1.0);
    vec3 emission2 = vec3(0.01, 0.01, 0.9);
    vec3 col = mix(emission1, emission2, fresnel); 
    return saturate(col);
}

as you can see, i hardcoded the values for the typical bias, power and scale, but that’s ok for the moment.
What really stole some time was the fact that i was using vec3(0,0,0) as emissive1 because i thought that would be black and NO emission at all. But somehow it turned out, that it had the opposite effect. The middle was white and the blueish emission was really hard to see, like this:

When i use vec3(1.0) as the “middle” color, i get the results as expected. Just the normal material on the middle and a blueish emission on the “edges”:

Any idea why it is that way? I thought mix would linear interpolate between the colors and black would be no emission… you see me confused…