Skip to content

Commit e13c9ca

Browse files
authored
Merge pull request #2449 from Spongman/per-pixel-lighting Phong Specular Lighting
webgl: Per pixel lighting adds `setAttributes('perPixelLighting', boolean)` for Phong Specular-style lighting
2 parents 40dcd39 + 296afbc commit e13c9ca

File tree

10 files changed

+313
-17
lines changed

10 files changed

+313
-17
lines changed

src/webgl/light.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ p5.prototype.ambientLight = function(v1, v2, v3, a) {
137137
* background(0);
138138
* //move your mouse to change light direction
139139
* var dirX = (mouseX / width - 0.5) * 2;
140-
* var dirY = (mouseY / height - 0.5) * -2;
141-
* directionalLight(250, 250, 250, dirX, dirY, 0.25);
140+
* var dirY = (mouseY / height - 0.5) * 2;
141+
* directionalLight(250, 250, 250, -dirX, -dirY, 0.25);
142142
* ambientMaterial(250);
143143
* sphere(50);
144144
* }
@@ -170,7 +170,9 @@ p5.prototype.directionalLight = function(v1, v2, v3, x, y, z) {
170170
//in case there's no material color for the geometry
171171
shader.setUniform('uMaterialColor', this._renderer.curFillColor);
172172

173-
this._renderer.directionalLightDirections.push(_x, _y, _z);
173+
// normalize direction
174+
var l = Math.sqrt(_x * _x + _y * _y + _z * _z);
175+
this._renderer.directionalLightDirections.push(_x / l, _y / l, _z / l);
174176
shader.setUniform(
175177
'uLightingDirection',
176178
this._renderer.directionalLightDirections

src/webgl/p5.RendererGL.js

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ var defaultShaders = {
3131
__dirname + '/shaders/light_texture.frag',
3232
'utf-8'
3333
),
34+
phongVert: fs.readFileSync(__dirname + '/shaders/phong.vert', 'utf-8'),
35+
phongFrag: fs.readFileSync(__dirname + '/shaders/phong.frag', 'utf-8'),
3436
lineVert: fs.readFileSync(__dirname + '/shaders/line.vert', 'utf-8'),
3537
lineFrag: fs.readFileSync(__dirname + '/shaders/line.frag', 'utf-8')
3638
};
@@ -59,6 +61,8 @@ p5.RendererGL = function(elt, pInst, isMainCanvas, attr) {
5961
attr.preserveDrawingBuffer === undefined
6062
? true
6163
: attr.preserveDrawingBuffer;
64+
this.attributes.perPixelLighting =
65+
attr.perPixelLighting === undefined ? false : attr.perPixelLighting;
6266
this._initContext();
6367
this.isP3D = true; //lets us know we're in 3d mode
6468
this.GL = this.drawingContext;
@@ -189,9 +193,12 @@ p5.RendererGL.prototype._resetContext = function(attr, options, callback) {
189193
}, 0);
190194
}
191195
};
192-
193196
/**
194-
*
197+
* @module Rendering
198+
* @submodule Rendering
199+
* @for p5
200+
*/
201+
/**
195202
* Set attributes for the WebGL Drawing context.
196203
* This is a way of adjusting ways that the WebGL
197204
* renderer works to fine-tune the display and performance.
@@ -219,6 +226,10 @@ p5.RendererGL.prototype._resetContext = function(attr, options, callback) {
219226
* (note that p5 clears automatically on draw loop)
220227
* default is true
221228
* <br><br>
229+
* perPixelLighting - if true, per-pixel lighting will be used in the
230+
* lighting shader.
231+
* default is false
232+
* <br><br>
222233
* @method setAttributes
223234
* @for p5
224235
* @param {String} key Name of attribute
@@ -227,7 +238,7 @@ p5.RendererGL.prototype._resetContext = function(attr, options, callback) {
227238
* <div>
228239
* <code>
229240
* function setup() {
230-
* createCanvas(150, 150, WEBGL);
241+
* createCanvas(100, 100, WEBGL);
231242
* }
232243
*
233244
* function draw() {
@@ -248,7 +259,7 @@ p5.RendererGL.prototype._resetContext = function(attr, options, callback) {
248259
* <div>
249260
* <code>
250261
* function setup() {
251-
* createCanvas(150, 150, WEBGL);
262+
* createCanvas(100, 100, WEBGL);
252263
* setAttributes('antialias', true);
253264
* }
254265
*
@@ -265,6 +276,59 @@ p5.RendererGL.prototype._resetContext = function(attr, options, callback) {
265276
* </code>
266277
* </div>
267278
*
279+
* <div>
280+
* <code>
281+
* // press the mouse button to enable perPixelLighting
282+
* function setup() {
283+
* createCanvas(100, 100, WEBGL);
284+
* noStroke();
285+
* fill(255);
286+
* }
287+
*
288+
* var lights = [
289+
* { c: '#f00', t: 1.12, p: 1.91, r: 0.2 },
290+
* { c: '#0f0', t: 1.21, p: 1.31, r: 0.2 },
291+
* { c: '#00f', t: 1.37, p: 1.57, r: 0.2 },
292+
* { c: '#ff0', t: 1.12, p: 1.91, r: 0.7 },
293+
* { c: '#0ff', t: 1.21, p: 1.31, r: 0.7 },
294+
* { c: '#f0f', t: 1.37, p: 1.57, r: 0.7 }
295+
* ];
296+
*
297+
* function draw() {
298+
* var t = millis() / 1000 + 1000;
299+
* background(0);
300+
* directionalLight(color('#222'), 1, 1, 1);
301+
*
302+
* for (var i = 0; i < lights.length; i++) {
303+
* var light = lights[i];
304+
* pointLight(
305+
* color(light.c),
306+
* p5.Vector.fromAngles(t * light.t, t * light.p, width * light.r)
307+
* );
308+
* }
309+
*
310+
* specularMaterial(255);
311+
* sphere(width * 0.1);
312+
*
313+
* rotateX(t * 0.77);
314+
* rotateY(t * 0.83);
315+
* rotateZ(t * 0.91);
316+
* torus(width * 0.3, width * 0.07, 30, 10);
317+
* }
318+
*
319+
* function mousePressed() {
320+
* setAttributes('perPixelLighting', true);
321+
* noStroke();
322+
* fill(255);
323+
* }
324+
* function mouseReleased() {
325+
* setAttributes('perPixelLighting', false);
326+
* noStroke();
327+
* fill(255);
328+
* }
329+
* </code>
330+
* </div>
331+
*
268332
* @alt a rotating cube with smoother edges
269333
*/
270334
/**
@@ -278,13 +342,17 @@ p5.prototype.setAttributes = function(key, value) {
278342
var attr;
279343
if (typeof value !== 'undefined') {
280344
attr = {};
281-
attr.key = value;
345+
attr[key] = value;
282346
} else if (key instanceof Object) {
283347
attr = key;
284348
}
285349
this._renderer._resetContext(attr);
286350
};
287351

352+
/**
353+
* @class p5.RendererGL
354+
*/
355+
288356
p5.RendererGL.prototype._computeCameraDefaultSettings = function() {
289357
this.defaultCameraFOV = 60 / 180 * Math.PI;
290358
this.defaultCameraAspect = this.width / this.height;
@@ -879,11 +947,19 @@ p5.RendererGL.prototype._useImmediateModeShader = function() {
879947

880948
p5.RendererGL.prototype._getLightShader = function() {
881949
if (!this._defaultLightShader) {
882-
this._defaultLightShader = new p5.Shader(
883-
this,
884-
defaultShaders.lightVert,
885-
defaultShaders.lightTextureFrag
886-
);
950+
if (this.attributes.perPixelLighting) {
951+
this._defaultLightShader = new p5.Shader(
952+
this,
953+
defaultShaders.phongVert,
954+
defaultShaders.phongFrag
955+
);
956+
} else {
957+
this._defaultLightShader = new p5.Shader(
958+
this,
959+
defaultShaders.lightVert,
960+
defaultShaders.lightTextureFrag
961+
);
962+
}
887963
}
888964
//this.drawMode = constants.FILL;
889965
return this._defaultLightShader;

src/webgl/shaders/light.vert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void main(void){
5050
for (int j = 0; j < 8; j++) {
5151
if (uDirectionalLightCount == j) break;
5252
vec3 dir = uLightingDirection[j];
53-
float directionalLightWeighting = max(dot(vertexNormal, dir), 0.0);
53+
float directionalLightWeighting = max(dot(vertexNormal, -dir), 0.0);
5454
directionalLightFactor += uDirectionalColor[j] * directionalLightWeighting;
5555
}
5656

src/webgl/shaders/phong.frag

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
precision mediump float;
2+
3+
//uniform mat4 uModelViewMatrix;
4+
uniform mat4 uViewMatrix;
5+
6+
uniform vec4 uMaterialColor;
7+
uniform sampler2D uSampler;
8+
uniform bool isTexture;
9+
uniform bool uUseLighting;
10+
11+
uniform vec3 uLightingDirection[8];
12+
uniform vec3 uDirectionalColor[8];
13+
uniform vec3 uPointLightLocation[8];
14+
uniform vec3 uPointLightColor[8];
15+
uniform bool uSpecular;
16+
17+
uniform int uDirectionalLightCount;
18+
uniform int uPointLightCount;
19+
20+
varying vec3 vNormal;
21+
varying vec2 vTexCoord;
22+
varying vec3 vViewPosition;
23+
varying vec3 vAmbientColor;
24+
25+
vec3 V;
26+
vec3 N;
27+
28+
const float shininess = 32.0;
29+
const float specularFactor = 2.0;
30+
const float diffuseFactor = 0.73;
31+
32+
struct LightResult {
33+
float specular;
34+
float diffuse;
35+
};
36+
37+
float phongSpecular(
38+
vec3 lightDirection,
39+
vec3 viewDirection,
40+
vec3 surfaceNormal,
41+
float shininess) {
42+
43+
vec3 R = normalize(reflect(-lightDirection, surfaceNormal));
44+
return pow(max(0.0, dot(R, viewDirection)), shininess);
45+
}
46+
47+
float lambertDiffuse(
48+
vec3 lightDirection,
49+
vec3 surfaceNormal) {
50+
return max(0.0, dot(-lightDirection, surfaceNormal));
51+
}
52+
53+
LightResult light(vec3 lightVector) {
54+
55+
vec3 L = normalize(lightVector);
56+
57+
//compute our diffuse & specular terms
58+
LightResult lr;
59+
if (uSpecular)
60+
lr.specular = phongSpecular(L, V, N, shininess);
61+
lr.diffuse = lambertDiffuse(L, N);
62+
return lr;
63+
}
64+
65+
void main(void) {
66+
67+
V = normalize(vViewPosition);
68+
N = vNormal;
69+
70+
vec3 diffuse = vec3(0.0);
71+
float specular = 0.0;
72+
73+
for (int j = 0; j < 8; j++) {
74+
if (uDirectionalLightCount == j) break;
75+
76+
LightResult result = light(uLightingDirection[j]);
77+
diffuse += result.diffuse * uDirectionalColor[j];
78+
specular += result.specular;
79+
}
80+
81+
for (int k = 0; k < 8; k++) {
82+
if (uPointLightCount == k) break;
83+
84+
vec3 lightPosition = (uViewMatrix * vec4(uPointLightLocation[k], 1.0)).xyz;
85+
vec3 lightVector = vViewPosition - lightPosition;
86+
87+
//calculate attenuation
88+
float lightDistance = length(lightVector);
89+
float falloff = 500.0 / (lightDistance + 500.0);
90+
91+
LightResult result = light(lightVector);
92+
diffuse += result.diffuse * falloff * uPointLightColor[k];
93+
specular += result.specular * falloff;
94+
}
95+
96+
gl_FragColor = isTexture ? texture2D(uSampler, vTexCoord) : uMaterialColor;
97+
gl_FragColor.rgb = gl_FragColor.rgb * (diffuse * diffuseFactor + vAmbientColor) + specular * specularFactor;
98+
}

src/webgl/shaders/phong.vert

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
precision mediump float;
2+
3+
attribute vec3 aPosition;
4+
attribute vec3 aNormal;
5+
attribute vec2 aTexCoord;
6+
7+
uniform vec3 uAmbientColor[8];
8+
9+
uniform mat4 uModelViewMatrix;
10+
uniform mat4 uProjectionMatrix;
11+
uniform mat3 uNormalMatrix;
12+
uniform int uAmbientLightCount;
13+
14+
varying vec3 vNormal;
15+
varying vec2 vTexCoord;
16+
varying vec3 vViewPosition;
17+
varying vec3 vAmbientColor;
18+
19+
void main(void){
20+
21+
vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);
22+
23+
// Pass varyings to fragment shader
24+
vViewPosition = viewModelPosition.xyz;
25+
gl_Position = uProjectionMatrix * viewModelPosition;
26+
27+
vNormal = normalize(uNormalMatrix * normalize(aNormal));
28+
vTexCoord = aTexCoord;
29+
30+
vAmbientColor = vec3(0.0);
31+
for (int i = 0; i < 8; i++) {
32+
if (uAmbientLightCount == i) break;
33+
vAmbientColor += uAmbientColor[i];
34+
}
35+
}

test/manual-test-examples/webgl/customShader/toonShader/sketch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function draw () {
1717
background(0);
1818
var dirY = (mouseY / height - 0.5) * 2;
1919
var dirX = (mouseX / width - 0.5) * 2;
20-
directionalLight(255, 204, 204, -dirX, dirY, -1);
20+
directionalLight(255, 204, 204, -dirX, -dirY, -1);
2121
ambientMaterial(0, 255, 255);
2222
sphere(120);
2323
}

test/manual-test-examples/webgl/lights/directionalLight/sketch.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
function setup(){
22
createCanvas(windowWidth, windowHeight, WEBGL);
3+
//setAttributes('perPixelLighting', true);
34
}
45

56
function draw(){
@@ -9,7 +10,7 @@ function draw(){
910
var dirX = (mouseX / width - 0.5) *2;
1011

1112
ambientLight(50);
12-
directionalLight(250, 250, 250, dirX, -dirY, 0.25);
13+
directionalLight(250, 250, 250, -dirX, -dirY, 0);
1314

1415
ambientMaterial(250);
1516
sphere(50, 64);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<title></title>
7+
<link rel="stylesheet" href="">
8+
<script language="javascript" type="text/javascript" src="../../../../../lib/p5.js"></script>
9+
<script language="javascript" type="text/javascript" src="sketch.js"></script>
10+
<style>
11+
html, body {margin:0; padding:0;}
12+
</style>
13+
</head>
14+
<body>
15+
<p style="position: absolute; width:300px; left:50%; margin-left: -150px; color:white; text-align: center;">Press mouse to enable per-pixel-lighting</p>
16+
<script>
17+
(function(){var script=document.createElement('script');script.onload=function(){var stats=new Stats();stats.domElement.style.cssText='position:fixed;left:0;top:0;z-index:10000';document.body.appendChild(stats.domElement);requestAnimationFrame(function loop(){stats.update();requestAnimationFrame(loop)});};script.src='http://rawgit.com/mrdoob/stats.js/master/build/stats.min.js';document.head.appendChild(script);})()
18+
</script>
19+
20+
</body>
21+
</html>

0 commit comments

Comments
 (0)