EnvMap with custom shader

I want to implement IBL (Image-Based Lighting) in THREE.js, and I’m looking into how to use environment maps. Below is my custom shader (fragment shader).

const fragmentShader = `
uniform samplerCube envMap;

in vec3 vReflect;
out vec4 color;

void main() {
    vec3 reflectColor = textureCube(envMap, vReflect).rgb;
    color = vec4(reflectColor, 1.0);
}`

I want to pass the environment map texture to the uniform samplerCube envMap; and wrote the following code as usual, but I wasn’t able to pass it correctly.

const material = new THREE.ShaderMaterial({
    glslVersion : THREE.GLSL3,
    uniforms    : {
        lightDirection : { value : new THREE.Vector3(1, 1, 1) },
        baseColor      : { value : new THREE.Color(1, 1, 1) },
        envMap         : { value : envMap }
    },
    vertexShader,
    fragmentShader,
    side : THREE.FrontSide,
});

After researching, it seems that if I set the environment map to scene.background as shown below, it will be passed to the fragment shader. However, this results in a completely unintended background being rendered. This behavior makes no sense to me, so I wonder why this is happening. Also, is there a way to handle this?

scene.background = envMap;

I found a previous post that mentioned the need to define “envMap” in the material properties. So, after getting scolded by the TypeScript compiler and Lint, I added the following code, but it didn’t solve the problem:

(material as any).envMap = reflectionCube;

A warning is output in the browser’s console as follows:

WebGL: INVALID_OPERATION: bindTexture: textures can not be used with multiple targets

I wrote code to reproduce my issue on JSFiddle.
If you comment out the code on line 27, the cube turns black.
Is there something wrong?

scene.background = envMap;

https://jsfiddle.net/yamasaki/0aeLhksj/61/

I had a go at solving your issue and I couldn’t figure it out either.

Just to rule out problems in your code, I created my own version based on your code, using the latest three (r171) and different shader code.

But it seems that you do need to have scene.background = envMap or it won’t work.

In my version, comment out line 91 and press ctrls to see the change,

1 Like

@seanwasere
Thank you for your cooperation. Could this be a bug in THREE.js?
Also, I’m curious why my sample appears jagged compared to your sample, even though the code is almost identical. Could it be because of a difference in the THREE.js version?
I’m struggling to figure out how to update the version on JSFiddle… I’ve been wrestling with the CDN.

(post deleted by author)

After several hours of struggle, I was able to update THREE.js on JSFiddle to the latest version. The jaggy issue has been resolved, but the main issue remains unresolved.
Deleting line 18 makes the screen completely black.

https://jsfiddle.net/yamasaki/0aeLhksj/

I agree… this is confusing behavior.

I think perhaps what you are loading isn’t a cubemap (not 6 faces loaded via cubemap loader) but a 2d equirectangular map… which would require using a sampler2D instead of samplerCube.

I switched to MeshStandardMaterial with envMap = your envmap and it works…
(without setting scene.environment or .background)

https://jsfiddle.net/manthrax/5evb02n8/

So perhaps you can see in the source of MeshStandardMaterial, what it is doing differently.

https://ycw.github.io/three-shaderlib-skim/dist/#/latest/standard/fragment

Also take a look at the method: .fromEquirectangularTexture on the cubeRenderTarget:

https://threejs.org/docs/#api/en/renderers/WebGLCubeRenderTarget.fromEquirectangularTexture

I think that was the issue.

I created a WebGLCubeRenderTarget and used .fromEquirectangularTexture( yourHDR )

and plugged it into your shader, and it works.

https://jsfiddle.net/manthrax/5evb02n8/9/

ps: fwiw MeshStandardMaterial does IBL out of the box:

https://threejs.org/examples/webgl_materials_envmaps_hdr.html

2 Likes

Yep, that was the answer. I’ve updated my example to be correct now.

2 Likes

@manthrax @seanwasere
Thank you very much.
It worked well with my sample too. I deeply appreciate both of your answer.
I wanted to apply IBL to the result of volume rendering CT images and was looking for a way to implement IBL with a custom shader.

1 Like

OH awesome! That’s a great goal. Respect.

1 Like