# Simple shader material with lighting support (Lambertian based)

**URL:** https://discourse.threejs.org/t/simple-shader-material-with-lighting-support-lambertian-based/31775
**Category:** Resources
**Tags:** shader-material
**Created:** [November 14, 2021, 10:09pm UTC](https://discourse.threejs.org/t/simple-shader-material-with-lighting-support-lambertian-based/31775 "2021-11-14T22:09:05Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![tfoller](https://avatars.discourse-cdn.com/v4/letter/t/3e96dc/32.png) [@tfoller](https://discourse.threejs.org/u/tfoller)
#### Post date: [November 14, 2021, 10:09pm UTC](https://discourse.threejs.org/t/simple-shader-material-with-lighting-support-lambertian-based/31775/1 "2021-11-14T22:09:06Z")

</div>

I’m aware of onBeforeCompile but I don’t find it convenient to work with due to the fact that it requires knowledge of internal library includes and working with customProgramCacheKey is also not very intuitive for me.

So, I wrote this shader as a default template, maybe it will be useful for somebody else who writes custom shaders and in need of simple illumination.

It is based on per-pixel Lambertian diffusion and provides the same results for Ambient and Directional lights and similar results for Point lights (except at a close distance).

Below is a split-canvas test for comparison, you can move split using buttons or arrow keys, or pan the camera.

Link:  
[https://jsfiddle.net/tfoller/r4tghjnz/9/](https://jsfiddle.net/tfoller/r4tghjnz/9/)

Screenshot:  
 ![test_lambert](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/8/a/8a10ca655c9300444c7b7b89224b0637473962de.png)

Here is the shader:

```javascript
<script type="x-shader/x-vertex" id="vs_lamb">

  // interpolated world positions and normals 
  varying mat2x3 vpn;

  void main() {
        
    vec4 pos = vec4(position, 1.0);
    vec4 nrm = vec4(normal, 0.0);    

    gl_Position = projectionMatrix * modelViewMatrix * pos;
    
    vpn = mat2x3(modelMatrix * mat2x4(pos, nrm));
    vpn[1] = normalize(vpn[1]);
  }
</script>
<script type="x-shader/x-vertex" id="fs_lamb">
   
  struct ambientLight {
    vec3 color;
    float intensity;
  };

  struct directLight {
    vec3 position;
    vec3 color;
    float intensity;
  };

  struct pointLight {
    vec3 position;
    vec3 color;
    float intensity;
    float power;
    float range;
    float decay;
  };

  uniform vec3 dif_color;
  uniform ambientLight amb_light;
  uniform directLight dir_light;
  uniform pointLight pnt_light;
  
  varying mat2x3 vpn;

  void main() {
   
    // ambient light

    vec3 light = amb_light.color * amb_light.intensity;
    
    // direct light

    vec3 dl_pos = dir_light.position;
    vec3 dl_dir = normalize(dl_pos);
    float dl_dot = dot(dl_dir, vpn[1]);      
    light += clamp(dl_dot, 0.0, 1.0) * dir_light.color * dir_light.intensity;

    // point light

    vec3 pl_pos = pnt_light.position;
    vec3 pl_dist = pl_pos - vpn[0];
    float range = length(pl_dist);
    if(pnt_light.range == 0.0 || pnt_light.range >= range) {

      vec3 pl_dir = normalize(pl_dist); 
      float pl_dot = dot(pl_dir, vpn[1]);
      float decay = 1.0;
      if(pnt_light.decay > 0.0) 
        decay = pnt_light.power * pow(range, -pnt_light.decay);    
      light += clamp(pl_dot, 0.0, 1.0) * pnt_light.color * pnt_light.intensity * decay;
    }

    gl_FragColor = vec4(light * dif_color, 1.0);
  }
</script>

```

And the most relevant code - setup for the shader mesh:

```javascript
const intensity = {amb: 0.2, dir: 0.8, pnt: 1.5};

const amb_light = new THREE.AmbientLight(0xfa0000, intensity.amb);
const dir_light = new THREE.DirectionalLight(0x008f00, intensity.dir);

const pl = { power: 1, decay: 0, };
const pnt_light = new THREE.PointLight(0xffffff, intensity.pnt, 1000, pl.decay);

const amb_uni = {
  color: Object.values(amb_light.color),
  get intensity() { return amb_light.intensity },
};

const dir_uni = {
  position: Object.values(dir_light.position), 
  color: Object.values(dir_light.color),
  get intensity() { return dir_light.intensity },
};

const pnt_uni = {
  position: Object.values(pnt_light.position), 
  color: Object.values(pnt_light.color),
  range: pnt_light.distance,
  get intensity() { return pnt_light.intensity },
  get power() { return pl.power },
  get decay() { return pl.decay },
};

const cube2 = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.ShaderMaterial( {
  uniforms: {
    dif_color: { value: Object.values(cube.material.color) },
    amb_light: { value: amb_uni },
    dir_light: { value: dir_uni },
    pnt_light: { get value() { return pnt_uni } },
  },
  vertexShader: getShader('vs_lamb'),
  fragmentShader: getShader('fs_lamb')
}),
);

```

---

<div class="post-metadata">

### Author: ![TheCat](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/thecat/32/14556_2.png) [@TheCat](https://discourse.threejs.org/u/TheCat)
#### Post date: [November 15, 2021, 4:01am UTC](https://discourse.threejs.org/t/simple-shader-material-with-lighting-support-lambertian-based/31775/2 "2021-11-15T04:01:28Z")

</div>

Cool, very nice of you to share, thanks 🙂

---

<div class="post-metadata">

### Author: ![tfoller](https://avatars.discourse-cdn.com/v4/letter/t/3e96dc/32.png) [@tfoller](https://discourse.threejs.org/u/tfoller)
#### Post date: [November 16, 2021, 6:44pm UTC](https://discourse.threejs.org/t/simple-shader-material-with-lighting-support-lambertian-based/31775/3 "2021-11-16T18:44:47Z")

</div>

While I’m on it, added a couple lines of code for specular reflections.

[https://jsfiddle.net/tfoller/89f1zw0r/5/](https://jsfiddle.net/tfoller/89f1zw0r/5/)
