@@ -20,17 +20,21 @@ export function generateEnvMapFromSceneComponents(directionalLights, environment
2020
2121export function initializeEnvMap ( environmentLights ) {
2222 let envImage ;
23+
2324 // Initialize map from environment light if present
2425 if ( environmentLights . length > 0 ) {
2526 // TODO: support multiple environment lights (what if they have different resolutions?)
2627 const environmentLight = environmentLights [ 0 ] ;
28+
2729 envImage = {
2830 width : environmentLight . map . image . width ,
2931 height : environmentLight . map . image . height ,
3032 data : environmentLight . map . image . data ,
3133 } ;
34+
3235 envImage . data = rgbeToFloat ( envImage . data , environmentLight . intensity ) ;
33- } else { // initialize blank map
36+ } else {
37+ // initialize blank map
3438 envImage = generateBlankMap ( DEFAULT_MAP_RESOLUTION . width , DEFAULT_MAP_RESOLUTION . height ) ;
3539 }
3640
@@ -40,7 +44,6 @@ export function initializeEnvMap(environmentLights) {
4044export function generateBlankMap ( width , height ) {
4145 const texels = width * height ;
4246 const floatBuffer = new Float32Array ( texels * 3 ) ;
43- floatBuffer . fill ( 0.0 ) ;
4447
4548 return {
4649 width : width ,
@@ -52,9 +55,11 @@ export function generateBlankMap(width, height) {
5255export function addDirectionalLightToEnvMap ( light , image ) {
5356 const sphericalCoords = new THREE . Spherical ( ) ;
5457 const lightDirection = light . position . clone ( ) . sub ( light . target . position ) ;
58+
5559 sphericalCoords . setFromVector3 ( lightDirection ) ;
5660 sphericalCoords . theta = ( Math . PI * 3 / 2 ) - sphericalCoords . theta ;
5761 sphericalCoords . makeSafe ( ) ;
62+
5863 return addLightAtCoordinates ( light , image , sphericalCoords ) ;
5964}
6065
@@ -63,58 +68,61 @@ function addLightAtCoordinates(light, image, originCoords) {
6368 const floatBuffer = image . data ;
6469 const width = image . width ;
6570 const height = image . height ;
66- const xTexels = ( floatBuffer . length / ( 3 * height ) ) ;
67- const yTexels = ( floatBuffer . length / ( 3 * width ) ) ;
68- const density = 8 ;
71+ const xTexels = floatBuffer . length / ( 3 * height ) ;
72+ const yTexels = floatBuffer . length / ( 3 * width ) ;
73+
6974 // default softness for standard directional lights is 0.01, i.e. a hard shadow
7075 const softness = light . softness || 0.01 ;
7176
7277 // angle from center of light at which no more contributions are projected
73- const threshold = findThreshold ( originCoords , softness ) ;
78+ const threshold = findThreshold ( softness ) ;
79+
7480 // if too few texels are rejected by the threshold then the time to evaluate it is no longer worth it
7581 const useThreshold = threshold < Math . PI / 5 ;
82+
7683 // functional trick to keep the conditional check out of the main loop
7784 const intensityFromAngleFunction = useThreshold ? getIntensityFromAngleDifferentialThresholded : getIntensityFromAngleDifferential ;
7885
79- let encounteredInThisRow = false ;
8086 let begunAddingContributions = false ;
8187 let currentCoords = new THREE . Spherical ( ) ;
82- let falloff ,
83- intensity ,
84- bufferIndex ;
8588
8689 // Iterates over each row from top to bottom
8790 for ( let i = 0 ; i < xTexels ; i ++ ) {
91+
92+ let encounteredInThisRow = false ;
93+
8894 // Iterates over each texel in row
8995 for ( let j = 0 ; j < yTexels ; j ++ ) {
90- bufferIndex = j * width + i ;
96+ const bufferIndex = j * width + i ;
9197 currentCoords = equirectangularToSpherical ( i , j , width , height , currentCoords ) ;
92- falloff = intensityFromAngleFunction ( originCoords , currentCoords , softness , threshold ) ;
98+ const falloff = intensityFromAngleFunction ( originCoords , currentCoords , softness , threshold ) ;
9399
94100 if ( falloff > 0 ) {
95101 encounteredInThisRow = true ;
96102 begunAddingContributions = true ;
97103 }
98- intensity = light . intensity * falloff ;
104+
105+ const intensity = light . intensity * falloff ;
99106
100107 floatBuffer [ bufferIndex * 3 ] += intensity * light . color . r ;
101108 floatBuffer [ bufferIndex * 3 + 1 ] += intensity * light . color . g ;
102109 floatBuffer [ bufferIndex * 3 + 2 ] += intensity * light . color . b ;
103110 }
111+
104112 // First row to not add a contribution since adding began
105113 // This means the entire light has been added and we can exit early
106114 if ( ! encounteredInThisRow && begunAddingContributions ) {
107115 return floatBuffer ;
108116 }
109- // reset for next row
110- encounteredInThisRow = false ;
111117 }
118+
112119 return floatBuffer ;
113120}
114121
115- function findThreshold ( originCoords , softness ) {
122+ function findThreshold ( softness ) {
116123 const step = Math . PI / 128 ;
117124 const maxSteps = ( 2.0 * Math . PI ) / step ;
125+
118126 for ( let i = 0 ; i < maxSteps ; i ++ ) {
119127 const angle = i * step ;
120128 const falloff = getFalloffAtAngle ( angle , softness ) ;
@@ -125,55 +133,54 @@ function findThreshold(originCoords, softness) {
125133}
126134
127135function getIntensityFromAngleDifferentialThresholded ( originCoords , currentCoords , softness , threshold ) {
128- let deltaPhi = getAngleDelta ( originCoords . phi , currentCoords . phi ) ;
129- let deltaTheta = getAngleDelta ( originCoords . theta , currentCoords . theta ) ;
136+ const deltaPhi = getAngleDelta ( originCoords . phi , currentCoords . phi ) ;
137+ const deltaTheta = getAngleDelta ( originCoords . theta , currentCoords . theta ) ;
130138
131139 if ( deltaTheta > threshold && deltaPhi > threshold ) {
132140 return 0 ;
133141 }
142+
134143 const angle = angleBetweenSphericals ( originCoords , currentCoords ) ;
135- const falloffCoeficient = getFalloffAtAngle ( angle , softness ) ;
136- return falloffCoeficient ;
144+ return getFalloffAtAngle ( angle , softness ) ;
137145}
138146
139147function getIntensityFromAngleDifferential ( originCoords , currentCoords , softness ) {
140148 const angle = angleBetweenSphericals ( originCoords , currentCoords ) ;
141- const falloffCoeficient = getFalloffAtAngle ( angle , softness ) ;
142- return falloffCoeficient ;
149+ return getFalloffAtAngle ( angle , softness ) ;
143150}
144151
145152export function getAngleDelta ( angleA , angleB ) {
146- let diff = Math . abs ( angleA - angleB ) % ( 2 * Math . PI ) ;
153+ const diff = Math . abs ( angleA - angleB ) % ( 2 * Math . PI ) ;
147154 return diff > Math . PI ? ( 2 * Math . PI - diff ) : diff ;
148155}
149156
150157const angleBetweenSphericals = function ( ) {
151158 const originVector = new THREE . Vector3 ( ) ;
152159 const currentVector = new THREE . Vector3 ( ) ;
153- const getAngle = function ( originCoords , currentCoords ) {
160+
161+ return ( originCoords , currentCoords ) => {
154162 originVector . setFromSpherical ( originCoords ) ;
155163 currentVector . setFromSpherical ( currentCoords ) ;
156164 return originVector . angleTo ( currentVector ) ;
157- }
158- return getAngle ;
165+ } ;
159166} ( ) ;
160167
161168 // TODO: possibly clean this up and optimize it
162169 //
163- // This function was arrived at through experimentation, it provides good
170+ // This function was arrived at through experimentation, it provides good
164171 // looking results with percieved softness that scale relatively linearly with
165172 // the softness value in the 0 - 1 range
166173 //
167174 // For now it doesn't incur too much of a performance penalty because for most of our use cases (lights without too much softness)
168175 // the threshold cutoff in getIntensityFromAngleDifferential stops us from running it too many times
169176function getFalloffAtAngle ( angle , softness ) {
170- const softnessCoeficient = Math . pow ( 2 , 14.5 * Math . max ( 0.001 , ( 1.0 - clamp ( softness , 0.0 , 1.0 ) ) ) ) ;
171- const falloff = Math . pow ( softnessCoeficient , 1.1 ) * Math . pow ( 8 , softnessCoeficient * - 1 * ( Math . pow ( angle , 1.8 ) ) ) ;
177+ const softnessCoefficient = Math . pow ( 2 , 14.5 * Math . max ( 0.001 , 1.0 - clamp ( softness , 0.0 , 1.0 ) ) ) ;
178+ const falloff = Math . pow ( softnessCoefficient , 1.1 ) * Math . pow ( 8 , - softnessCoefficient * Math . pow ( angle , 1.8 ) ) ;
172179 return falloff ;
173180}
174181
175182export function equirectangularToSpherical ( x , y , width , height , target ) {
176183 target . phi = ( Math . PI * y ) / height ;
177184 target . theta = ( 2.0 * Math . PI * x ) / width ;
178185 return target ;
179- }
186+ }
0 commit comments