EnvMap¶
EnvMap for User¶
Three.js envMap is designed can be used by ShaderMaterial. Here are some example:
which won’t run without these lines:
materialShader.envMap = envMap;
materialShader.combine = THREE.MultiplyOperation;
materialShader.uniforms.envMap.value = envMap;
See discussion here.
Some Essential Parts¶
- Shader of MeshLambertMaterial for the above example.
The fragment shader is:
src/renderers/shaders/ShaderLib/meshlambert_frag.glsl.js
#include <common>
...
#include <envmap_common_pars_fragment>
#include <envmap_pars_fragment>
void main() {
...
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;
#include <envmap_fragment>
gl_FragColor = vec4( outgoingLight, diffuseColor.a );
// include tonemapping and more
}
- Env texture sampling
Light direction is done by vertex shader:
src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js
vec3 cameraToVertex;
if ( isOrthographic ) {
cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );
} else {
cameraToVertex = normalize( worldPosition.xyz - cameraPosition );
}
vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );
#ifdef ENVMAP_MODE_REFLECTION
vReflect = reflect( cameraToVertex, worldNormal );
#else
vReflect = refract( cameraToVertex, worldNormal, refractionRatio );
#endif
Then env texture been sampled in fragment shader like:
src/renderers/shaders/ShaderChunk/envmap_fragement.glsl.js
#ifdef ENVMAP_TYPE_CUBE
vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );
#elif defined( ENVMAP_TYPE_CUBE_UV )
vec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );
#else
vec4 envColor = vec4( 0.0 );
#endif
The uniforms used is:
uniform float envMapIntensity;
uniform float flipEnvMap;
uniform int maxMipLevel;
#ifdef ENVMAP_TYPE_CUBE
uniform samplerCube envMap;
#else
uniform sampler2D envMap;
#endif
See example of cube map: Three.js example: envmap.
- Uniforms
flipEnvMap is set by default with -1 in UniformsLib
const UniformsLib = {
...
envmap: {
envMap: { value: null },
flipEnvMap: { value: - 1 },
...
},
...
}
flipEnvMap should be changed to ‘1’ if not cube texture used, e.g. WebGlBackground will change this according to backgound.isCubeTexture.
Revert normal vector¶
To get reflected direction, transformed normal vector must been reverted [1] [2].
A normal vector can be got from reverting a transformed normal vector[2]. The Three.js way breaks this into some separate parts.
src/renderers/shaders/ShaderChunk/common.glsl.js
- and [5] shows this is correct for normal vector.
vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {
return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );
}
src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js
vec3 transformedNormal = objectNormal;
transformedNormal = normalMatrix * transformedNormal;
src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js
vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );
We want invert transpose normal,
which can be done by
where
\(M_n =\) normalMatrix,
\(M_v =\) viewmatrix.
Because \((M_v)_{3x3} = M_n^{-1}\) according to [3, 4, 5]. (And there is no difference in glsl for \(n^T\) or \(n\)?)
References
[1] Transforming normals with the transpose of the inverse of the modelview matrix
[3] viewMatrix = camera.matrixWorldInverse, WebGLProgram, Three.js docs
[4] viewMatrix = camera.matrixWorldInverse, Stackoverflow
[5] Inverse of transformation matrix, Mathematics, stackexchange
[6] Three.js Backgrounds and Skyboxes, Three.js Fundamentals