Three.js: metallic standard material not affected by area / hemispherical light

Created on 26 Jun 2016  路  19Comments  路  Source: mrdoob/three.js

Description of the problem

the standard material is not affect by area or hemispherical lights when having a metallic value of 1.0.

when using These lights in a Scene, this results in metallic materials being generally darker than non-metallic materials.

what it Looks like (left is non-metallic, right is metallic, front is rough, back is smooth)
threjjsmats

what i expect (simulated by adding an Environment map)
threjjsmats2

the other way round, Environment maps which are not cubemaps do not affect non-metallic materials
threjjsmats3

Three.js version
  • [ ] Dev
  • [x] r78
  • [ ] ...

    Browser
  • [x] All of them

  • [ ] Chrome
  • [ ] Firefox
  • [ ] Internet Explorer

    OS
  • [x] All of them

  • [ ] Windows
  • [ ] Linux
  • [ ] Android
  • [ ] IOS
    Hardware Requirements (graphics card, VR Device, ...)

Most helpful comment

@Maksims

geometry.faceVertexUvs[ 1 ] = geometry.faceVertexUvs[ 0 ]; // Geometry

geometry.attributes.uv2 = geometry.attributes.uv; // BufferGeometry

All 19 comments

the standard material is not affect by area or hemispherical lights when having a metallic value of 1.0.

three.js does not support area lights (yet).

Hemisphere lights in three.js are a source of indirect diffuse (ambient) light which is only reflected diffusely. Pure metals have no diffuse component -- they only reflect specularly. The code is working properly.

what i expect (simulated by adding an Environment map)

MeshStandardMaterial is best used with an environment map if metalness is non-zero -- for exactly the reason above.

Environment maps which are not cube maps do not affect non-metallic materials

Diffusely reflected light from an environment map is currently only computed for cube maps.

@WestLangley Isn't the suggested "generated" cube reflection correct though? (Infinite plane with skyColor on top and color at the bottom)

three.js does not support area lights (yet).

i meant to say ambient light, but are area lights happening?

in a physical based shader, i would expect a light to always effect both specular and diffuse in the same way. it is about energy conservation, isnt it?

now i see that as missing envmap on diffuse and missing ambient on spec cancel out each other when used in parallel, but it is not a nice workflow to adjust the counterpart each time changes are done to one of them. its a constant back-and forth
although it looks reasonable when it is done as diffuse is 'stationary' and the spec envmap travels if the viewing angle changes

@mrdoob Sorry, I do not know what the OP is doing to generate these images, so I am not sure of the answer to your question.

@thmasn Sorry, I do not understand what you have written.

I think if you provide a live demo to demonstrate your concerns it will be helpful.

@WestLangley ok ill try again.

if a light does not affect specular / metallic materials (current state), it can not be considered physical correct, because the light energy 'disappears' into the void. this results in metallic materials being darker than non-metallic materials.(as seen in my first image) this is exactly what a physical workflow tries to avoid.

-->I think a hemispherical (as well as an ambient) light should affect specular and therefore metallic materials. If the reflected ray is reflected into the sky, it should reflect the sky color, if it reflects into the ground, it should reflect the ground color.

i tried to simulate this wanted behaviour in my second image by adding this environment map which matches the colors of the hemispherical light:
ng
this workaround has problems though. p.e.
1.i need to change the texture when adjusting the light and the other way round to achieve consistent results.

  1. the environment map is mapped to the camera instead of the reflection resulting in wrong reflections. (if the camera is upside down, the reflection is inverted)
    a positive side effect of this workaround is that diffuse calculation is cheap (hemi light) and static, yet specular lighting precise(envmap) and has a 'reflecting feel' to it

edit: maybe a gradient reflection would look better, best would be an option

@thmasn you are correct that in the case of an hemisphere light, we should have our metallic materials reflecting that light. It shouldn't be necessary to create a texture to represent them. This is just something that needs to be fixed in ThreeJS properly. Thanks for bringing this up.

