getNormal() Shader Chunk error on engine v1.6.1+

On engine patch 1.6.1+ an omni light that is casting shadows will cause a vertex shader compile error when trying to apply a normalMap shader chunk. Previous engine versions compile the shader with no errors. Maybe it has something to do with the updated lit-shader.js?

Code (full shader gist:631b43301b3a2306b039ddd7ab86200f · GitHub)

PlanetShader.baseVS = `
// START OF PLAYCANVAS BUILT INS
attribute vec3 vertex_position;
attribute vec3 vertex_normal;
attribute vec4 vertex_tangent;
attribute vec2 vertex_texCoord0;
attribute vec2 vertex_texCoord1;
attribute vec4 vertex_color;
uniform mat4 matrix_viewProjection;
uniform mat4 matrix_model;
uniform mat3 matrix_normal;
vec3 dPositionW;
mat4 dModelMatrix;
mat3 dNormalMatrix;
// END OF PLAYCANVAS BUILT INS END


uniform vec3 view_position;
uniform vec3 light0_position;

uniform float uTime;
uniform vec4 uSurfaceST;
uniform vec4 uCloudST;
uniform vec2 uCloudSpeed;

varying vec4 vSurfaceCloudUvs;
varying vec3 vEmissiveAmounts;
`;

PlanetShader.startVS = `
void main(void) {
    // START OF PLAYCANVAS BUILT INS
    gl_Position = getPosition();
    // END OF PLAYCANVAS BUILT INS

    //  Getting vertex normal here since we don't have access to dVertexNormalW in vertex shader
    vNormalW = getNormal();

    // Do the uv calculations per vertex, and pack into a vec4 to send to the fragment shader
    vSurfaceCloudUvs.xy = (vertex_texCoord0 + uSurfaceST.zw) * uSurfaceST.xy;
    vSurfaceCloudUvs.zw = (vertex_texCoord0 + uCloudST.zw) * uCloudST.xy + (uCloudSpeed.xy * vec2(uTime));

    // Do the fresnel calculations per vertex and pack into a vec2
    vec3 V = normalize(dPositionW.xyz - view_position.xyz);    
    vec3 N = normalize(vNormalW);
    vec3 L = normalize(dPositionW.xyz - light0_position.xyz);
    float LdotN = dot(L, N);

    float fresnel = pow(1.0 + dot(V, N), 4.0);
    float eclipse = 4.0 - LdotN;

    vEmissiveAmounts.x =  fresnel * eclipse + (fresnel * 0.1); // Regular atmosphere for non emissive planets. Maintly affects the lit side, with some view based mixed in to fake scattering
    vEmissiveAmounts.y = 1.0 - pow(1.0 + dot(V, N), 1.0); // Center based glow for emissive planets  
    vEmissiveAmounts.z = pow(max(0.0,LdotN),2.0); // Dot calculation for emissive map to show only on dark side of planet

`;

PlanetShader.normalMapPS = `

#ifdef MAPTEXTURE
uniform float material_bumpiness;
#endif

void getNormal() {
#ifdef MAPTEXTURE
    vec3 normalMap = unpackNormal(texture2DBias(uNormalMap, vSurfaceCloudUvs.xy, textureBias));
    normalMap = mix(vec3(0.0, 0.0, 1.0), normalMap, material_bumpiness);
    vec3 waterNormalMix = mix(vec3(0.0, 0.0, 1.0), normalMap, waterMask - (clouds.g * uCloudColor.w));
    dNormalW = normalize(dTBN * addNormalDetail(waterNormalMix));
#else
    dNormalW = dVertexNormalW;
#endif
}

`;

Error

Failed to compile vertex shader:

ERROR: 0:90: 'getNormal' : no matching overloaded function found
ERROR: 0:90: '=' : dimension mismatch
ERROR: 0:90: 'assign' : cannot convert from 'const mediump float' to 'out highp 3-component vector of float'

85:
86: gl_Position = getPosition();
87:
88:
89:
90: vNormalW = getNormal();
91:
92:
93:
94: vSurfaceCloudUvs.xy = (vertex_texCoord0 + uSurfaceST.zw) * uSurfaceST.xy;
95: vSurfaceCloudUvs.zw = (vertex_texCoord0 + uCloudST.zw) * uCloudST.xy + (uCloudSpeed.xy * vec2(uTime));

[object Object]

Error compiling shadow shader for material=Planet_Mat pass=10

[object Object]

Hi @Robert_Fikes_IV,

I’m not sure what’s the issue here, given overriding normalMapPS seems to work fine in v1.61.3 for me. Are you able to provide a simpler repro project?

Here are the steps I tried and it worked for me:

  1. Create a blank project with a simple material and one shadow casting omni light.

  2. Added a simple normalMapPS override shader chunk:

MaterialNormalMap.prototype.initialize = function () {

    const material = this.app.assets.find('Material Ground').resource;
    material.chunks.normalMapPS = `
void getNormal() {
    dNormalW = vec3(0.3, 1.0, -0.2);
}    
    `;
    material.update();
};

2 Likes

Here’s the project PlayCanvas 3D HTML5 Game Engine

@Gustav_Sterbrant - any thoughts on this one?

Hello everyone!

Looking at this, it seems the issue was introduced in this PR: Fix shadow map using vertex normals. by GSterbrant · Pull Request #4985 · playcanvas/engine · GitHub. We made this change to fix an issue where the backend shader generation would try to output to normals, but shadow passes don’t produce normals.

The same problem happens in your shader. Because you override the beginVS and startVS shaders, that triggers shadow passes to be generated with said shaders, and since shadow passes don’t produce normals, that’s where it all breaks :slight_smile:

I fixed the example provided by simply using #ifndef SHADOW_PASS around the code that should only be output for lit shaders. You can see the fork here:

https://playcanvas.com/editor/scene/1683897

Thanks,
Gustav

4 Likes

Thanks a lot for your feedback, appreciate it! Problem resolved

1 Like