Skip to content
This repository was archived by the owner on Nov 22, 2022. It is now read-only.

Commit a753952

Browse files
author
Lucas Crane
authored
Tweak Directional Light (#27)
* tweaks * default param * rename param * remove intermediate variable
1 parent 82d7f8b commit a753952

File tree

3 files changed

+50
-41
lines changed

3 files changed

+50
-41
lines changed

src/SoftDirectionalLight.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { DirectionalLight } from 'three';
22

33
export class SoftDirectionalLight extends DirectionalLight {
4-
constructor(...args) {
5-
super(...args);
6-
this.softness = 0.0;
4+
constructor(color, intensity, softness = 0) {
5+
super(color, intensity);
6+
this.softness = softness;
77
}
88

99
copy(source) {

src/renderer/envMapCreation.js

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,21 @@ export function generateEnvMapFromSceneComponents(directionalLights, environment
2020

2121
export 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) {
4044
export 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) {
5255
export 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

127135
function 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

139147
function 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

145152
export 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

150157
const 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
169176
function 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

175182
export 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+
}

src/renderer/rgbeToFloat.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
// Convert image data from the RGBE format to a 32-bit floating point format
22
// See https://www.cg.tuwien.ac.at/research/theses/matkovic/node84.html for a description of the RGBE format
33
// Optional multiplier argument for performance optimization
4-
export function rgbeToFloat(buffer, multiplier = 1) {
4+
export function rgbeToFloat(buffer, intensity = 1) {
55
const texels = buffer.length / 4;
66
const floatBuffer = new Float32Array(texels * 3);
77

8+
const expTable = [];
9+
for (let i = 0; i < 255; i++) {
10+
expTable[i] = intensity * Math.pow(2, i - 128) / 255;
11+
}
12+
813
for (let i = 0; i < texels; i++) {
914

1015
const r = buffer[4 * i];
1116
const g = buffer[4 * i + 1];
1217
const b = buffer[4 * i + 2];
1318
const a = buffer[4 * i + 3];
19+
const e = expTable[a];
1420

15-
const exponent = a - 128;
16-
// bit shift equivilent to e = Math.pow(2, exponent);
17-
const e = exponent > 0 ? 1 << exponent : 1 / (1 << (-1 * exponent));
18-
19-
floatBuffer[3 * i] = multiplier * r * e / 255;
20-
floatBuffer[3 * i + 1] = multiplier * g * e / 255;
21-
floatBuffer[3 * i + 2] = multiplier * b * e / 255;
21+
floatBuffer[3 * i] = r * e;
22+
floatBuffer[3 * i + 1] = g * e;
23+
floatBuffer[3 * i + 2] = b * e;
2224
}
2325

2426
return floatBuffer;

0 commit comments

Comments
 (0)