Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image Light Feature - GSOC 2023 #6255

Merged
merged 56 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
50a0c14
Added functions for diffused image light
Jul 6, 2023
dfa600c
Added functions for diffused image light
Jul 6, 2023
0521694
Merge branch 'main' into imageLight
Jul 10, 2023
b155bac
Merge branch 'main' into imageLight
Jul 13, 2023
34e6438
Added shaders and code to read them
Jul 13, 2023
916f5be
Merge branch 'main' into imageLight
Jul 20, 2023
c59ae6c
refactoring the ImageLight fragment shader
Jul 20, 2023
8b1e19c
added definition of map function in lightingglsl
Jul 21, 2023
5287a4f
removed errors
Jul 21, 2023
b05bca6
error correction
Jul 25, 2023
f6899a8
Merge branch 'main' into imageLight
Jul 25, 2023
8cd5775
corrected errors
Jul 25, 2023
3c9bf12
added comments
Aug 4, 2023
701cbee
added comments
Aug 4, 2023
4c63de5
Merge branch 'main' into imageLight
AryanKoundal Aug 5, 2023
b9837dc
made the changes from the build file test
AryanKoundal Aug 5, 2023
5546093
create a new map for storing the images and modifying the files where…
AryanKoundal Aug 6, 2023
98af50f
modifications according to the reviews
AryanKoundal Aug 8, 2023
8dbbff5
Merge branch 'main' into imageLight
AryanKoundal Aug 8, 2023
711c5f5
applied all the changes after debugging
AryanKoundal Aug 8, 2023
52c4d42
Merge branch 'main' into imageLight
AryanKoundal Aug 11, 2023
0121439
made changes according to the reviews
AryanKoundal Aug 11, 2023
8e9cd8a
Merge branch 'main' into imageLight
AryanKoundal Sep 8, 2023
7cf062d
Convert existing shaders to support GLES 300
davepagurek Sep 24, 2023
93a2428
Merge branch 'main' into imageLight
AryanKoundal Oct 10, 2023
d1c19b3
Merge branch 'imageLight' into feat/mipmap
davepagurek Oct 10, 2023
b803601
getting on track with main
AryanKoundal Oct 13, 2023
dc0ef6a
Merge branch 'main' into imageLight
AryanKoundal Oct 13, 2023
f0f3a19
Merge pull request #1 from davepagurek/feat/mipmap
AryanKoundal Oct 13, 2023
02d6f00
added code for specular light
AryanKoundal Oct 14, 2023
9a0c3fc
Merge branch 'main' into imageLight
AryanKoundal Oct 14, 2023
0860bd7
Fix tests
davepagurek Oct 15, 2023
0ade514
modifications to rendererGl and lighting.glsl
AryanKoundal Oct 18, 2023
07371e8
Merge branch 'main' into imageLight
AryanKoundal Oct 18, 2023
25a711b
Merge pull request #2 from davepagurek/feat/mipmap
AryanKoundal Oct 18, 2023
c04d916
additions
AryanKoundal Oct 23, 2023
9dbe8a0
Merge branch 'main' into imageLight
AryanKoundal Oct 23, 2023
5cac577
Fall back to regular texture on WebGL1
davepagurek Oct 23, 2023
5547d7d
resolved errors
AryanKoundal Oct 26, 2023
ff3973d
Merge branch 'main' into imageLight
AryanKoundal Oct 26, 2023
b37afe5
Updated the outcolor
AryanKoundal Oct 26, 2023
69008c0
added comments
AryanKoundal Oct 26, 2023
d713b35
resolved pow error and added documentation
AryanKoundal Oct 26, 2023
6656e90
added documentation and example
AryanKoundal Oct 27, 2023
374051b
resolved imageLight documentation example error
AryanKoundal Oct 27, 2023
b964403
modified timeout to 25sec for imageLight examples to work
AryanKoundal Oct 27, 2023
0e7c3bc
modified timeout to 25sec for imageLight examples to work
AryanKoundal Oct 27, 2023
14aed59
changed settimeout back to original 4sec limit and changed the image …
AryanKoundal Oct 27, 2023
5d99712
Merge branch 'main' into imageLight
AryanKoundal Oct 27, 2023
4fe2890
slight modifications on the suggestions
AryanKoundal Oct 28, 2023
6fec78a
resolved suggestion
AryanKoundal Oct 30, 2023
94e639b
used WorldNormalInstead of R in lighting.glgl
AryanKoundal Oct 31, 2023
f2c0f1e
changed angles in lighting.glsl
AryanKoundal Oct 31, 2023
e68f9f0
added images in examples and removed unnecessary code
AryanKoundal Oct 31, 2023
6df91ec
Merge branch 'main' into imageLight
AryanKoundal Nov 3, 2023
fe5e0db
replaced asset with smaller size
AryanKoundal Nov 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/webgl/light.js
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,12 @@ p5.prototype.pointLight = function(v1, v2, v3, x, y, z) {
return this;
};

p5.prototype.imageLight = function(img){
davepagurek marked this conversation as resolved.
Show resolved Hide resolved
// setting activeImageLight so that the setUniform is executed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can be a bit more specific here and say that this is what _setImageLightUniforms looks at to see if it needs to send uniforms to the fill shader

this._renderer.activeImageLight = img;
davepagurek marked this conversation as resolved.
Show resolved Hide resolved
this._renderer._enableLighting = true;
};

