Skip to content

Commit

Permalink
Merge pull request #11 from AndrewMcBurney/lights_in_shader
Browse files Browse the repository at this point in the history
Pass lights into the shader in `drawObject.ts`.
  • Loading branch information
armcburney committed May 3, 2018
2 parents 1e3005d + 11e9de5 commit 7afbf5d
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 24 deletions.
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', () => {
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

0 comments on commit 7afbf5d

Please sign in to comment.