CubeTexture does not appear to support indexing via surface normal. How would I add that?

THREE.CubeTexture is awesome for specular and refractive illumination. However, I am interested in using it for IBL - image based lighting. I have cube map textures that I have convolved for use for global diffuse illumination. THREE.CubeTexture supports specular maps - reflective/refractive. How could I adapt it to use diffuse maps?

Do you mean that THREE.MeshStandardMaterial supports specular maps? I believe you’ll just want to then create a new material that inherits from THREE.MeshStandardMaterial and then inject the shader code you want to handle image based lighting into the preexisting material.

It does support a light map, though I’m guessing cubemap based lighting would need to be based on surface normal and not UVs so there’s no way to use that pre-existing functionality.

Here’s a snippet I always use when I need a slightly different version of a THREE.js shader. Requires ES6 but it would work in ES5 albeit with more boilerplate.

class MeshCustomStandardMaterial extends THREE.MeshStandardMaterial {
  constructor(options) {
    super(options);
    //setup any new parameter here
  }

  //hooks into THREE.js renderer internals
  onBeforeCompile(shader, renderer){
    this.shader = shader;
    //add new uniforms here
    //shader.uniforms.cubemapLightmap = { value: this._myMap };

    //inject stuff into .vertexShader or .fragmentShader here
    //console.log(shader.fragmentShader); (debug logs help to read the fully compiled shader to pick your injection points
    //and example can be found below from one of my projects. It injects a new "varying float" which you'd probably want to inject your new CubeTexture, and then adds some extra code to play with PBR values based on that amount
    let injectPos3 = shader.fragmentShader.indexOf("#include <lights_physical_fragment>");
    let injectPos4 = shader.fragmentShader.indexOf("#include <fog_fragment>");
    shader.fragmentShader = `
      varying float spikeAmount;
      ${shader.fragmentShader.slice(0,injectPos3)}
      float perpToCamera = length(cross(normal, vViewPosition))/10.0;
      metalnessFactor *= perpToCamera;
      metalnessFactor += spikeAmount;
      totalEmissiveRadiance += spikeAmount*2.0;
      ${shader.fragmentShader.slice(injectPos3,injectPos4)}
      gl_FragColor *= perpToCamera;
      ${shader.fragmentShader.slice(injectPos4)}
    `;
  }
}

Here’s the full original code in case the stripped down example is confusing.

I believe I last tested this in r96

1 Like

Thanks so much for this Cobertos. :+1:t5: I’ll dig into the code.

As you point out the only different between a specular map fragment shader and a diffuse map frag shader is the former (specular) uses the reflection vector and the latter (diffuse) uses the surface normal. Perhaps a future version will provide it.

Oh, also, if anyone is interested in using/creating diffuse maps - cubic or lat-lon - I have an implementation of the OpenEXR image library that can do the cosine convolution (blur, basically) from specular map to diffuse map. This is a quick and dirty Mac OS app I used to learn about this stuff:

Feel free fo fork/clone whatever.

Cheers. Thanks again.

Cobertos, one other thing. Can you point me to the relevant reference docs on writing custom shaders? Cheers.

Yeah, the ShaderMaterial documentation has lots of info on how to write custom shaders (at least in the THREE.js ecosystem). Shader writing is itself a whole field almost so you’ll just have to google around to find stuff on general shader writing.

1 Like

Cool. I actually have a lot of experience writing shaders/renderers, etc. I’m just trying to navigate this awesome library. Cheers.

1 Like