diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js
index bfc96e0fed..4c897b89e7 100644
--- a/src/core/shape/vertex.js
+++ b/src/core/shape/vertex.js
@@ -992,4 +992,56 @@ p5.prototype.vertex = function(x, y, moveTo, u, v) {
return this;
};
+/**
+ * Sets the 3d vertex normal to use for subsequent vertices drawn with
+ * vertex(). A normal is a vector that is generally
+ * nearly perpendicular to a shape's surface which controls how much light will
+ * be reflected from that part of the surface.
+ *
+ * @method normal
+ * @param {Vector} vector A p5.Vector representing the vertex normal.
+ * @chainable
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * noStroke();
+ * }
+ *
+ * function draw() {
+ * background(255);
+ * rotateY(frameCount / 100);
+ * normalMaterial();
+ * beginShape(TRIANGLE_STRIP);
+ * normal(-0.4, 0.4, 0.8);
+ * vertex(-30, 30, 0);
+ *
+ * normal(0, 0, 1);
+ * vertex(-30, -30, 30);
+ * vertex(30, 30, 30);
+ *
+ * normal(0.4, -0.4, 0.8);
+ * vertex(30, -30, 0);
+ * endShape();
+ * }
+ *
+ *
+ */
+
+/**
+ * @method normal
+ * @param {Number} x The x component of the vertex normal.
+ * @param {Number} y The y component of the vertex normal.
+ * @param {Number} z The z component of the vertex normal.
+ * @chainable
+ */
+p5.prototype.normal = function(x, y, z) {
+ this._assert3d('normal');
+ p5._validateParameters('normal', arguments);
+ this._renderer.normal(...arguments);
+
+ return this;
+};
+
export default p5;
diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js
index 16cb6fb988..410237643b 100644
--- a/src/webgl/p5.RendererGL.Immediate.js
+++ b/src/webgl/p5.RendererGL.Immediate.js
@@ -67,6 +67,7 @@ p5.RendererGL.prototype.vertex = function(x, y) {
}
const vert = new p5.Vector(x, y, z);
this.immediateMode.geometry.vertices.push(vert);
+ this.immediateMode.geometry.vertexNormals.push(this._currentNormal);
const vertexColor = this.curFillColor || [0.5, 0.5, 0.5, 1.0];
this.immediateMode.geometry.vertexColors.push(
vertexColor[0],
@@ -103,6 +104,28 @@ p5.RendererGL.prototype.vertex = function(x, y) {
return this;
};
+/**
+ * Sets the normal to use for subsequent vertices.
+ * @method vertexNormal
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} z
+ * @chainable
+ *
+ * @method vertexNormal
+ * @param {Vector} v
+ * @chainable
+ */
+p5.RendererGL.prototype.normal = function(xorv, y, z) {
+ if (xorv instanceof p5.Vector) {
+ this._currentNormal = xorv;
+ } else {
+ this._currentNormal = new p5.Vector(xorv, y, z);
+ }
+
+ return this;
+};
+
/**
* End shape drawing and render vertices to screen.
* @chainable
@@ -251,7 +274,6 @@ p5.RendererGL.prototype._drawImmediateFill = function() {
const gl = this.GL;
const shader = this._getImmediateFillShader();
- this._calculateNormals(this.immediateMode.geometry);
this._setFillUniforms(shader);
for (const buff of this.immediateMode.buffers.fill) {
@@ -298,16 +320,4 @@ p5.RendererGL.prototype._drawImmediateStroke = function() {
shader.unbindShader();
};
-/**
- * Called from _drawImmediateFill(). Currently adds default normals which
- * only work for flat shapes.
- * @parem
- * @private
- */
-p5.RendererGL.prototype._calculateNormals = function(geometry) {
- geometry.vertices.forEach(() => {
- geometry.vertexNormals.push(new p5.Vector(0, 0, 1));
- });
-};
-
export default p5.RendererGL;
diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js
index c0fdc2b9ba..f2b4237ce2 100755
--- a/src/webgl/p5.RendererGL.js
+++ b/src/webgl/p5.RendererGL.js
@@ -122,6 +122,9 @@ p5.RendererGL = function(elt, pInst, isMainCanvas, attr) {
this.uPMatrix = new p5.Matrix();
this.uNMatrix = new p5.Matrix('mat3');
+ // Current vertex normal
+ this._currentNormal = new p5.Vector(0, 0, 1);
+
// Camera
this._curCamera = new p5.Camera(this);
this._curCamera._computeCameraDefaultSettings();
@@ -1026,6 +1029,8 @@ p5.RendererGL.prototype.push = function() {
properties._tex = this._tex;
properties.drawMode = this.drawMode;
+ properties._currentNormal = this._currentNormal;
+
return style;
};
diff --git a/test/unit/webgl/normal.js b/test/unit/webgl/normal.js
new file mode 100644
index 0000000000..d8254ef7d3
--- /dev/null
+++ b/test/unit/webgl/normal.js
@@ -0,0 +1,54 @@
+suite('', function() {
+ var myp5;
+
+ if (!window.Modernizr.webgl) {
+ return;
+ }
+
+ setup(function() {
+ myp5 = new p5(function(p) {
+ p.setup = function() {
+ p.createCanvas(100, 100, p.WEBGL);
+ };
+ });
+ });
+
+ teardown(function() {
+ myp5.remove();
+ });
+
+ suite('p5.prototype.normal', function() {
+ test('should be a function', function() {
+ assert.ok(myp5.normal);
+ assert.typeOf(myp5.normal, 'function');
+ });
+ test('missing param #1', function() {
+ assert.validationError(function() {
+ myp5.normal(10);
+ });
+ });
+ test('wrong param type at #0', function() {
+ assert.validationError(function() {
+ myp5.normal('a', 1);
+ });
+ });
+ test('accepts numeric arguments', function() {
+ assert.doesNotThrow(
+ function() {
+ myp5.normal(0, 1, 0);
+ },
+ Error,
+ 'got unwanted exception'
+ );
+ });
+ test('accepts vector argument', function() {
+ assert.doesNotThrow(
+ function() {
+ myp5.normal(myp5.createVector(0, 1, 0));
+ },
+ Error,
+ 'got unwanted exception'
+ );
+ });
+ });
+});