Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass lights into the shader in drawObject.ts. #11

Merged
merged 9 commits into from
May 3, 2018
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"gl-matrix": "^2.5.1",
"lodash": "^4.17.10",
"regl": "^1.3.1",
"ts-sinon": "^1.0.12",
"tslib": "~1.9.0"
}
}
10 changes: 9 additions & 1 deletion src/examples/render.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { Light } from '../renderer/interfaces/Light';
import { Renderer } from '../renderer/Renderer';

import { mat4, vec3 } from 'gl-matrix';
import { flatMap, range } from 'lodash';

const renderer = new Renderer(800, 600);
const light1: Light = { lightPosition: [10, 10, 10], lightColor: [1, 1, 1], lightIntensity: 256 };
const light2: Light = { lightPosition: [700, 500, 50], lightColor: [3, 3, 3], lightIntensity: 100 };

const renderer: Renderer = new Renderer(800, 600, 2);

// Add lights to the renderer
renderer.addLight(light1);
renderer.addLight(light2);

const transform = mat4.fromTranslation(mat4.create(), [0, 0, -4]);
const vertices: number[] = [];
Expand Down
46 changes: 42 additions & 4 deletions src/renderer/Renderer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Camera } from './Camera';
import { drawAxes, DrawAxesProps } from './commands/drawAxes';
import { drawObject, DrawObjectProps } from './commands/drawObject';
import { Light } from './interfaces/Light';

import { mat4, vec4 } from 'gl-matrix';

Expand All @@ -26,6 +27,7 @@ export interface RenderObject {
export class Renderer {
public readonly width: number;
public readonly height: number;
public readonly maxLights: number;
public readonly stage: HTMLDivElement;

public camera: Camera = new Camera();
Expand All @@ -34,13 +36,16 @@ export class Renderer {
private clearDepth: () => void;
private drawObject: REGL.DrawCommand<REGL.DefaultContext, DrawObjectProps>;
private drawAxes: REGL.DrawCommand<REGL.DefaultContext, DrawAxesProps>;
private lights: Light[];

private projectionMatrix: mat4 = mat4.create();
private ctx2D: CanvasRenderingContext2D;

constructor(width: number, height: number) {
constructor(width: number, height: number, maxLights: number) {
this.width = width;
this.height = height;
this.maxLights = maxLights;
this.lights = [];

// Create a single element to contain the renderer view
this.stage = document.createElement('div');
Expand Down Expand Up @@ -90,7 +95,7 @@ export class Renderer {
depth: 1
});

this.drawObject = drawObject(regl);
this.drawObject = drawObject(regl, this.maxLights);
this.drawAxes = drawAxes(regl);
}

Expand All @@ -105,15 +110,48 @@ export class Renderer {
positions: o.vertices,
normals: o.normals,
colors: o.colors,
indices: o.indices
})
indices: o.indices,
numLights: this.lights.length,
lights: this.lights
}, this.maxLights)
);

if (debug) {
this.drawCrosshairs();
}
}

/**
* Adds a light in the `lightPositions` match an entry in the `this.lights` array.
*
* @param {Light} light A light source to be added to the rendering context.
* @throws {RangeError} If the number of lights in `this.lights` would exceed `this.maxLights` by appending another
* light to `this.lights`.
*/
public addLight(light: Light) {
if (this.lights.length === this.maxLights) {
throw new RangeError(`Number of lights must be less than or equal to maxLights (${this.maxLights}).`);
}
this.lights.push(light);
}

/**
* Removes a light if the entry passed in matches an entry in the `this.lights` array.
*
* @param {Light} light A light source to be removed from the rendering context.
* @throws {RangeError} If the length of `this.lights` is equal to 0.
*/
public removeLight(light: Light) {
if (this.lights.length === 0) {
throw new RangeError(`Can't remove a light from an empty array.`);
}
this.lights.filter((l: Light) => l === light);
}

public getLights(): Light[] {
return this.lights;
}