Here is an implementation of a hemisphere light that supports specular - I haven't validated its correctness though:

https://github.com/hughsk/glsl-hemisphere-light/blob/master/index.glsl

@bhouston As three.js is currently implemented, a HemisphereLight encodes irradiance, which is the total energy received over the hemisphere defined by the surface normal. That is why it adds to indirect diffuse.

An AmbientLight encodes the same thing -- it is just a constant HemisphereLight.

Neither are physically-based, because they apply to just the diffuse component -- to the exclusion of the specular one.

For a specular reflection, you need to have something to reflect. A hemisphere light does not provide anything to reflect. Nor does an ambient light. (Why? Because they encode irradiance only -- not radiance.)

We have two choices if you want to have a consistent model.

  1. Physical material should not support AmbientLight and HemisphereLight. Users should specify a light probe (environment map) instead.
  2. We modify AmbientLight and HemisphereLight so they produce the same result as a light probe with the upper hemisphere of one color and the lower hemisphere of the second (or same) color.

I have found that specular reflection on metals in which the light source is constant everywhere does not look good. However, if you are happy with the look of option 2, then that is fine. If you are not, then only option 1 makes sense.

I am willing to go either route.

Hemispheric lights are popular in rendering so I think we need to for #2. I do not think it should be a hard change to make, we just need to ensure that the units for specular make sense compared to the irradiance for diffuse. I will not have time for this this week though, but I think the derivation is actually very easy, probably just multiply irradiance by the right constant.

@bhouston

Hemispheric lights are popular in rendering

Granted. The units are not a concern to me right now -- it is what to reflect. We have to specify something to reflect...

Question: So the way you want to think about it is that when a user specifies a HemisphereLight, we think of it as "short-hand" for specifying an IBL with 2 colors -- top and bottom?

Any idea how to handle the blurring part? Maybe we have to actually construct an IBL cube map internally, then everything will just work.

We can create new classes that do just that: THREE.IBLAmbient(), THREE.IBLHemisphere(), THREE.IBLSky().

Any idea how to handle the blurring part? Maybe we have to actually construct an IBL cube map internally, then everything will just work.

We could just view it as a sphere with an upper half of one color and a lower half of another color as the OP suggested. I believe that the existing diffuse hemispheric light is based on approximating the hemispheric contributions from a two halved light like that. We could likely check that approximation and fix it if it diverges significantly. Thus we would just fix our light by minimally changing it to match a physical one as the OP suggests.

This sounds good to me!

Here is an implementation of a hemisphere light that supports specular - I haven't validated its correctness though:

https://github.com/hughsk/glsl-hemisphere-light/blob/master/index.glsl

this Looks pretty good to me, it even has Parameters for specular strength and blurriness of the specular reflection

both make sense imo

it wouldnt break old Scenes which were created with a non-specular hemi light in mind. as Long as the Default specular influence is 0.

it wouldnt break old Scenes which were created with a non-specular hemi light in mind. as Long as the Default specular influence is 0.

I don't mind breaking old scenes in this case.

Have this been implemented?

UPD:
Seems like it has been, but when adding aoMap to StandardMaterial, it makes it pitch black again :(

@Maksims aoMaps requrire a second set of UVs.

@WestLangley is it adjustable? As there are many cases when same UV set used for AO map.

UPD: by patching THREE.ShaderChunk.aomap_fragment, globally can be switched. Perhaps can look into per case extensions, end of the day, it is open source ;)

@Maksims

geometry.faceVertexUvs[ 1 ] = geometry.faceVertexUvs[ 0 ]; // Geometry

geometry.attributes.uv2 = geometry.attributes.uv; // BufferGeometry

Closing due to age.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Bandit picture Bandit  路  3Comments

Horray picture Horray  路  3Comments

clawconduce picture clawconduce  路  3Comments

jack-jun picture jack-jun  路  3Comments

donmccurdy picture donmccurdy  路  3Comments