Skip to content

Addresses issue #6587 #6665

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

Draft
wants to merge 37 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
227fe69
Extending p5.texture for cubemap textures
Garima3110 Dec 24, 2023
8e2d05c
Merge remote-tracking branch 'upstream/main' into cubeMap
Garima3110 Dec 25, 2023
317d915
Merge remote-tracking branch 'upstream/main' into cubeMap
Garima3110 Dec 28, 2023
917de2f
Added vertex and fragment shaders for cubemaps
Garima3110 Dec 28, 2023
5139c0a
Updated src/webgl/shaders/lighting.glsl file for cubemaps
Garima3110 Dec 28, 2023
45e33d5
Storing the diffuse irradiance in a cubemap texture
Garima3110 Dec 28, 2023
1d54fad
Added caching mechanism for shaders
Garima3110 Dec 29, 2023
1ff8702
Updated caching for some shaders
Garima3110 Dec 29, 2023
6cd7856
Updated p5.Texture.js
Garima3110 Dec 29, 2023
4401ebd
Updated cubeVertex and cubeFragment shaders
Garima3110 Dec 29, 2023
37751d9
renamed cubemap shader files
Garima3110 Dec 29, 2023
93b8b50
Updated p5.RendererGL.js
Garima3110 Dec 29, 2023
66dc0dc
Updated p5.Texture.js
Garima3110 Dec 29, 2023
cffda69
Updated lighting.glsl
Garima3110 Dec 29, 2023
751b16a
Added TEXTURE_CUBE
Garima3110 Dec 29, 2023
29e7a14
Added gl.SAMPLER_CUBE uniform type to p5.Shader.js
Garima3110 Dec 29, 2023
7981fdc
Merge remote-tracking branch 'upstream/main' into cubeMap
Garima3110 Dec 31, 2023
e7cd392
Minor changes in SAMPLER_CUBE uniform type
Garima3110 Dec 31, 2023
f30af0e
Minor updates on caching shaders
Garima3110 Dec 31, 2023
d51c4a8
Merge branch 'processing:main' into cubeMap
Garima3110 Jan 4, 2024
38bcaab
Merge branch 'processing:main' into cubeMap
Garima3110 Jan 8, 2024
2558755
Merge remote-tracking branch 'upstream/main' into cubeMap
Garima3110 Jan 13, 2024
773198a
Updated RendererGL.js
Garima3110 Jan 13, 2024
bbc7c72
Merge remote-tracking branch 'upstream/main' into cubeMap
Garima3110 Jan 15, 2024
ffd9097
Reordered some code in p5.RendererGL.js
Garima3110 Jan 16, 2024
cc77e7e
Minor fixes
Garima3110 Jan 20, 2024
518b0f0
Merge remote-tracking branch 'upstream/main' into cubeMap
Garima3110 Jan 20, 2024
c124738
Fragment shader error fixed
Garima3110 Jan 23, 2024
38e6e84
Merge remote-tracking branch 'upstream/main' into cubeMap
Garima3110 Jan 23, 2024
a585ef5
Minor updates
Garima3110 Jan 23, 2024
f118a3b
some fixes
Garima3110 Jan 24, 2024
2da03d1
Merge branch 'processing:main' into cubeMap
Garima3110 Jan 29, 2024
5961be8
Minor changes
Garima3110 Mar 30, 2024
4515c09
Some changes with cubemap implementation
Garima3110 Jun 20, 2024
be6d27f
Minor changes
Garima3110 Jun 24, 2024
6dc9916
fix
Garima3110 Jun 24, 2024
3133e80
Merge branch 'processing:main' into cubeMap
Garima3110 Nov 2, 2024
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
136 changes: 107 additions & 29 deletions src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
@@ -9,10 +9,11 @@ import './p5.Matrix';
import './p5.Framebuffer';
import { readFileSync } from 'fs';
import { join } from 'path';
import { MipmapTexture } from './p5.Texture';
import { CubemapTexture, MipmapTexture } from './p5.Texture';

