Im attempting to add lights to my basic shader material. The expected behaviour is that i have a simple cube that i apply the ShaderMaterial to and it results in the same lights and colors as a Lambert Cube right next to it. However. Ive tried many different approaches. Ive tried passing all the lights into the material as uniforms and calculating the light and color data from those like so:
vec3 calcGlobalLight(vec3 baseColor) {
float normalDotLight = dot(abs(normalize(_DirToLight)), vec3(0.0, 1.0, 0.0));
float lightIntensity = 1.0 - (1.0 - normalDotLight);
vec3 lightCol = ((_Moon + _Sun) * lightIntensity);
vec3 diffuse = lightCol * (RECIPROCAL_PI * baseColor);
diffuse += _Ambient * baseColor;
return diffuse;
}
Here i have 3 lights that are added: Moon, Sun and Ambient. The moon and sun are simple directional lights with intensity and color. The ambient is just an ambient light. The normal is just up because this material will be used on flat surfaces only like an ocea.
While this somewhat works. It looks vastly different from the lambert material next to it.
So i dug through the three js docs and forums and found that you can include lights by setting lights: true on your material. I did this and came up with this code instead (fragment shader portion):
uniform vec3 diffuse;
uniform float opacity;
uniform vec3 ambientLightColor;
uniform sampler2D map;
struct DirectionalLight {
vec3 direction;
vec3 color;
};
uniform DirectionalLight directionalLights[NUM_DIR_LIGHTS];
varying vec3 vViewPosition;
varying vec3 vNormal;
vec3 getDirectionalIndirectLightIrradiance(const in DirectionalLight directionalLight, const in vec3 normal) {
vec3 irradiance = vec3(0.0);
vec3 lightDirection = directionalLight.direction;
float dotNL = max(dot(normal, lightDirection), 0.0);
irradiance += dotNL * directionalLight.color;
return irradiance;
}
void getDirectionalDirectLightIrradiance(const in DirectionalLight directionalLight, const in vec3 normal, out vec3 directLight) {
directLight = max(dot(normal, directionalLight.direction), 0.0) * directionalLight.color;
}
void main() {
vec4 diffuseColor = vec4(1.0, 0.0, 0.0, 1.0);
// Include map fragment functionality directly
#ifdef USE_MAP
vec4 texelColor = texture2D(map, gl_FragCoord.xy);
texelColor = mapTexelToLinear(texelColor);
diffuseColor *= texelColor;
#endif
struct ReflectedLight {
vec3 directDiffuse;
vec3 indirectDiffuse;
};
ReflectedLight reflectedLight;
reflectedLight.directDiffuse = vec3(0.0);
reflectedLight.indirectDiffuse = ambientLightColor;
vec3 normal = normalize(vNormal);
for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
reflectedLight.indirectDiffuse += diffuse * getDirectionalIndirectLightIrradiance(directionalLights[i], normal);
}
for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
vec3 directLight;
getDirectionalDirectLightIrradiance(directionalLights[i], normal, directLight);
reflectedLight.directDiffuse += directLight * diffuse;
}
#if defined RE_IndirectDiffuse
vec3 irradiance = ambientLightColor;
reflectedLight.indirectDiffuse += diffuse * irradiance;
#endif
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;
gl_FragColor = vec4(outgoingLight, diffuseColor.a);
#include <tonemapping_fragment>
#include <colorspace_fragment>
}
The result still differs (right = lambert, left = shader material)
I tried to go through the entire flow of the Lambert material to see what im missing. All i want to do is make a super basic version of lambert that doesnt include all the bloat and things like normal maps, specular and all this and only does the lighting calculations the same way as a lambert for color and map data.