diff --git a/docs/yuidoc-p5-theme/assets/outdoor_image.jpg b/docs/yuidoc-p5-theme/assets/outdoor_image.jpg
new file mode 100644
index 0000000000..3e60040595
Binary files /dev/null and b/docs/yuidoc-p5-theme/assets/outdoor_image.jpg differ
diff --git a/docs/yuidoc-p5-theme/assets/outdoor_spheremap.jpg b/docs/yuidoc-p5-theme/assets/outdoor_spheremap.jpg
new file mode 100644
index 0000000000..69b0bb17aa
Binary files /dev/null and b/docs/yuidoc-p5-theme/assets/outdoor_spheremap.jpg differ
diff --git a/package-lock.json b/package-lock.json
index 0d3ee5ac43..51095eb0b4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3915,9 +3915,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001399",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001399.tgz",
- "integrity": "sha512-4vQ90tMKS+FkvuVWS5/QY1+d805ODxZiKFzsU8o/RsVJz49ZSRR8EjykLJbqhzdPgadbX6wB538wOzle3JniRA==",
+ "version": "1.0.30001549",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz",
+ "integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==",
"dev": true,
"funding": [
{
@@ -3927,6 +3927,10 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
}
]
},
diff --git a/src/webgl/light.js b/src/webgl/light.js
index 3fbab91e7b..e8c8079248 100644
--- a/src/webgl/light.js
+++ b/src/webgl/light.js
@@ -494,6 +494,99 @@ p5.prototype.pointLight = function(v1, v2, v3, x, y, z) {
return this;
};
+/**
+ * Creates an image light with the given image.
+ *
+ * The image light simulates light from all the directions.
+ * This is done by using the image as a texture for an infinitely
+ * large sphere light. This sphere contains
+ * or encapsulates the whole scene/drawing.
+ * It will have different effect for varying shininess of the
+ * object in the drawing.
+ * Under the hood it is mainly doing 2 types of calculations,
+ * the first one is creating an irradiance map the
+ * environment map of the input image.
+ * The second one is managing reflections based on the shininess
+ * or roughness of the material used in the scene.
+ *
+ * Note: The image's diffuse light will be affected by fill()
+ * and the specular reflections will be affected by specularMaterial()
+ * and shininess().
+ *
+ * @method imageLight
+ * @param {p5.image} img image for the background
+ * @example
+ *
+ *
+ * let img;
+ * function preload() {
+ * img = loadImage('assets/outdoor_image.jpg');
+ * }
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * }
+ * function draw() {
+ * background(220);
+ * imageMode(CENTER);
+ * push();
+ * translate(0, 0, -200);
+ * scale(2);
+ * image(img, 0, 0, width, height);
+ * pop();
+ * ambientLight(50);
+ * imageLight(img);
+ * specularMaterial(20);
+ * noStroke();
+ * scale(2);
+ * rotateX(frameCount * 0.005);
+ * rotateY(frameCount * 0.005);
+ * box(25);
+ * }
+ *
+ *
+ * @alt
+ * image light example
+ * @example
+ *
+ *
+ * let img;
+ * let slider;
+ * function preload() {
+ * img = loadImage('assets/outdoor_spheremap.jpg');
+ * }
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * slider = createSlider(0, 400, 100, 1);
+ * slider.position(0, height);
+ * }
+ * function draw() {
+ * background(220);
+ * imageMode(CENTER);
+ * push();
+ * translate(0, 0, -200);
+ * scale(2);
+ * image(img, 0, 0, width, height);
+ * pop();
+ * ambientLight(50);
+ * imageLight(img);
+ * specularMaterial(20);
+ * shininess(slider.value());
+ * noStroke();
+ * scale(2);
+ * sphere(15);
+ * }
+ *
+ *
+ * @alt
+ * light with slider having a slider for varying roughness
+ */
+p5.prototype.imageLight = function(img){
+ // activeImageLight property is checked by _setFillUniforms
+ // for sending uniforms to the fillshader
+ this._renderer.activeImageLight = img;
+ this._renderer._enableLighting = true;
+};
+
/**
* Places an ambient and directional light in the scene.
* The lights are set to ambientLight(128, 128, 128) and
diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js
index 3542584674..5e95a83088 100644
--- a/src/webgl/p5.RendererGL.js
+++ b/src/webgl/p5.RendererGL.js
@@ -9,6 +9,7 @@ import './p5.Matrix';
import './p5.Framebuffer';
import { readFileSync } from 'fs';
import { join } from 'path';
+import { MipmapTexture } from './p5.Texture';
const STROKE_CAP_ENUM = {};
const STROKE_JOIN_ENUM = {};
@@ -67,17 +68,21 @@ const defaultShaders = {
phongFrag:
lightingShader +
readFileSync(join(__dirname, '/shaders/phong.frag'), 'utf-8'),
- fontVert: webgl2CompatibilityShader +
- readFileSync(join(__dirname, '/shaders/font.vert'), 'utf-8'),
- fontFrag: webgl2CompatibilityShader +
- readFileSync(join(__dirname, '/shaders/font.frag'), 'utf-8'),
+ fontVert: readFileSync(join(__dirname, '/shaders/font.vert'), 'utf-8'),
+ fontFrag: readFileSync(join(__dirname, '/shaders/font.frag'), 'utf-8'),
lineVert:
lineDefs + readFileSync(join(__dirname, '/shaders/line.vert'), 'utf-8'),
lineFrag:
lineDefs + readFileSync(join(__dirname, '/shaders/line.frag'), 'utf-8'),
pointVert: readFileSync(join(__dirname, '/shaders/point.vert'), 'utf-8'),
- pointFrag: readFileSync(join(__dirname, '/shaders/point.frag'), 'utf-8')
+ pointFrag: readFileSync(join(__dirname, '/shaders/point.frag'), 'utf-8'),
+ imageLightVert : readFileSync(join(__dirname, '/shaders/imageLight.vert'), 'utf-8'),
+ imageLightDiffusedFrag : readFileSync(join(__dirname, '/shaders/imageLightDiffused.frag'), 'utf-8'),
+ imageLightSpecularFrag : readFileSync(join(__dirname, '/shaders/imageLightSpecular.frag'), 'utf-8')
};
+for (const key in defaultShaders) {
+ defaultShaders[key] = webgl2CompatibilityShader + defaultShaders[key];
+}
const filterShaderFrags = {
[constants.GRAY]:
@@ -468,6 +473,19 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this.spotLightAngle = [];
this.spotLightConc = [];
+ // This property contains the input image if imageLight function
+ // is called.
+ // activeImageLight is checked by _setFillUniforms
+ // for sending uniforms to the fillshader
+ this.activeImageLight = null;
+ // If activeImageLight property is Null, diffusedTextures,
+ // specularTextures are Empty.
+ // Else, it maps a p5.Image used by imageLight() to a p5.Graphics.
+ // p5.Graphics for this are calculated in getDiffusedTexture function
+ this.diffusedTextures = new Map();
+ // p5.Graphics for this are calculated in getSpecularTexture function
+ this.specularTextures = new Map();
+
this.drawMode = constants.FILL;
this.curFillColor = this._cachedFillStyle = [1, 1, 1, 1];
@@ -1603,6 +1621,9 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
properties._currentNormal = this._currentNormal;
properties.curBlendMode = this.curBlendMode;
+ // So that the activeImageLight gets reset in push/pop
+ properties.activeImageLight = this.activeImageLight;
+
return style;
}
pop(...args) {
@@ -1740,14 +1761,18 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
if (this._pInst._glAttributes.perPixelLighting) {
this._defaultLightShader = new p5.Shader(
this,
- defaultShaders.phongVert,
- defaultShaders.phongFrag
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
+ defaultShaders.phongVert,
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
+ defaultShaders.phongFrag
);
} else {
this._defaultLightShader = new p5.Shader(
this,
- defaultShaders.lightVert,
- defaultShaders.lightTextureFrag
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
+ defaultShaders.lightVert,
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
+ defaultShaders.lightTextureFrag
);
}
}
@@ -1759,8 +1784,10 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
if (!this._defaultImmediateModeShader) {
this._defaultImmediateModeShader = new p5.Shader(
this,
- defaultShaders.immediateVert,
- defaultShaders.vertexColorFrag
+ this._webGL2CompatibilityPrefix('vert', 'mediump') +
+ defaultShaders.immediateVert,
+ this._webGL2CompatibilityPrefix('frag', 'mediump') +
+ defaultShaders.vertexColorFrag
);
}
@@ -1771,8 +1798,10 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
if (!this._defaultNormalShader) {
this._defaultNormalShader = new p5.Shader(
this,
- defaultShaders.normalVert,
- defaultShaders.normalFrag
+ this._webGL2CompatibilityPrefix('vert', 'mediump') +
+ defaultShaders.normalVert,
+ this._webGL2CompatibilityPrefix('frag', 'mediump') +
+ defaultShaders.normalFrag
);
}
@@ -1783,8 +1812,10 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
if (!this._defaultColorShader) {
this._defaultColorShader = new p5.Shader(
this,
- defaultShaders.normalVert,
- defaultShaders.basicFrag
+ this._webGL2CompatibilityPrefix('vert', 'mediump') +
+ defaultShaders.normalVert,
+ this._webGL2CompatibilityPrefix('frag', 'mediump') +
+ defaultShaders.basicFrag
);
}
@@ -1795,8 +1826,10 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
if (!this._defaultPointShader) {
this._defaultPointShader = new p5.Shader(
this,
- defaultShaders.pointVert,
- defaultShaders.pointFrag
+ this._webGL2CompatibilityPrefix('vert', 'mediump') +
+ defaultShaders.pointVert,
+ this._webGL2CompatibilityPrefix('frag', 'mediump') +
+ defaultShaders.pointFrag
);
}
return this._defaultPointShader;
@@ -1806,8 +1839,10 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
if (!this._defaultLineShader) {
this._defaultLineShader = new p5.Shader(
this,
- defaultShaders.lineVert,
- defaultShaders.lineFrag
+ this._webGL2CompatibilityPrefix('vert', 'mediump') +
+ defaultShaders.lineVert,
+ this._webGL2CompatibilityPrefix('frag', 'mediump') +
+ defaultShaders.lineFrag
);
}
@@ -1874,6 +1909,88 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this.textures.set(src, tex);
return tex;
}
+ /*
+ * used in imageLight,
+ * To create a blurry image from the input non blurry img, if it doesn't already exist
+ * Add it to the diffusedTexture map,
+ * Returns the blurry image
+ * maps a p5.Image used by imageLight() to a p5.Graphics
+ */
+ getDiffusedTexture(input){
+ // if one already exists for a given input image
+ if(this.diffusedTextures.get(input)!=null){
+ return this.diffusedTextures.get(input);
+ }
+ // if not, only then create one
+ let newGraphic; // maybe switch to framebuffer
+ // hardcoded to 200px, because it's going to be blurry and smooth
+ let smallWidth = 200;
+ let width = smallWidth;
+ let height = Math.floor(smallWidth * (input.height / input.width));
+ newGraphic = this._pInst.createGraphics(width, height, constants.WEBGL);
+ // create graphics is like making a new sketch, all functions on main
+ // sketch it would be available on graphics
+ let irradiance = newGraphic.createShader(
+ defaultShaders.imageLightVert,
+ defaultShaders.imageLightDiffusedFrag
+ );
+ newGraphic.shader(irradiance);
+ irradiance.setUniform('environmentMap', input);
+ newGraphic.noStroke();
+ newGraphic.rectMode(newGraphic.CENTER);
+ newGraphic.rect(0, 0, newGraphic.width, newGraphic.height);
+ this.diffusedTextures.set(input, newGraphic);
+ return newGraphic;
+ }
+
+ /*
+ * used in imageLight,
+ * To create a texture from the input non blurry image, if it doesn't already exist
+ * Creating 8 different levels of textures according to different
+ * sizes and atoring them in `levels` array
+ * Creating a new Mipmap texture with that `levels` array
+ * Storing the texture for input image in map called `specularTextures`
+ * maps the input p5.Image to a p5.MipmapTexture
+ */
+ getSpecularTexture(input){
+ // check if already exits (there are tex of diff resolution so which one to check)
+ // currently doing the whole array
+ if(this.specularTextures.get(input)!=null){
+ return this.specularTextures.get(input);
+ }
+ // Hardcoded size
+ const size = 512;
+ let tex;
+ const levels = [];
+ const graphic = this._pInst.createGraphics(size, size, constants.WEBGL);
+ let count = Math.log(size)/Math.log(2);
+ graphic.pixelDensity(1);
+ // currently only 8 levels
+ // This loop calculates 8 graphics of varying size of canvas
+ // and corresponding different roughness levels.
+ // Roughness increases with the decrease in canvas size,
+ // because rougher surfaces have less detailed/more blurry reflections.
+ for (let w = size; w >= 1; w /= 2) {
+ graphic.resizeCanvas(w, w);
+ let currCount = Math.log(w)/Math.log(2);
+ let roughness = 1-currCount/count;
+ let myShader = graphic.createShader(
+ defaultShaders.imageLightVert,
+ defaultShaders.imageLightSpecularFrag
+ );
+ graphic.shader(myShader);
+ graphic.clear();
+ myShader.setUniform('environmentMap', input);
+ myShader.setUniform('roughness', roughness);
+ graphic.noStroke();
+ graphic.plane(w, w);
+ levels.push(graphic.get().drawingContext.getImageData(0, 0, w, w));
+ }
+ graphic.remove();
+ tex = new MipmapTexture(this, levels, {});
+ this.specularTextures.set(input,tex);
+ return tex;
+ }
/**
* @method activeFramebuffer
@@ -1920,6 +2037,8 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
fillShader.setUniform('uEmissive', this._useEmissiveMaterial);
fillShader.setUniform('uShininess', this._useShininess);
+ this._setImageLightUniforms(fillShader);
+
fillShader.setUniform('uUseLighting', this._enableLighting);
const pointLightCount = this.pointLightDiffuseColors.length / 3;
@@ -1970,6 +2089,29 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
fillShader.bindTextures();
}
+ // getting called from _setFillUniforms
+ _setImageLightUniforms(shader){
+ //set uniform values
+ shader.setUniform('uUseImageLight', this.activeImageLight != null );
+ // true
+ if (this.activeImageLight) {
+ // this.activeImageLight has image as a key
+ // look up the texture from the diffusedTexture map
+ let diffusedLight = this.getDiffusedTexture(this.activeImageLight);
+ shader.setUniform('environmentMapDiffused', diffusedLight);
+ let specularLight = this.getSpecularTexture(this.activeImageLight);
+ // In p5js the range of shininess is >= 1,
+ // Therefore roughness range will be ([0,1]*8)*20 or [0, 160]
+ // The factor of 8 is because currently the getSpecularTexture
+ // only calculated 8 different levels of roughness
+ // The factor of 20 is just to spread up this range so that,
+ // [1, max] of shininess is converted to [0,160] of roughness
+ let roughness = 20/this._useShininess;
+ shader.setUniform('levelOfDetail', roughness*8);
+ shader.setUniform('environmentMapSpecular', specularLight);
+ }
+ }
+
_setPointUniforms(pointShader) {
pointShader.bindShader();
diff --git a/src/webgl/p5.Texture.js b/src/webgl/p5.Texture.js
index f9dbe31439..55a4d3ed82 100644
--- a/src/webgl/p5.Texture.js
+++ b/src/webgl/p5.Texture.js
@@ -337,17 +337,8 @@ p5.Texture = class Texture {
setInterpolation (downScale, upScale) {
const gl = this._renderer.GL;
- if (downScale === constants.NEAREST) {
- this.glMinFilter = gl.NEAREST;
- } else {
- this.glMinFilter = gl.LINEAR;
- }
-
- if (upScale === constants.NEAREST) {
- this.glMagFilter = gl.NEAREST;
- } else {
- this.glMagFilter = gl.LINEAR;
- }
+ this.glMinFilter = this.glFilter(downScale);
+ this.glMagFilter = this.glFilter(upScale);
this.bindTexture();
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.glMinFilter);
@@ -355,6 +346,15 @@ p5.Texture = class Texture {
this.unbindTexture();
}
+ glFilter(filter) {
+ const gl = this._renderer.GL;
+ if (filter === constants.NEAREST) {
+ return gl.NEAREST;
+ } else {
+ return gl.LINEAR;
+ }
+ }
+
/**
* Sets the texture wrapping mode. This controls how textures behave
* when their uv's go outside of the 0 - 1 range. There are three options:
@@ -452,6 +452,51 @@ p5.Texture = class Texture {
}
};
+export class MipmapTexture extends p5.Texture {
+ constructor(renderer, levels, settings) {
+ super(renderer, levels, settings);
+ const gl = this._renderer.GL;
+ if (this.glMinFilter === gl.LINEAR) {
+ this.glMinFilter = gl.LINEAR_MIPMAP_LINEAR;
+ }
+ }
+
+ glFilter(_filter) {
+ const gl = this._renderer.GL;
+ // TODO: support others
+ return gl.LINEAR_MIPMAP_LINEAR;
+ }
+
+ _getTextureDataFromSource() {
+ return this.src;
+ }
+
+ init(levels) {
+ const gl = this._renderer.GL;
+ this.glTex = gl.createTexture();
+
+ this.bindTexture();
+ for (let level = 0; level < levels.length; level++) {
+ gl.texImage2D(
+ this.glTarget,
+ level,
+ this.glFormat,
+ this.glFormat,
+ this.glDataType,
+ levels[level]
+ );
+ }
+
+ this.glMinFilter = gl.LINEAR_MIPMAP_LINEAR;
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.glMagFilter);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.glMinFilter);
+
+ this.unbindTexture();
+ }
+
+ update() {}
+}
+
export function checkWebGLCapabilities({ GL, webglVersion }) {
const gl = GL;
const supportsFloat = webglVersion === constants.WEBGL2
diff --git a/src/webgl/shaders/basic.frag b/src/webgl/shaders/basic.frag
index be191e1c34..11b14ea09c 100644
--- a/src/webgl/shaders/basic.frag
+++ b/src/webgl/shaders/basic.frag
@@ -1,5 +1,4 @@
-precision mediump float;
-varying vec4 vColor;
+IN vec4 vColor;
void main(void) {
- gl_FragColor = vec4(vColor.rgb, 1.) * vColor.a;
+ OUT_COLOR = vec4(vColor.rgb, 1.) * vColor.a;
}
diff --git a/src/webgl/shaders/imageLight.vert b/src/webgl/shaders/imageLight.vert
new file mode 100644
index 0000000000..6f68e6d092
--- /dev/null
+++ b/src/webgl/shaders/imageLight.vert
@@ -0,0 +1,33 @@
+precision highp float;
+attribute vec3 aPosition;
+attribute vec3 aNormal;
+attribute vec2 aTexCoord;
+
+varying vec3 localPos;
+varying vec3 vWorldNormal;
+varying vec3 vWorldPosition;
+varying vec2 vTexCoord;
+
+uniform mat4 uModelViewMatrix;
+uniform mat4 uProjectionMatrix;
+uniform mat3 uNormalMatrix;
+
+void main() {
+ // Multiply the position by the matrix.
+ vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);
+ gl_Position = uProjectionMatrix * viewModelPosition;
+
+ // orient the normals and pass to the fragment shader
+ vWorldNormal = uNormalMatrix * aNormal;
+
+ // send the view position to the fragment shader
+ vWorldPosition = (uModelViewMatrix * vec4(aPosition, 1.0)).xyz;
+
+ localPos = vWorldPosition;
+ vTexCoord = aTexCoord;
+}
+
+
+/*
+in the vertex shader we'll compute the world position and world oriented normal of the vertices and pass those to the fragment shader as varyings.
+*/
diff --git a/src/webgl/shaders/imageLightDiffused.frag b/src/webgl/shaders/imageLightDiffused.frag
new file mode 100644
index 0000000000..60867f81ac
--- /dev/null
+++ b/src/webgl/shaders/imageLightDiffused.frag
@@ -0,0 +1,74 @@
+precision highp float;
+varying vec3 localPos;
+
+// the HDR cubemap converted (can be from an equirectangular environment map.)
+uniform sampler2D environmentMap;
+varying vec2 vTexCoord;
+
+const float PI = 3.14159265359;
+
+vec2 nTOE( vec3 v ){
+ // x = r sin(phi) cos(theta)
+ // y = r cos(phi)
+ // z = r sin(phi) sin(theta)
+ float phi = acos( v.y );
+ // if phi is 0, then there are no x, z components
+ float theta = 0.0;
+ // else
+ theta = acos(v.x / sin(phi));
+ float sinTheta = v.z / sin(phi);
+ if (sinTheta < 0.0) {
+ // Turn it into -theta, but in the 0-2PI range
+ theta = 2.0 * PI - theta;
+ }
+ theta = theta / (2.0 * 3.14159);
+ phi = phi / 3.14159 ;
+
+ vec2 angles = vec2( phi, theta );
+ return angles;
+}
+
+void main()
+{
+ // the sample direction equals the hemisphere's orientation
+ float phi = vTexCoord.x * 2.0 * PI;
+ float theta = vTexCoord.y * PI;
+ float x = sin(theta) * cos(phi);
+ float y = sin(theta) * sin(phi);
+ float z = cos(theta);
+ vec3 normal = vec3( x, y, z);
+
+ // Discretely sampling the hemisphere given the integral's
+ // spherical coordinates translates to the following fragment code:
+ vec3 irradiance = vec3(0.0);
+ vec3 up = vec3(0.0, 1.0, 0.0);
+ vec3 right = normalize(cross(up, normal));
+ up = normalize(cross(normal, right));
+
+ // We specify a fixed sampleDelta delta value to traverse
+ // the hemisphere; decreasing or increasing the sample delta
+ // will increase or decrease the accuracy respectively.
+ const float sampleDelta = 0.025;
+ float nrSamples = 0.0;
+
+ for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta)
+ {
+ for(float theta = 0.0; theta < ( 0.5 ) * PI; theta += sampleDelta)
+ {
+ // spherical to cartesian (in tangent space) // tangent space to world // add each sample result to irradiance
+ float x = sin(theta) * cos(phi);
+ float y = sin(theta) * sin(phi);
+ float z = cos(theta);
+ vec3 tangentSample = vec3( x, y, z);
+
+ vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal;
+ irradiance += (texture2D(environmentMap, nTOE(sampleVec)).xyz) * cos(theta) * sin(theta);
+ nrSamples++;
+ }
+ }
+ // divide by the total number of samples taken, giving us the average sampled irradiance.
+ irradiance = PI * irradiance * (1.0 / float(nrSamples )) ;
+
+
+ gl_FragColor = vec4(irradiance, 1.0);
+}
\ No newline at end of file
diff --git a/src/webgl/shaders/imageLightSpecular.frag b/src/webgl/shaders/imageLightSpecular.frag
new file mode 100644
index 0000000000..3c4ab9f316
--- /dev/null
+++ b/src/webgl/shaders/imageLightSpecular.frag
@@ -0,0 +1,112 @@
+precision highp float;
+varying vec3 localPos;
+varying vec2 vTexCoord;
+
+// our texture
+uniform sampler2D environmentMap;
+uniform float roughness;
+
+const float PI = 3.14159265359;
+
+float VanDerCorput(int bits);
+vec2 HammersleyNoBitOps(int i, int N);
+vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness);
+
+
+vec2 nTOE( vec3 v ){
+ // x = r sin(phi) cos(theta)
+ // y = r cos(phi)
+ // z = r sin(phi) sin(theta)
+ float phi = acos( v.y );
+ // if phi is 0, then there are no x, z components
+ float theta = 0.0;
+ // else
+ theta = acos(v.x / sin(phi));
+ float sinTheta = v.z / sin(phi);
+ if (sinTheta < 0.0) {
+ // Turn it into -theta, but in the 0-2PI range
+ theta = 2.0 * PI - theta;
+ }
+ theta = theta / (2.0 * 3.14159);
+ phi = phi / 3.14159 ;
+
+ vec2 angles = vec2( phi, theta );
+ return angles;
+}
+
+
+void main(){
+ const int SAMPLE_COUNT = 1024; // 4096
+ float totalWeight = 0.0;
+ vec3 prefilteredColor = vec3(0.0);
+ float phi = vTexCoord.x * 2.0 * PI;
+ float theta = vTexCoord.y * PI;
+ float x = sin(theta) * cos(phi);
+ float y = sin(theta) * sin(phi);
+ float z = cos(theta);
+ vec3 N = vec3(x,y,z);
+ vec3 V = N;
+ for (int i = 0; i < SAMPLE_COUNT; ++i)
+ {
+ vec2 Xi = HammersleyNoBitOps(i, SAMPLE_COUNT);
+ vec3 H = ImportanceSampleGGX(Xi, N, roughness);
+ vec3 L = normalize(2.0 * dot(V, H) * H - V);
+
+ float NdotL = max(dot(N, L), 0.0);
+ if (NdotL > 0.0)
+ {
+ prefilteredColor += texture2D(environmentMap, nTOE(L)).xyz * NdotL;
+ totalWeight += NdotL;
+ }
+ }
+ prefilteredColor = prefilteredColor / totalWeight;
+
+ gl_FragColor = vec4(prefilteredColor, 1.0);
+}
+
+vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness){
+ float a = roughness * roughness;
+
+ float phi = 2.0 * PI * Xi.x;
+ float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y));
+ float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+ // from spherical coordinates to cartesian coordinates
+ vec3 H;
+ H.x = cos(phi) * sinTheta;
+ H.y = sin(phi) * sinTheta;
+ H.z = cosTheta;
+
+ // from tangent-space vector to world-space sample vector
+ vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+ vec3 tangent = normalize(cross(up, N));
+ vec3 bitangent = cross(N, tangent);
+
+ vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
+ return normalize(sampleVec);
+}
+
+
+float VanDerCorput(int n, int base)
+{
+ float invBase = 1.0 / float(base);
+ float denom = 1.0;
+ float result = 0.0;
+
+ for (int i = 0; i < 32; ++i)
+ {
+ if (n > 0)
+ {
+ denom = mod(float(n), 2.0);
+ result += denom * invBase;
+ invBase = invBase / 2.0;
+ n = int(float(n) / 2.0);
+ }
+ }
+
+ return result;
+}
+
+vec2 HammersleyNoBitOps(int i, int N)
+{
+ return vec2(float(i) / float(N), VanDerCorput(i, 2));
+}
diff --git a/src/webgl/shaders/immediate.vert b/src/webgl/shaders/immediate.vert
index 396e69eca2..d430e60302 100644
--- a/src/webgl/shaders/immediate.vert
+++ b/src/webgl/shaders/immediate.vert
@@ -1,12 +1,12 @@
-attribute vec3 aPosition;
-attribute vec4 aVertexColor;
+IN vec3 aPosition;
+IN vec4 aVertexColor;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
uniform float uResolution;
uniform float uPointSize;
-varying vec4 vColor;
+OUT vec4 vColor;
void main(void) {
vec4 positionVec4 = vec4(aPosition, 1.0);
gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
diff --git a/src/webgl/shaders/light.vert b/src/webgl/shaders/light.vert
index b79713cd26..c0d3b25939 100644
--- a/src/webgl/shaders/light.vert
+++ b/src/webgl/shaders/light.vert
@@ -1,9 +1,9 @@
// include lighting.glgl
-attribute vec3 aPosition;
-attribute vec3 aNormal;
-attribute vec2 aTexCoord;
-attribute vec4 aVertexColor;
+IN vec3 aPosition;
+IN vec3 aNormal;
+IN vec2 aTexCoord;
+IN vec4 aVertexColor;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
@@ -12,10 +12,10 @@ uniform mat3 uNormalMatrix;
uniform bool uUseVertexColor;
uniform vec4 uMaterialColor;
-varying highp vec2 vVertTexCoord;
-varying vec3 vDiffuseColor;
-varying vec3 vSpecularColor;
-varying vec4 vColor;
+OUT highp vec2 vVertTexCoord;
+OUT vec3 vDiffuseColor;
+OUT vec3 vSpecularColor;
+OUT vec4 vColor;
void main(void) {
diff --git a/src/webgl/shaders/light_texture.frag b/src/webgl/shaders/light_texture.frag
index 5db6a60e84..e02083b97b 100644
--- a/src/webgl/shaders/light_texture.frag
+++ b/src/webgl/shaders/light_texture.frag
@@ -1,28 +1,26 @@
-precision highp float;
-
uniform vec4 uTint;
uniform sampler2D uSampler;
uniform bool isTexture;
uniform bool uEmissive;
-varying highp vec2 vVertTexCoord;
-varying vec3 vDiffuseColor;
-varying vec3 vSpecularColor;
-varying vec4 vColor;
+IN highp vec2 vVertTexCoord;
+IN vec3 vDiffuseColor;
+IN vec3 vSpecularColor;
+IN vec4 vColor;
void main(void) {
if(uEmissive && !isTexture) {
- gl_FragColor = vColor;
+ OUT_COLOR = vColor;
}
else {
vec4 baseColor = isTexture
// Textures come in with premultiplied alpha. To apply tint and still have
// premultiplied alpha output, we need to multiply the RGB channels by the
// tint RGB, and all channels by the tint alpha.
- ? texture2D(uSampler, vVertTexCoord) * vec4(uTint.rgb/255., 1.) * (uTint.a/255.)
+ ? TEXTURE(uSampler, vVertTexCoord) * vec4(uTint.rgb/255., 1.) * (uTint.a/255.)
// Colors come in with unmultiplied alpha, so we need to multiply the RGB
// channels by alpha to convert it to premultiplied alpha.
: vec4(vColor.rgb * vColor.a, vColor.a);
- gl_FragColor = vec4(baseColor.rgb * vDiffuseColor + vSpecularColor, baseColor.a);
+ OUT_COLOR = vec4(baseColor.rgb * vDiffuseColor + vSpecularColor, baseColor.a);
}
}
diff --git a/src/webgl/shaders/lighting.glsl b/src/webgl/shaders/lighting.glsl
index e2eb47563a..131a0767e1 100644
--- a/src/webgl/shaders/lighting.glsl
+++ b/src/webgl/shaders/lighting.glsl
@@ -1,3 +1,5 @@
+#define PI 3.141592
+
precision highp float;
precision highp int;
@@ -33,6 +35,16 @@ uniform float uConstantAttenuation;
uniform float uLinearAttenuation;
uniform float uQuadraticAttenuation;
+// setting from _setImageLightUniforms()
+// boolean to initiate the calculateImageDiffuse and calculateImageSpecular
+uniform bool uUseImageLight;
+// texture for use in calculateImageDiffuse
+uniform sampler2D environmentMapDiffused;
+// texture for use in calculateImageSpecular
+uniform sampler2D environmentMapSpecular;
+// roughness for use in calculateImageSpecular
+uniform float levelOfDetail;
+
const float specularFactor = 2.0;
const float diffuseFactor = 0.73;
@@ -67,6 +79,60 @@ LightResult _light(vec3 viewDirection, vec3 normal, vec3 lightVector) {
return lr;
}
+// converts the range of "value" from [min1 to max1] to [min2 to max2]
+float map(float value, float min1, float max1, float min2, float max2) {
+ return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
+}
+
+vec2 mapTextureToNormal( vec3 v ){
+ // x = r sin(phi) cos(theta)
+ // y = r cos(phi)
+ // z = r sin(phi) sin(theta)
+ float phi = acos( v.y );
+ // if phi is 0, then there are no x, z components
+ float theta = 0.0;
+ // else
+ theta = acos(v.x / sin(phi));
+ float sinTheta = v.z / sin(phi);
+ if (sinTheta < 0.0) {
+ // Turn it into -theta, but in the 0-2PI range
+ theta = 2.0 * PI - theta;
+ }
+ theta = theta / (2.0 * 3.14159);
+ phi = phi / 3.14159 ;
+
+ vec2 angles = vec2( fract(theta + 0.25), 1.0 - phi );
+ return angles;
+}
+
+
+vec3 calculateImageDiffuse( vec3 vNormal, vec3 vViewPosition ){
+ // make 2 seperate builds
+ vec3 worldCameraPosition = vec3(0.0, 0.0, 0.0); // hardcoded world camera position
+ vec3 worldNormal = normalize(vNormal);
+ vec2 newTexCoor = mapTextureToNormal( worldNormal );
+ vec4 texture = TEXTURE( environmentMapDiffused, newTexCoor );
+ // this is to make the darker sections more dark
+ // png and jpg usually flatten the brightness so it is to reverse that
+ return smoothstep(vec3(0.0), vec3(0.8), texture.xyz);
+}
+
+vec3 calculateImageSpecular( vec3 vNormal, vec3 vViewPosition ){
+ vec3 worldCameraPosition = vec3(0.0, 0.0, 0.0);
+ vec3 worldNormal = normalize(vNormal);
+ vec3 lightDirection = normalize( vViewPosition - worldCameraPosition );
+ vec3 R = reflect(lightDirection, worldNormal);
+ vec2 newTexCoor = mapTextureToNormal( R );
+#ifdef WEBGL2
+ vec4 outColor = textureLod(environmentMapSpecular, newTexCoor, levelOfDetail);
+#else
+ vec4 outColor = TEXTURE(environmentMapSpecular, newTexCoor);
+#endif
+ // this is to make the darker sections more dark
+ // png and jpg usually flatten the brightness so it is to reverse that
+ return pow(outColor.xyz, vec3(10.0));
+}
+
void totalLight(
vec3 modelPosition,
vec3 normal,
@@ -138,6 +204,11 @@ void totalLight(
}
}
+ if( uUseImageLight ){
+ totalDiffuse += calculateImageDiffuse(normal, modelPosition);
+ totalSpecular += calculateImageSpecular(normal, modelPosition);
+ }
+
totalDiffuse *= diffuseFactor;
totalSpecular *= specularFactor;
}
diff --git a/src/webgl/shaders/line.frag b/src/webgl/shaders/line.frag
index e38e9b66ee..42c24edcff 100644
--- a/src/webgl/shaders/line.frag
+++ b/src/webgl/shaders/line.frag
@@ -1,4 +1,3 @@
-precision mediump float;
precision mediump int;
uniform vec4 uMaterialColor;
@@ -6,13 +5,13 @@ uniform int uStrokeCap;
uniform int uStrokeJoin;
uniform float uStrokeWeight;
-varying vec4 vColor;
-varying vec2 vTangent;
-varying vec2 vCenter;
-varying vec2 vPosition;
-varying float vMaxDist;
-varying float vCap;
-varying float vJoin;
+IN vec4 vColor;
+IN vec2 vTangent;
+IN vec2 vCenter;
+IN vec2 vPosition;
+IN float vMaxDist;
+IN float vCap;
+IN float vJoin;
float distSquared(vec2 a, vec2 b) {
vec2 aToB = b - a;
@@ -47,5 +46,5 @@ void main() {
}
// Use full area for MITER
}
- gl_FragColor = vec4(vColor.rgb, 1.) * vColor.a;
+ OUT_COLOR = vec4(vColor.rgb, 1.) * vColor.a;
}
diff --git a/src/webgl/shaders/line.vert b/src/webgl/shaders/line.vert
index 4b6d032a1f..bd608181a9 100644
--- a/src/webgl/shaders/line.vert
+++ b/src/webgl/shaders/line.vert
@@ -18,7 +18,6 @@
#define PROCESSING_LINE_SHADER
-precision mediump float;
precision mediump int;
uniform mat4 uModelViewMatrix;
@@ -32,19 +31,19 @@ uniform vec4 uViewport;
uniform int uPerspective;
uniform int uStrokeJoin;
-attribute vec4 aPosition;
-attribute vec3 aTangentIn;
-attribute vec3 aTangentOut;
-attribute float aSide;
-attribute vec4 aVertexColor;
-
-varying vec4 vColor;
-varying vec2 vTangent;
-varying vec2 vCenter;
-varying vec2 vPosition;
-varying float vMaxDist;
-varying float vCap;
-varying float vJoin;
+IN vec4 aPosition;
+IN vec3 aTangentIn;
+IN vec3 aTangentOut;
+IN float aSide;
+IN vec4 aVertexColor;
+
+OUT vec4 vColor;
+OUT vec2 vTangent;
+OUT vec2 vCenter;
+OUT vec2 vPosition;
+OUT float vMaxDist;
+OUT float vCap;
+OUT float vJoin;
vec2 lineIntersection(vec2 aPoint, vec2 aDir, vec2 bPoint, vec2 bDir) {
// Rotate and translate so a starts at the origin and goes out to the right
diff --git a/src/webgl/shaders/normal.frag b/src/webgl/shaders/normal.frag
index d9613af243..6b0e370158 100644
--- a/src/webgl/shaders/normal.frag
+++ b/src/webgl/shaders/normal.frag
@@ -1,5 +1,4 @@
-precision mediump float;
-varying vec3 vVertexNormal;
+IN vec3 vVertexNormal;
void main(void) {
- gl_FragColor = vec4(vVertexNormal, 1.0);
-}
\ No newline at end of file
+ OUT_COLOR = vec4(vVertexNormal, 1.0);
+}
diff --git a/src/webgl/shaders/normal.vert b/src/webgl/shaders/normal.vert
index 8a94e83ed1..a428dbdd27 100644
--- a/src/webgl/shaders/normal.vert
+++ b/src/webgl/shaders/normal.vert
@@ -1,7 +1,7 @@
-attribute vec3 aPosition;
-attribute vec3 aNormal;
-attribute vec2 aTexCoord;
-attribute vec4 aVertexColor;
+IN vec3 aPosition;
+IN vec3 aNormal;
+IN vec2 aTexCoord;
+IN vec4 aVertexColor;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
@@ -10,9 +10,9 @@ uniform mat3 uNormalMatrix;
uniform vec4 uMaterialColor;
uniform bool uUseVertexColor;
-varying vec3 vVertexNormal;
-varying highp vec2 vVertTexCoord;
-varying vec4 vColor;
+OUT vec3 vVertexNormal;
+OUT highp vec2 vVertTexCoord;
+OUT vec4 vColor;
void main(void) {
vec4 positionVec4 = vec4(aPosition, 1.0);
diff --git a/src/webgl/shaders/phong.frag b/src/webgl/shaders/phong.frag
index fa80efa267..c22531087d 100644
--- a/src/webgl/shaders/phong.frag
+++ b/src/webgl/shaders/phong.frag
@@ -1,5 +1,4 @@
// include lighting.glsl
-precision highp float;
precision highp int;
uniform bool uHasSetAmbient;
@@ -11,11 +10,11 @@ uniform vec4 uTint;
uniform sampler2D uSampler;
uniform bool isTexture;
-varying vec3 vNormal;
-varying vec2 vTexCoord;
-varying vec3 vViewPosition;
-varying vec3 vAmbientColor;
-varying vec4 vColor;
+IN vec3 vNormal;
+IN vec2 vTexCoord;
+IN vec3 vViewPosition;
+IN vec3 vAmbientColor;
+IN vec4 vColor;
void main(void) {
@@ -29,11 +28,11 @@ void main(void) {
// Textures come in with premultiplied alpha. To apply tint and still have
// premultiplied alpha output, we need to multiply the RGB channels by the
// tint RGB, and all channels by the tint alpha.
- ? texture2D(uSampler, vTexCoord) * vec4(uTint.rgb/255., 1.) * (uTint.a/255.)
+ ? TEXTURE(uSampler, vTexCoord) * vec4(uTint.rgb/255., 1.) * (uTint.a/255.)
// Colors come in with unmultiplied alpha, so we need to multiply the RGB
// channels by alpha to convert it to premultiplied alpha.
: vec4(vColor.rgb * vColor.a, vColor.a);
- gl_FragColor = vec4(diffuse * baseColor.rgb +
+ OUT_COLOR = vec4(diffuse * baseColor.rgb +
vAmbientColor * (
uHasSetAmbient ? uAmbientMatColor.rgb : baseColor.rgb
) +
diff --git a/src/webgl/shaders/phong.vert b/src/webgl/shaders/phong.vert
index 4a6a7f1400..5667709162 100644
--- a/src/webgl/shaders/phong.vert
+++ b/src/webgl/shaders/phong.vert
@@ -1,10 +1,9 @@
-precision highp float;
precision highp int;
-attribute vec3 aPosition;
-attribute vec3 aNormal;
-attribute vec2 aTexCoord;
-attribute vec4 aVertexColor;
+IN vec3 aPosition;
+IN vec3 aNormal;
+IN vec2 aTexCoord;
+IN vec4 aVertexColor;
uniform vec3 uAmbientColor[5];
@@ -16,11 +15,11 @@ uniform int uAmbientLightCount;
uniform bool uUseVertexColor;
uniform vec4 uMaterialColor;
-varying vec3 vNormal;
-varying vec2 vTexCoord;
-varying vec3 vViewPosition;
-varying vec3 vAmbientColor;
-varying vec4 vColor;
+OUT vec3 vNormal;
+OUT vec2 vTexCoord;
+OUT vec3 vViewPosition;
+OUT vec3 vAmbientColor;
+OUT vec4 vColor;
void main(void) {
diff --git a/src/webgl/shaders/point.frag b/src/webgl/shaders/point.frag
index eeafb4ef83..5185794d37 100644
--- a/src/webgl/shaders/point.frag
+++ b/src/webgl/shaders/point.frag
@@ -1,7 +1,6 @@
-precision mediump float;
precision mediump int;
uniform vec4 uMaterialColor;
-varying float vStrokeWeight;
+IN float vStrokeWeight;
void main(){
float mask = 0.0;
@@ -24,5 +23,5 @@ void main(){
discard;
}
- gl_FragColor = vec4(uMaterialColor.rgb, 1.) * uMaterialColor.a;
+ OUT_COLOR = vec4(uMaterialColor.rgb, 1.) * uMaterialColor.a;
}
diff --git a/src/webgl/shaders/point.vert b/src/webgl/shaders/point.vert
index 24d9a405b7..9df67d1588 100644
--- a/src/webgl/shaders/point.vert
+++ b/src/webgl/shaders/point.vert
@@ -1,6 +1,6 @@
-attribute vec3 aPosition;
+IN vec3 aPosition;
uniform float uPointSize;
-varying float vStrokeWeight;
+OUT float vStrokeWeight;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main() {
@@ -8,4 +8,4 @@ void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
gl_PointSize = uPointSize;
vStrokeWeight = uPointSize;
-}
\ No newline at end of file
+}
diff --git a/src/webgl/shaders/vertexColor.frag b/src/webgl/shaders/vertexColor.frag
index be191e1c34..11b14ea09c 100644
--- a/src/webgl/shaders/vertexColor.frag
+++ b/src/webgl/shaders/vertexColor.frag
@@ -1,5 +1,4 @@
-precision mediump float;
-varying vec4 vColor;
+IN vec4 vColor;
void main(void) {
- gl_FragColor = vec4(vColor.rgb, 1.) * vColor.a;
+ OUT_COLOR = vec4(vColor.rgb, 1.) * vColor.a;
}
diff --git a/src/webgl/shaders/vertexColor.vert b/src/webgl/shaders/vertexColor.vert
index b91c914bf4..3dc3df2434 100644
--- a/src/webgl/shaders/vertexColor.vert
+++ b/src/webgl/shaders/vertexColor.vert
@@ -1,10 +1,10 @@
-attribute vec3 aPosition;
-attribute vec4 aVertexColor;
+IN vec3 aPosition;
+IN vec4 aVertexColor;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
-varying vec4 vColor;
+OUT vec4 vColor;
void main(void) {
vec4 positionVec4 = vec4(aPosition, 1.0);