diff --git a/developer_docs/webgl_mode_architecture.md b/developer_docs/webgl_mode_architecture.md index c7332331fa..4232022d83 100644 --- a/developer_docs/webgl_mode_architecture.md +++ b/developer_docs/webgl_mode_architecture.md @@ -87,7 +87,7 @@ Provides flat shading of objects, based on the current fill color. #### Light Shader (for lighting AND textures) Accounts for: * Lighting parameters set by `ambientLight()`, `directionalLight()`, and `pointLight()` -* Material parameters set by `ambientMaterial()`, and `specularMaterial()` +* Material parameters set by `ambientMaterial()`, `emissiveMaterial()` and `specularMaterial()` * Texture parameters, set by `texture()` #### Normal Shader @@ -133,6 +133,7 @@ The normal shader is set when `normalMaterial()` is in use. It uses the surface |`uniform vec3 uPointLightLocation[8];`| |x | | | | |`uniform vec3 uPointLightColor[8];` | |x | | | | |`uniform bool uSpecular;` | |x | | | | +|`uniform bool uEmissive;` | |x | | | | |`uniform int uShininess;` | |x | | | | |`uniform bool uUseLighting;` | |x | | | | |`uniform float uConstantAttenuation;` | |x | | | | diff --git a/src/webgl/material.js b/src/webgl/material.js index 72788f05ea..d5d52db9b3 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -312,6 +312,7 @@ p5.prototype.normalMaterial = function() { p5._validateParameters('normalMaterial', arguments); this._renderer.drawMode = constants.FILL; this._renderer._useSpecularMaterial = false; + this._renderer._useEmissiveMaterial = false; this._renderer._useNormalMaterial = true; this._renderer.curFillColor = [1, 1, 1, 1]; this._renderer._setProperty('_doFill', true); @@ -409,6 +410,7 @@ p5.prototype.texture = function(tex) { this._renderer.drawMode = constants.TEXTURE; this._renderer._useSpecularMaterial = false; + this._renderer._useEmissiveMaterial = false; this._renderer._useNormalMaterial = false; this._renderer._tex = tex; this._renderer._setProperty('_doFill', true); @@ -613,6 +615,59 @@ p5.prototype.ambientMaterial = function(v1, v2, v3, a) { var color = p5.prototype.color.apply(this, arguments); this._renderer.curFillColor = color._array; this._renderer._useSpecularMaterial = false; + this._renderer._useEmissiveMaterial = false; + this._renderer._useNormalMaterial = false; + this._renderer._enableLighting = true; + this._renderer._tex = null; + + return this; +}; + +/** + * Sets the emissive color of the material used for geometry drawn to + * the screen. This is a misnomer in the sense that the material does not + * actually emit light that effects surrounding polygons. Instead, + * it gives the appearance that the object is glowing. An emissive material + * will display at full strength even if there is no light for it to reflect. + * @method emissiveMaterial + * @param {Number} v1 gray value, red or hue value + * (depending on the current color mode), + * @param {Number} [v2] green or saturation value + * @param {Number} [v3] blue or brightness value + * @param {Number} [a] opacity + * @chainable + * @example + *
+ * + * function setup() { + * createCanvas(100, 100, WEBGL); + * } + * function draw() { + * background(0); + * noStroke(); + * ambientLight(0); + * emissiveMaterial(130, 230, 0); + * sphere(40); + * } + * + *
+ * + * @alt + * radiating light source from top right of canvas + */ +/** + * @method emissiveMaterial + * @param {Number[]|String|p5.Color} color color, color Array, or CSS color string + * @chainable + */ +p5.prototype.emissiveMaterial = function(v1, v2, v3, a) { + this._assert3d('emissiveMaterial'); + p5._validateParameters('emissiveMaterial', arguments); + + var color = p5.prototype.color.apply(this, arguments); + this._renderer.curFillColor = color._array; + this._renderer._useSpecularMaterial = false; + this._renderer._useEmissiveMaterial = true; this._renderer._useNormalMaterial = false; this._renderer._enableLighting = true; this._renderer._tex = null; @@ -664,6 +719,7 @@ p5.prototype.specularMaterial = function(v1, v2, v3, a) { var color = p5.prototype.color.apply(this, arguments); this._renderer.curFillColor = color._array; this._renderer._useSpecularMaterial = true; + this._renderer._useEmissiveMaterial = false; this._renderer._useNormalMaterial = false; this._renderer._enableLighting = true; this._renderer._tex = null; diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 94a7c0832e..46a60a68ae 100755 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -83,6 +83,7 @@ p5.RendererGL = function(elt, pInst, isMainCanvas, attr) { this.blendExt = this.GL.getExtension('EXT_blend_minmax'); this._useSpecularMaterial = false; + this._useEmissiveMaterial = false; this._useNormalMaterial = false; this._useShininess = 1; @@ -874,6 +875,7 @@ p5.RendererGL.prototype.push = function() { properties.curFillColor = this.curFillColor; properties._useSpecularMaterial = this._useSpecularMaterial; + properties._useEmissiveMaterial = this._useEmissiveMaterial; properties._useShininess = this._useShininess; properties.constantAttenuation = this.constantAttenuation; @@ -1112,6 +1114,7 @@ p5.RendererGL.prototype._setFillUniforms = function(fillShader) { fillShader.setUniform('uTint', this._tint); fillShader.setUniform('uSpecular', this._useSpecularMaterial); + fillShader.setUniform('uEmissive', this._useEmissiveMaterial); fillShader.setUniform('uShininess', this._useShininess); fillShader.setUniform('uUseLighting', this._enableLighting); diff --git a/src/webgl/shaders/light_texture.frag b/src/webgl/shaders/light_texture.frag index 649bfd70da..b8fc378a66 100644 --- a/src/webgl/shaders/light_texture.frag +++ b/src/webgl/shaders/light_texture.frag @@ -4,12 +4,18 @@ uniform vec4 uMaterialColor; uniform vec4 uTint; uniform sampler2D uSampler; uniform bool isTexture; +uniform bool uEmissive; varying highp vec2 vVertTexCoord; varying vec3 vDiffuseColor; varying vec3 vSpecularColor; void main(void) { - gl_FragColor = isTexture ? texture2D(uSampler, vVertTexCoord) * (uTint / vec4(255, 255, 255, 255)) : uMaterialColor; - gl_FragColor.rgb = gl_FragColor.rgb * vDiffuseColor + vSpecularColor; + if(uEmissive && !isTexture) { + gl_FragColor = uMaterialColor; + } + else { + gl_FragColor = isTexture ? texture2D(uSampler, vVertTexCoord) * (uTint / vec4(255, 255, 255, 255)) : uMaterialColor; + gl_FragColor.rgb = gl_FragColor.rgb * vDiffuseColor + vSpecularColor; + } } \ No newline at end of file diff --git a/src/webgl/shaders/phong.frag b/src/webgl/shaders/phong.frag index c188fa2d12..b937377966 100644 --- a/src/webgl/shaders/phong.frag +++ b/src/webgl/shaders/phong.frag @@ -3,6 +3,7 @@ uniform vec4 uMaterialColor; uniform sampler2D uSampler; uniform bool isTexture; +uniform bool uEmissive; varying vec3 vNormal; varying vec2 vTexCoord; @@ -15,6 +16,11 @@ void main(void) { vec3 specular; totalLight(vViewPosition, normalize(vNormal), diffuse, specular); - gl_FragColor = isTexture ? texture2D(uSampler, vTexCoord) : uMaterialColor; - gl_FragColor.rgb = gl_FragColor.rgb * (diffuse + vAmbientColor) + specular; + if(uEmissive && !isTexture) { + gl_FragColor = uMaterialColor; + } + else { + gl_FragColor = isTexture ? texture2D(uSampler, vTexCoord) : uMaterialColor; + gl_FragColor.rgb = gl_FragColor.rgb * (diffuse + vAmbientColor) + specular; + } } \ No newline at end of file diff --git a/test/manual-test-examples/webgl/material/emissive/index.html b/test/manual-test-examples/webgl/material/emissive/index.html new file mode 100644 index 0000000000..ef2cf9f556 --- /dev/null +++ b/test/manual-test-examples/webgl/material/emissive/index.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/manual-test-examples/webgl/material/emissive/sketch.js b/test/manual-test-examples/webgl/material/emissive/sketch.js new file mode 100644 index 0000000000..621e26418b --- /dev/null +++ b/test/manual-test-examples/webgl/material/emissive/sketch.js @@ -0,0 +1,32 @@ +function setup() { + createCanvas(windowWidth, windowHeight, WEBGL); + // setAttributes('perPixelLighting', true); +} + +function draw() { + background(0); + + var locY = mouseY - height / 2; + var locX = mouseX - width / 2; + + ambientLight(100); + pointLight(200, 200, 200, locX, locY, 0); + + translate(-200, 0, 0); + push(); + ambientMaterial(250); + rotateZ(frameCount * 0.02); + rotateX(frameCount * 0.02); + rotateY(frameCount * 0.02); + torus(80, 20, 64, 64); + pop(); + + translate(400, 0, 0); + push(); + emissiveMaterial(200, 0, 0); + rotateZ(frameCount * 0.02); + rotateX(frameCount * 0.02); + rotateY(frameCount * 0.02); + torus(81, 20, 64, 64); + pop(); +} diff --git a/test/unit/core/rendering.js b/test/unit/core/rendering.js index 8c0d3f9fbf..26c711f88d 100644 --- a/test/unit/core/rendering.js +++ b/test/unit/core/rendering.js @@ -112,7 +112,8 @@ suite('Rendering', function() { 'ambientLight', 'directionalLight', 'pointLight', 'lights', 'model', 'createShader', 'shader', - 'normalMaterial', 'texture', 'ambientMaterial', 'specularMaterial', 'shininess', 'lightFalloff', + 'normalMaterial', 'texture', 'ambientMaterial', 'emissiveMaterial', 'specularMaterial', + 'shininess', 'lightFalloff', 'plane', 'box', 'sphere', 'cylinder', 'cone', 'ellipsoid', 'torus', ]; diff --git a/test/unit/webgl/p5.Shader.js b/test/unit/webgl/p5.Shader.js index 53ef22ab96..283ed96ca8 100644 --- a/test/unit/webgl/p5.Shader.js +++ b/test/unit/webgl/p5.Shader.js @@ -83,6 +83,7 @@ suite('p5.Shader', function() { 'uMaterialColor', 'uSampler', 'isTexture', + 'uEmissive', 'uConstantAttenuation', 'uLinearAttenuation', 'uQuadraticAttenuation' @@ -211,6 +212,22 @@ suite('p5.Shader', function() { 'after call to specularMaterial()' ); }); + test('Light shader set after emissiveMaterial()', function() { + var lightShader = myp5._renderer._getLightShader(); + myp5.emissiveMaterial(128); + var selectedRetainedShader = myp5._renderer._getRetainedFillShader(); + var selectedImmediateShader = myp5._renderer._getImmediateFillShader(); + assert( + lightShader === selectedRetainedShader, + "_renderer's retain mode shader was not light shader " + + 'after call to emissiveMaterial()' + ); + assert( + lightShader === selectedImmediateShader, + "_renderer's immediate mode shader was not light shader " + + 'after call to emissiveMaterial()' + ); + }); test('Able to setUniform empty arrays', function() { myp5.shader(myp5._renderer._getLightShader());