const STROKE_CAP_ENUM = {};
const STROKE_JOIN_ENUM = {};

let lineDefs = '';
const defineStrokeCapEnum = function (key, val) {
lineDefs += `#define STROKE_CAP_${key} ${val}\n`;
@@ -83,7 +84,9 @@ const defaultShaders = {
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')
imageLightSpecularFrag: readFileSync(join(__dirname, '/shaders/imageLightSpecular.frag'), 'utf-8'),
cubemapVertexShader: readFileSync(join(__dirname, '/shaders/cubeVertex.vert'), 'utf-8'),
cubemapFragmentShader: readFileSync(join(__dirname, '/shaders/cubeFragment.frag'), 'utf-8')
};
let sphereMapping = defaultShaders.sphereMappingFrag;
for (const key in defaultShaders) {
@@ -110,6 +113,14 @@ const filterShaderFrags = {
};
const filterShaderVert = readFileSync(join(__dirname, '/shaders/filters/default.vert'), 'utf-8');


function renderCube() {
push(); // Save the current state of the drawing
translate(0, 0, 0); // Translate to the position where you want to draw the cube
box(2); // Draw a cube with a size of 2 units (you can adjust the size)
pop(); // Restore the previous state of the drawing
}

/**
* @module Rendering
* @submodule Rendering
@@ -450,6 +461,19 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this.GL = this.drawingContext;
this._pInst._setProperty('drawingContext', this.drawingContext);

if (!this._pInst.shaderCache) {
this._pInst.shaderCache = {}; // ensures a unique shaderCache for each instance of p5.RendererGL
}
this.getCachedShader =
function (shaderKey, vertexShaderSource, fragmentShaderSource) {
if (!this._pInst.shaderCache[shaderKey]) {

this._pInst.shaderCache[shaderKey] = this._pInst.createShader(
vertexShaderSource, fragmentShaderSource);
}
return this._pInst.shaderCache[shaderKey];
};

// erasing
this._isErasing = false;

@@ -574,6 +598,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this._defaultNormalShader = undefined;
this._defaultColorShader = undefined;
this._defaultPointShader = undefined;
this._defaultCubemapShader=undefined;

this.userFillShader = undefined;
this.userStrokeShader = undefined;
@@ -872,6 +897,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this._pInst,
!isPGraphics
);

this._pInst._setProperty('_renderer', renderer);
renderer.resize(w, h);
renderer._applyDefaults();
@@ -2051,6 +2077,15 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
return this._defaultFontShader;
}

_getCubemapShader() {
return this.getCachedShader('_defaultCubemapShader',
this._webGL2CompatibilityPrefix('vert', 'mediump') +
defaultShaders.cubemapVertexShader,
this._webGL2CompatibilityPrefix('frag', 'mediump') +
defaultShaders.cubemapFragmentShader
);
}

_webGL2CompatibilityPrefix(
shaderType,
floatPrecision
@@ -2103,44 +2138,87 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
* maps a p5.Image used by imageLight() to a p5.Framebuffer
*/
getDiffusedTexture(input) {
// if one already exists for a given input image
// 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 newFramebuffer;
// 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));
newFramebuffer = this._pInst.createFramebuffer({
width, height, density: 1
});
// create framebuffer is like making a new sketch, all functions on main
// sketch it would be available on framebuffer
if (!this.diffusedShader) {
this.diffusedShader = this._pInst.createShader(
defaultShaders.imageLightVert,
defaultShaders.imageLightDiffusedFrag

// Create a cubemap texture
const envCubemap = this.GL.createTexture();
this.GL.bindTexture(this.GL.TEXTURE_CUBE_MAP, envCubemap);

// Loop through each face and allocate storage
for (let i = 0; i < 6; ++i) {
this.GL.texImage2D(
this.GL.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
this.GL.RGB16F, 512, 512, 0,
this.GL.RGB, this.GL.FLOAT, null
);
}
newFramebuffer.draw(() => {
this._pInst.shader(this.diffusedShader);
this.diffusedShader.setUniform('environmentMap', input);
this._pInst.noStroke();
this._pInst.rectMode(constants.CENTER);
this._pInst.noLights();
this._pInst.rect(0, 0, width, height);

// Set parameters for the cubemap
this.GL.texParameteri(this.GL.TEXTURE_CUBE_MAP
, this.GL.TEXTURE_WRAP_S, this.GL.CLAMP_TO_EDGE);
this.GL.texParameteri(this.GL.TEXTURE_CUBE_MAP
, this.GL.TEXTURE_WRAP_T, this.GL.CLAMP_TO_EDGE);
this.GL.texParameteri(this.GL.TEXTURE_CUBE_MAP
, this.GL.TEXTURE_WRAP_R, this.GL.CLAMP_TO_EDGE);
this.GL.texParameteri(this.GL.TEXTURE_CUBE_MAP
, this.GL.TEXTURE_MIN_FILTER, this.GL.LINEAR);
this.GL.texParameteri(this.GL.TEXTURE_CUBE_MAP
, this.GL.TEXTURE_MAG_FILTER, this.GL.LINEAR);

// Get the cubemap shader
const cubemapShader = this._getCubemapShader();
this._pInst.shader(cubemapShader);

// Convert p5.Image to WebGLTexture
const texture = new p5.Texture(this, input);
const webglTexture = texture.glTex;

cubemapShader.setUniform('equirectangularMap', webglTexture);

this.GL.activeTexture(this.GL.TEXTURE0);
this.GL.bindTexture(this.GL.TEXTURE_2D, input);

// Create a new framebuffer
let newFramebuffer = this._pInst.createFramebuffer({
width: 512,
height: 512,
density: 1
});
this.diffusedTextures.set(input, newFramebuffer);
return newFramebuffer;

this.GL.bindFramebuffer(this.GL.FRAMEBUFFER, newFramebuffer.handle);

// Render each face of the cubemap
for (let i = 0; i < 6; ++i) {
this.GL.framebufferTexture2D(
this.GL.FRAMEBUFFER,
this.GL.COLOR_ATTACHMENT0,
this.GL.TEXTURE_CUBE_MAP_POSITIVE_X + i,
envCubemap,
0
);

this.GL.clear(this.GL.COLOR_BUFFER_BIT | this.GL.DEPTH_BUFFER_BIT);

renderCube(); // Renders a 1x1 cube
}

this.GL.bindFramebuffer(this.GL.FRAMEBUFFER, null);

// Initialize CubemapTexture class with the cubemap texture
let cubemapTexture = new CubemapTexture(this, envCubemap, {});
this.diffusedTextures.set(input, cubemapTexture);

return cubemapTexture;
}

/*
* 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
* sizes and storing 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
@@ -2317,7 +2395,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
// 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);
shader.setUniform('environmentMapDiffusedCubemap', diffusedLight);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am not sure but I feel diffusedLight is also a sampler2D texture, could it be passed as a uniform with samplerCube texture?

Copy link
Member Author

Choose a reason for hiding this comment

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

Here, diffusedLight is obtained from getDiffusedTexture, which returns a CubemapTexture object. Since environmentMapDiffusedCubemap in the shader expects a cubemap texture (samplerCube), so I think diffusedLight is being correctly assigned to the environmentMapDiffusedCubemap uniform in the shader, which is of type samplerCube.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oooh...sorry. I overlooked this, previously we had diffusedLight with sampler2D texture so I got confused. I see you have updated getDiffusedTexture()

let specularLight = this.getSpecularTexture(this.activeImageLight);

shader.setUniform('environmentMapSpecular', specularLight);
3 changes: 2 additions & 1 deletion src/webgl/p5.Shader.js
Original file line number Diff line number Diff line change
@@ -852,7 +852,7 @@ p5.Shader = class {
uniform.name = uniformName;
uniform.type = uniformInfo.type;
uniform._cachedData = undefined;
if (uniform.type === gl.SAMPLER_2D) {
if (uniform.type === gl.SAMPLER_2D || uniform.type === gl.SAMPLER_CUBE) {
uniform.samplerIndex = samplerIndex;
samplerIndex++;
this.samplers.push(uniform);
@@ -1326,6 +1326,7 @@ p5.Shader = class {
}
break;
case gl.SAMPLER_2D:
case gl.SAMPLER_CUBE:
gl.activeTexture(gl.TEXTURE0 + uniform.samplerIndex);
uniform.texture =
data instanceof p5.Texture ? data : this._renderer.getTexture(data);
79 changes: 77 additions & 2 deletions src/webgl/p5.Texture.js
Original file line number Diff line number Diff line change
@@ -132,6 +132,8 @@ p5.Texture = class Texture {
textureData = this.src.canvas || this.src.elt;
} else if (this.isImageData) {
textureData = this.src;
} else if (this.isCubemapTexture) {
textureData = this.src;
}
return textureData;
}
@@ -196,7 +198,7 @@ p5.Texture = class Texture {
/**
* Checks if the source data for this texture has changed (if it's
* easy to do so) and reuploads the texture if necessary. If it's not
* possible or to expensive to do a calculation to determine wheter or
* possible or too expensive to do a calculation to determine whether or
* not the data has occurred, this method simply re-uploads the texture.
* @method update
*/
@@ -513,12 +515,85 @@ export function checkWebGLCapabilities({ GL, webglVersion }) {
: gl.getExtension('OES_texture_half_float');
const supportsHalfFloatLinear = supportsHalfFloat &&
gl.getExtension('OES_texture_half_float_linear');
const supportsCubemap = (webglVersion === constants.WEBGL2)
|| (gl.getExtension('OES_texture_cube_map'));
return {
float: supportsFloat,
floatLinear: supportsFloatLinear,
halfFloat: supportsHalfFloat,
halfFloatLinear: supportsHalfFloatLinear
halfFloatLinear: supportsHalfFloatLinear,
cubemap: supportsCubemap
};
}

export class CubemapTexture extends p5.Texture {
constructor(renderer, faces, settings) {
super(renderer, faces, settings);

this.isCubemapTexture = false; // Default value

if (faces instanceof CubemapTexture) {
this.isCubemapTexture = true;
}
}

glFilter(_filter) {
const gl = this._renderer.GL;
// TODO: Support other filters if needed
return gl.LINEAR;
}

_getTextureDataFromSource() {
return this.src;
}

init(faces) {
const gl = this._renderer.GL;
this.glTex = gl.createTexture();

this.bindTexture();

// Looping through each face and loading the data
const targets = [
gl.TEXTURE_CUBE_MAP_POSITIVE_X,
gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
];
// Looping through each face and loading the data
for (let faceIndex = 0; faceIndex < targets.length; faceIndex++) {
// Setting up each face of the cubemap
gl.texImage2D(
targets[faceIndex],
0,
this.glFormat,
this.width,
this.height,
0,
this.glFormat,
this.glDataType,
faces[faceIndex]
);
}

// Set parameters for the cubemap
gl.texParameteri(gl.TEXTURE_CUBE_MAP
, gl.TEXTURE_MAG_FILTER, this.glMagFilter);
gl.texParameteri(gl.TEXTURE_CUBE_MAP
, gl.TEXTURE_MIN_FILTER, this.glMinFilter);
gl.texParameteri(gl.TEXTURE_CUBE_MAP
, gl.TEXTURE_WRAP_S, this.glWrapS);
gl.texParameteri(gl.TEXTURE_CUBE_MAP
, gl.TEXTURE_WRAP_T, this.glWrapT);

this.unbindTexture();
}

update() {
// Custom update logic, if needed
}
}

export default p5.Texture;
22 changes: 22 additions & 0 deletions src/webgl/shaders/cubeFragment.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Adapted from: https://learnopengl.com/PBR/IBL/Diffuse-irradiance
IN vec3 localPos;
Copy link
Collaborator

Choose a reason for hiding this comment

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

In my opinion I feel for maximum compatibility, we can do this one in GL ES 100 instead of 300? Using attribute instead of IN, using the spacial variable gl_FragColor instead of defining an OUT_COLOR, and using texture2D() instead of texture()? What you feel?

Copy link
Member Author

Choose a reason for hiding this comment

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

Well I started from that only , and was getting a lot of errors and from Dave's suggestion of using the macros instead resolved the errors , I again tried doing what u have suggested today, but strangely that gives in more errors! So I think we should stick to using these macros only :-)
You can refer to Dave's suggestion here in this comment:
#6665 (comment)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmmm...It looks like you need to use both webgl-1 and webgl-2 modes on shaders.


uniform sampler2D equirectangularMap;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you have hard-coded the value of equirectangularMap to "0" which won't work or its actually a float value.


const vec2 invAtan = vec2(0.1591, 0.3183);

vec2 SampleSphericalMap(vec3 v)
{
vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
uv *= invAtan;
uv += 0.5;
return uv;
}

void main()
{
vec2 uv = SampleSphericalMap(normalize(localPos));
vec3 color = TEXTURE(equirectangularMap, uv).rgb;

OUT_COLOR = vec4(color, 1.0);
}
13 changes: 13 additions & 0 deletions src/webgl/shaders/cubeVertex.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Adapted from: https://learnopengl.com/PBR/IBL/Diffuse-irradiance
layout (location = 0) IN vec3 aPosition;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks good, but I feel like do we actually need this layout(). Can it be done just with IN vec3 aPosition;

Copy link
Member Author

Choose a reason for hiding this comment

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

Well honestly speaking , I'm also not sure about what exactly it signifies here, but as i searched on web , I found this
If your application code (typically written in a language like C++ or JavaScript) explicitly binds attribute locations using glBindAttribLocation, you can omit the layout(location = 0) in the shader. The application code would look something like this:

glBindAttribLocation(program, 0, "aPosition");

If your application does not do this, you should keep layout(location = 0) to ensure that the attribute location is explicitly defined.

Also , just to mention , I tried running the test sketch by omitting this, and the results were similar , So I think we should keep it for a safer side as its not causing any error.
What say?!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, this should not give any errors. Since, vertex shaders are just dealing with the positions, I think we don't have layout(...) at other files and they are working perfectly.

But the main reason in my mind is, since we only use layout with in we can't use it with attributes, where I think is layout used only for webgl-2? If that the case, layout should be omitted.


OUT vec3 localPos;

uniform mat4 uProjectionMatrix;
uniform mat4 uModelViewMatrix;

void main()
{
localPos = aPosition;
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(localPos, 1.0);
}
8 changes: 6 additions & 2 deletions src/webgl/shaders/lighting.glsl
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ uniform float uQuadraticAttenuation;
// boolean to initiate the calculateImageDiffuse and calculateImageSpecular
uniform bool uUseImageLight;
// texture for use in calculateImageDiffuse
uniform sampler2D environmentMapDiffused;
uniform samplerCube environmentMapDiffusedCubemap;
// texture for use in calculateImageSpecular
uniform sampler2D environmentMapSpecular;

@@ -112,7 +112,11 @@ vec3 calculateImageDiffuse(vec3 vNormal, vec3 vViewPosition, float metallic){
vec3 worldCameraPosition = vec3(0.0, 0.0, 0.0); // hardcoded world camera position
vec3 worldNormal = normalize(vNormal * uCameraRotation);
vec2 newTexCoor = mapTextureToNormal( worldNormal );
vec4 texture = TEXTURE( environmentMapDiffused, newTexCoor );

// Sample the diffuse color from the environment map (cube map) using the transformed
// world-normal vector as the direction in the cube map space.
vec4 texture = TEXTURE_CUBE( environmentMapDiffusedCubemap, worldNormal );

// this is to make the darker sections more dark
// png and jpg usually flatten the brightness so it is to reverse that
return mix(smoothstep(vec3(0.0), vec3(1.0), texture.xyz), vec3(0.0), metallic);
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.