private drawCrosshairs() {
this.clearDepth();

Expand Down
61 changes: 46 additions & 15 deletions src/renderer/commands/drawObject.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// tslint:disable-next-line:import-name
import REGL = require('regl');
import { range } from 'lodash';
import { blankLight, Light } from '../interfaces/Light';

// tslint:disable:no-unsafe-any

Expand All @@ -19,7 +21,7 @@ interface Attributes {
color: REGL.Vec3;
}

/*
/**
* All the information needed to be able to draw an object to the screen
*/
export interface DrawObjectProps {
Expand All @@ -32,12 +34,12 @@ export interface DrawObjectProps {
indices: number[];
}

/*
* Shader to draw a single object with Phong shading to the screen
/**
* Shader to draw a single object with Phong shading to the screen.
*
* @param {REGL.regl} regl The regl object factory to build a function to draw an object.
*/
export function drawObject(
regl: REGL.regl
): REGL.DrawCommand<REGL.DefaultContext, DrawObjectProps> {
export function drawObject(regl: REGL.regl, maxLights: number): REGL.DrawCommand<REGL.DefaultContext, DrawObjectProps> {
return regl<Uniforms, Attributes, DrawObjectProps>({
vert: `
precision mediump float;
Expand All @@ -64,7 +66,7 @@ export function drawObject(
frag: `
precision mediump float;

const int MAX_LIGHTS = 1; // TODO(davepagurek) [#10] Increase this to allow more lights
const int MAX_LIGHTS = ${maxLights};

varying vec3 vertexPosition;
varying vec3 vertexNormal;
Expand Down Expand Up @@ -108,15 +110,44 @@ export function drawObject(
projection: regl.prop('projectionMatrix'),
view: regl.prop('cameraTransform'),
model: regl.prop('model'),
numLights: 1, // Note: must be <= MAX_LIGHTS in the shader

// TODO(davepagurek): [#10] When we increase MAX_LIGHTS, Regl will expect a property
// in this object for each array element, even if less than MAX_LIGHTS lights are
// passed in, so the rest will have to be filled with zeroed data
'lightPositions[0]': [10, 10, 10],
'lightIntensities[0]': 256,
'lightColors[0]': [1, 1, 1]
numLights: regl.prop('numLights'),
...buildLightMetadata(maxLights)
},
elements: regl.prop('indices')
});
}

// tslint:disable:newline-before-return
// tslint:disable:variable-name
// tslint:disable:typedef

/**
* Returns JSON metadata for lights to be passed into the `uniforms` object.
*
* NOTE: When we increase MAX_LIGHTS, Regl will expect a property in this object for each array
* element, even if less than MAX_LIGHTS lights are passed in, so the rest will have to be filled
* with zeroed data.
*
* @param {number} maxLights The maximum number of light points that may exist.
* @returns {{}} The JSON metadata for the lights.
*/
function buildLightMetadata(maxLights: number): {} {
const visibleLightsJSON: {}[] = range(maxLights).map((index: number) => {
return {
[`lightPositions[${index}]`]: (_context, props, _batch_id) => {
const light: Light | undefined = props.lights[index];
return light ? light.lightPosition : blankLight.lightPosition;
},
[`lightIntensities[${index}]`]: (_context, props, _batch_id) => {
const light: Light | undefined = props.lights[index];
return light ? light.lightIntensity : blankLight.lightIntensity;
},
[`lightColors[${index}]`]: (_context, props, _batch_id) => {
const light: Light = props.lights[index];
return light ? light.lightColor : blankLight.lightColor;
}
};
});

return visibleLightsJSON.reduce((accum: {}, obj: {}) => { return { ...accum, ...obj }; }, {});
}
13 changes: 13 additions & 0 deletions src/renderer/interfaces/Light.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import REGL = require('regl');

export interface Light {
lightPosition: REGL.Vec3[];
lightColor: REGL.Vec3[];
lightIntensity: number;
}

export const blankLight: Light = {
lightPosition: [0, 0, 0],
lightColor: [0, 0, 0],
lightIntensity: 0
}
31 changes: 31 additions & 0 deletions tests/Renderer/Renderer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Light } from '../../src/renderer/interfaces/Light';
import { Renderer } from '../../src/renderer/Renderer';

describe('Renderer', () => {
const maxLights: number = 3;
const light: Light = {
lightPosition: [1, 1, 1],
lightColor: [0, 0, 0],
lightIntensity: 256
}

xdescribe('addLight', () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#20

it('can append a new light to the `lights` array', () => {
const renderer: Renderer = new Renderer(800, 600, maxLights);
expect(renderer.getLights().length).toEqual(0);
renderer.addLight(light);
expect(renderer.getLights().length).toEqual(1);

});
});

xdescribe('removeLight', () => {
it('can remove an existing light from the `lights` array', () => {
const renderer: Renderer = new Renderer(800, 600, maxLights);
renderer.addLight(light);
expect(renderer.getLights().length).toEqual(1);
renderer.removeLight(light);
expect(renderer.getLights().length).toEqual(0);
});
});
});
Loading