/**
* Places an ambient and directional light in the scene.
* The lights are set to ambientLight(128, 128, 128) and
Expand Down
172 changes: 153 additions & 19 deletions src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down Expand Up @@ -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]:
Expand Down Expand Up @@ -468,6 +473,17 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this.spotLightAngle = [];
this.spotLightConc = [];

// Null if no image light is set, otherwise, it is set to a p5.Image
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can rephrase this into sentences so it's a bit easier to read? Also, for each map, we should say what the keys and values are. E.g. for this one, it maps a p5.Image used by imageLight() to a p5.Graphics containing the combined light it sends to each direction.

// made a blurrytexture attribute to be used in imageLight function
// this is to lookup image from blurrytexture Map rather from a
// texture map
this.diffusedTextures = new Map();
// map to store the created textures to prevent mis calculation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one maps the input p5.Image to a p5.MipmapTexture. Worth mentioning that we use a mipmap texture because it can contain light from multiple roughness levels.

this.specularTextures = new Map();
// property to be set true after imageLight is called
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slight update to this: we don't set it to true, we set it to a p5.Image. Instead of checking if it's true, we check if it's not null

// then if it is true the setUniform would be done on shader
this.activeImageLight = null;

this.drawMode = constants.FILL;

this.curFillColor = this._cachedFillStyle = [1, 1, 1, 1];
Expand Down Expand Up @@ -601,6 +617,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this.curStrokeCap = constants.ROUND;
this.curStrokeJoin = constants.ROUND;

// repetition
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this line?

// map of texture sources to textures created in this gl context via this.getTexture(src)
this.textures = new Map();

Expand Down Expand Up @@ -1740,14 +1757,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
);
}
}
Expand All @@ -1759,8 +1780,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
);
}

Expand All @@ -1771,8 +1794,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
);
}

Expand All @@ -1783,8 +1808,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
);
}

Expand All @@ -1795,8 +1822,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;
Expand All @@ -1806,8 +1835,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
);
}

Expand Down Expand Up @@ -1875,6 +1906,81 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
return tex;
}

// used in imageLight
// create a new texture from the img input
/*
To create a blurry image from the input non blurry img
Add it to the blurryTexture map
Returns the blurry image
*/
getBlurryTexture(input){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can rename this to getDiffusedTexture for consistency?

// if one already exists for a given input image
if(this.diffusedTextures.get(input)!=null){
return this.diffusedTextures.get(input);
}
// if not, then create one
let newGraphic; // maybe switch to framebuffer
// draw the blurry image
// set the shader on the graphic and set the shader on the image
// this._renderer._applyTextProperties(img, newGraphic);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can take out this one

// make small width, hardcode to 200px
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could mention that it's OK that it's small because it's going to be really blurry and smooth anyway

let smallWidth = 200;
//let smallWidth = input.width;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be removed

let width = smallWidth;
let height = Math.floor(smallWidth * (input.height / input.width));
newGraphic = createGraphics(width, height, 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;
}

getSpecularTexture(input){
// check if already exits (there are tex of diff resolution so which one to check)
// currently doing the whole array
// please check this line ?
if(this.specularTextures.get(input)!=null){
return this.specularTextures.get(input);
}
// What should be the size?
const size = 512;
let tex;
const levels = [];
const graphic = createGraphics(size, size, WEBGL);
let count = Math.log(size)/Math.log(2);
graphic.pixelDensity(1);
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));
// please check this line?
}
graphic.remove();
tex = new MipmapTexture(this, levels, {});
this.specularTextures.set(input,tex);
return tex;
}

/**
* @method activeFramebuffer
* @private
Expand Down Expand Up @@ -1920,6 +2026,9 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
fillShader.setUniform('uEmissive', this._useEmissiveMaterial);
fillShader.setUniform('uShininess', this._useShininess);

// calling the _setImageLightUniforms from here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one probably doesn't need a comment, since it doesn't add much over the code itself

this._setImageLightUniforms(fillShader);

fillShader.setUniform('uUseLighting', this._enableLighting);

const pointLightCount = this.pointLightDiffuseColors.length / 3;
Expand Down Expand Up @@ -1970,6 +2079,31 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
fillShader.bindTextures();
}

// getting called from _setFillUniforms
_setImageLightUniforms(shader){
//set uniform values
if( this.activeImageLight == null ){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just for debugging, right? We can probably remove this if block now

console.log('activeImageLight prop is null');
}
shader.setUniform('uUseImageLight', this.activeImageLight != null );
// true
if (this.activeImageLight) {
// this.activeImageLight has image as a key
// look up the texture from the blurryTexture map
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another spot where we can rename to diffusedTextures for consistency

// change the name of shader of diffused to prevent confusion
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can take this note out now

let diffusedLight = this.getBlurryTexture(this.activeImageLight);
// change the name of this uniform as diff for diffused and specular
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can take this one out too

shader.setUniform('environmentMapDiffused', diffusedLight);
let specularLight = this.getSpecularTexture(this.activeImageLight);
// shine >= 1 so
// 0-1*8 = 0-8
// 0-8 * 20 = 0-160
let roughness = 20/this._useShininess;
shader.setUniform('levelOfDetail', roughness*8);
davepagurek marked this conversation as resolved.
Show resolved Hide resolved
shader.setUniform('environmentMapSpecular', specularLight);
}
}

_setPointUniforms(pointShader) {
pointShader.bindShader();

Expand Down
67 changes: 56 additions & 11 deletions src/webgl/p5.Texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,24 +337,24 @@ 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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.glMagFilter);
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:
Expand Down Expand Up @@ -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
Expand Down
Loading
Loading