-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
base: main
Are you sure you want to change the base?
Addresses issue #6587 #6665
Changes from 34 commits
227fe69
8e2d05c
317d915
917de2f
5139c0a
45e33d5
1d54fad
1ff8702
6cd7856
4401ebd
37751d9
93b8b50
66dc0dc
cffda69
751b16a
29e7a14
7981fdc
e7cd392
f30af0e
d51c4a8
38bcaab
2558755
773198a
bbc7c72
ffd9097
cc77e7e
518b0f0
c124738
38e6e84
a585ef5
f118a3b
2da03d1
5961be8
4515c09
be6d27f
6dc9916
3133e80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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`; | ||
|
|
@@ -78,7 +79,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') | ||
| }; | ||
| for (const key in defaultShaders) { | ||
| defaultShaders[key] = webgl2CompatibilityShader + defaultShaders[key]; | ||
|
|
@@ -104,6 +107,91 @@ const filterShaderFrags = { | |
| }; | ||
| const filterShaderVert = readFileSync(join(__dirname, '/shaders/filters/default.vert'), 'utf-8'); | ||
|
|
||
| function lookAt(eye, target, up) { | ||
| let zAxis = normalize(subtractVectors(eye, target)); | ||
| let xAxis = normalize(cross(up, zAxis)); | ||
| let yAxis = cross(zAxis, xAxis); | ||
|
|
||
| return [ | ||
| xAxis[0], yAxis[0], zAxis[0], 0, | ||
| xAxis[1], yAxis[1], zAxis[1], 0, | ||
| xAxis[2], yAxis[2], zAxis[2], 0, | ||
| -dot(xAxis, eye), -dot(yAxis, eye), -dot(zAxis, eye), 1 | ||
| ]; | ||
| } | ||
|
|
||
| function normalize(v) { | ||
| let len = Math.hypot(v[0], v[1], v[2]); | ||
| return [v[0] / len, v[1] / len, v[2] / len]; | ||
| } | ||
|
|
||
| function subtractVectors(a, b) { | ||
| return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]; | ||
| } | ||
|
|
||
| function cross(a, b) { | ||
| return [ | ||
| a[1] * b[2] - a[2] * b[1], | ||
| a[2] * b[0] - a[0] * b[2], | ||
| a[0] * b[1] - a[1] * b[0] | ||
| ]; | ||
| } | ||
|
|
||
| function dot(a, b) { | ||
| return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; | ||
| } | ||
|
|
||
| function renderCube() { | ||
| if (!this.cubeVAO) { | ||
| this.cubeVAO = this.GL.createVertexArray(); | ||
| this.cubeVBO = this.GL.createBuffer(); | ||
| this.GL.bindVertexArray(this.cubeVAO); | ||
|
|
||
| const vertices = new Float32Array([ | ||
| // positions | ||
| -1.0, -1.0, -1.0,1.0, -1.0, -1.0,1.0, 1.0, -1.0,1.0, 1.0, -1.0, | ||
| -1.0, 1.0, -1.0, | ||
| -1.0, -1.0, -1.0, | ||
|
|
||
| -1.0, -1.0, 1.0,1.0, -1.0, 1.0,1.0, 1.0, 1.0,1.0, 1.0, 1.0, | ||
| -1.0, 1.0, 1.0, | ||
| -1.0, -1.0, 1.0, | ||
|
|
||
| -1.0, 1.0, 1.0, | ||
| -1.0, 1.0, -1.0, | ||
| -1.0, -1.0, -1.0, | ||
| -1.0, -1.0, -1.0, | ||
| -1.0, -1.0, 1.0, | ||
| -1.0, 1.0, 1.0, | ||
|
|
||
| 1.0, 1.0, 1.0, | ||
| 1.0, 1.0, -1.0, | ||
| 1.0, -1.0, -1.0, | ||
| 1.0, -1.0, -1.0, | ||
| 1.0, -1.0, 1.0, | ||
| 1.0, 1.0, 1.0, | ||
|
|
||
| -1.0, -1.0, -1.0,1.0, -1.0, -1.0,1.0, -1.0, 1.0,1.0, -1.0, 1.0, | ||
| -1.0, -1.0, 1.0, | ||
| -1.0, -1.0, -1.0, | ||
|
|
||
| -1.0, 1.0, -1.0,1.0, 1.0, -1.0,1.0, 1.0, 1.0,1.0, 1.0, 1.0, | ||
| -1.0, 1.0, 1.0, | ||
| -1.0, 1.0, -1.0 | ||
| ]); | ||
|
|
||
| this.GL.bindBuffer(this.GL.ARRAY_BUFFER, this.cubeVBO); | ||
| this.GL.bufferData(this.GL.ARRAY_BUFFER, vertices, this.GL.STATIC_DRAW); | ||
| this.GL.enableVertexAttribArray(0); | ||
| this.GL.vertexAttribPointer(0, 3, this.GL.FLOAT, false, 3 * 4, 0); | ||
| this.GL.bindVertexArray(null); | ||
| } | ||
|
|
||
| this.GL.bindVertexArray(this.cubeVAO); | ||
| this.GL.drawArrays(this.GL.TRIANGLES, 0, 36); | ||
| this.GL.bindVertexArray(null); | ||
| } | ||
|
|
||
| /** | ||
| * @module Rendering | ||
| * @submodule Rendering | ||
|
|
@@ -444,6 +532,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; | ||
|
|
||
|
|
@@ -565,6 +666,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; | ||
|
|
@@ -831,6 +933,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer { | |
| this._pInst, | ||
| !isPGraphics | ||
| ); | ||
|
|
||
| this._pInst._setProperty('_renderer', renderer); | ||
| renderer.resize(w, h); | ||
| renderer._applyDefaults(); | ||
|
|
@@ -1847,6 +1950,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 | ||
|
|
@@ -1899,44 +2011,100 @@ 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); | ||
|
|
||
| // Set up view matrices for each face | ||
| const captureViews = [ | ||
| lookAt([0, 0, 0], [1, 0, 0], [0, -1, 0]), // Positive X | ||
| lookAt([0, 0, 0], [-1, 0, 0], [0, -1, 0]), // Negative X | ||
| lookAt([0, 0, 0], [0, 1, 0], [0, 0, 1]), // Positive Y | ||
| lookAt([0, 0, 0], [0, -1, 0], [0, 0, -1]), // Negative Y | ||
| lookAt([0, 0, 0], [0, 0, 1], [0, -1, 0]), // Positive Z | ||
| lookAt([0, 0, 0], [0, 0, -1], [0, -1, 0]) // Negative Z | ||
| ]; | ||
|
|
||
| // Get the cubemap shader | ||
| const cubemapShader = this._getCubemapShader(); | ||
| this._pInst.shader(cubemapShader); | ||
| cubemapShader.setUniform('equirectangularMap', 0); | ||
|
|
||
| // Define captureProjection as a 4x4 projection matrix | ||
| let captureProjection = new p5.Matrix(); | ||
| captureProjection.perspective(Math.PI / 2.0, 1.0, 0.1, 10.0); | ||
|
|
||
| cubemapShader.setUniform('projection', captureProjection.mat4); | ||
|
||
|
|
||
| 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); | ||
|
|
||
| cubemapShader.setUniform('view', captureViews[i].mat4); | ||
|
||
|
|
||
| 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 | ||
|
|
@@ -2113,7 +2281,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); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, diffusedLight is obtained from
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oooh...sorry. I overlooked this, previously we had diffusedLight with |
||
| 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] | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can't be hardcoded to 0. It must have a variable, a sampler2D texture specifically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I had this in mind that in WebGL (and OpenGL), when you set a texture uniform in a shader, you also need to specify which texture unit the texture is bound to.
If you see the next 2 lines:
Here,
this.GL.activeTexture(this.GL.TEXTURE0)activates texture unit 0, andthis.GL.bindTexture(this.GL.TEXTURE_2D, input)binds the texture input to texture unit 0.But lets just keep it simple and stick to what you have suggested. That seems to work fine.!