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

Added a Method (panorama(img)) which adds a sphereMapped Background. #6808

Merged
merged 17 commits into from
Feb 27, 2024
65 changes: 56 additions & 9 deletions src/webgl/light.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ import p5 from '../core/main';
* @param {p5.Color} color color as a <a href="#/p5.Color">p5.Color</a>
* @chainable
*/
p5.prototype.ambientLight = function(v1, v2, v3, a) {
p5.prototype.ambientLight = function (v1, v2, v3, a) {
this._assert3d('ambientLight');
p5._validateParameters('ambientLight', arguments);
const color = this.color(...arguments);
Expand Down Expand Up @@ -229,7 +229,7 @@ p5.prototype.ambientLight = function(v1, v2, v3, a) {
* @param {p5.Color} color color as a <a href="#/p5.Color">p5.Color</a>
* @chainable
*/
p5.prototype.specularColor = function(v1, v2, v3) {
p5.prototype.specularColor = function (v1, v2, v3) {
this._assert3d('specularColor');
p5._validateParameters('specularColor', arguments);
const color = this.color(...arguments);
Expand Down Expand Up @@ -327,7 +327,7 @@ p5.prototype.specularColor = function(v1, v2, v3) {
* @param {p5.Vector} direction
* @chainable
*/
p5.prototype.directionalLight = function(v1, v2, v3, x, y, z) {
p5.prototype.directionalLight = function (v1, v2, v3, x, y, z) {
this._assert3d('directionalLight');
p5._validateParameters('directionalLight', arguments);

Expand Down Expand Up @@ -454,7 +454,7 @@ p5.prototype.directionalLight = function(v1, v2, v3, x, y, z) {
* @param {p5.Vector} position
* @chainable
*/
p5.prototype.pointLight = function(v1, v2, v3, x, y, z) {
p5.prototype.pointLight = function (v1, v2, v3, x, y, z) {
this._assert3d('pointLight');
p5._validateParameters('pointLight', arguments);

Expand Down Expand Up @@ -592,13 +592,60 @@ p5.prototype.pointLight = function(v1, v2, v3, x, y, z) {
* @alt
* light with slider having a slider for varying roughness
*/
p5.prototype.imageLight = function(img){
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;
};

/**
* The `panorama(img)` method transforms images containing
* 360-degree content, such as maps or HDRIs, into immersive
* 3D backgrounds that surround your scene. This is similar to calling
* `background(color)`; call `panorama(img)` before drawing your
* scene to create a 360-degree background from your image. It
* operates on the concept of sphere mapping, where the image is
* mapped onto an infinitely large sphere based on the angles of the
* camera.
*
* To enable 360-degree viewing, either use `orbitControl()` or try changing
* the orientation of the camera to see different parts of the background.
*
* @method panorama
* @param {p5.Image} img A 360-degree image to use as a background panorama
* @example
* <div class="notest">
* <code>
* let img;
* function preload() {
* img = loadImage('assets/outdoor_spheremap.jpg');
* }
* function setup() {
* createCanvas(100 ,100 ,WEBGL);
* }
* function draw() {
* panorama(img);
* imageMode(CENTER);
* orbitControl();
* noStroke();
* push();
* imageLight(img);
* specularMaterial('green');
* shininess(200);
* metalness(100);
* sphere(25);
* pop();
* }
* </code>
* </div>
* @alt
* The image transformed into a panoramic scene.
*/
p5.prototype.panorama = function (img) {
this.filter(this._renderer._getSphereMapping(img));
};

/**
* Places an ambient and directional light in the scene.
* The lights are set to ambientLight(128, 128, 128) and
Expand Down Expand Up @@ -634,7 +681,7 @@ p5.prototype.imageLight = function(img){
* @alt
* the light is partially ambient and partially directional
*/
p5.prototype.lights = function() {
p5.prototype.lights = function () {
this._assert3d('lights');
// Both specify gray by default.
const grayColor = this.color('rgb(128,128,128)');
Expand Down Expand Up @@ -698,7 +745,7 @@ p5.prototype.lights = function() {
* @alt
* Two spheres with different falloff values show different intensity of light
*/
p5.prototype.lightFalloff = function(
p5.prototype.lightFalloff = function (
constantAttenuation,
linearAttenuation,
quadraticAttenuation
Expand Down Expand Up @@ -892,7 +939,7 @@ p5.prototype.lightFalloff = function(
* @param {Number} [angle]
* @param {Number} [concentration]
*/
p5.prototype.spotLight = function(
p5.prototype.spotLight = function (
v1,
v2,
v3,
Expand Down Expand Up @@ -1153,7 +1200,7 @@ p5.prototype.spotLight = function(
* Three white spheres. Each appears as a different
* color due to lighting.
*/
p5.prototype.noLights = function(...args) {
p5.prototype.noLights = function (...args) {
this._assert3d('noLights');
p5._validateParameters('noLights', args);

Expand Down
32 changes: 27 additions & 5 deletions src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ const webgl2CompatibilityShader = readFileSync(
);

const defaultShaders = {
sphereMappingFrag: readFileSync(
join(__dirname, '/shaders/sphereMapping.frag'),
'utf-8'
),
immediateVert: readFileSync(
join(__dirname, '/shaders/immediate.vert'),
'utf-8'
Expand Down Expand Up @@ -80,6 +84,7 @@ const defaultShaders = {
imageLightDiffusedFrag: readFileSync(join(__dirname, '/shaders/imageLightDiffused.frag'), 'utf-8'),
imageLightSpecularFrag: readFileSync(join(__dirname, '/shaders/imageLightSpecular.frag'), 'utf-8')
};
let sphereMapping = defaultShaders.sphereMappingFrag;
for (const key in defaultShaders) {
defaultShaders[key] = webgl2CompatibilityShader + defaultShaders[key];
}
Expand Down Expand Up @@ -497,7 +502,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this.curStrokeColor = this._cachedStrokeStyle = [0, 0, 0, 1];

this.curBlendMode = constants.BLEND;
this.preEraseBlend=undefined;
this.preEraseBlend = undefined;
this._cachedBlendMode = undefined;
if (this.webglVersion === constants.WEBGL2) {
this.blendExt = this.GL;
Expand Down Expand Up @@ -559,6 +564,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this.executeRotateAndMove = false;

this.specularShader = undefined;
this.sphereMapping = undefined;
this.diffusedShader = undefined;
this._defaultLightShader = undefined;
this._defaultImmediateModeShader = undefined;
Expand Down Expand Up @@ -1127,6 +1133,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this._pInst.resetMatrix();
this._pInst.image(fbo, -target.width / 2, -target.height / 2,
target.width, target.height);
this._pInst.clearDepth();
this._pInst.pop();
this._pInst.pop();
}
Expand Down Expand Up @@ -1186,7 +1193,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this.curFillColor = this._cachedFillStyle.slice();
this.curStrokeColor = this._cachedStrokeStyle.slice();
// Restore blend mode
this.curBlendMode=this.preEraseBlend;
this.curBlendMode = this.preEraseBlend;
this.blendMode(this.preEraseBlend);
// Ensure that _applyBlendMode() sets preEraseBlend back to the original blend mode
this._isErasing = false;
Expand Down Expand Up @@ -1676,6 +1683,21 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
return this._getImmediateStrokeShader();
}

_getSphereMapping(img) {
if (!this.sphereMapping) {
this.sphereMapping = this._pInst.createFilterShader(
sphereMapping
);
}
this.uNMatrix.inverseTranspose(this.uMVMatrix);
this.uNMatrix.invert3x3(this.uNMatrix);
this.sphereMapping.setUniform('uFovY', this._curCamera.cameraFOV);
this.sphereMapping.setUniform('uAspect', this._curCamera.aspectRatio);
this.sphereMapping.setUniform('uNewNormalMatrix', this.uNMatrix.mat3);
this.sphereMapping.setUniform('uSampler', img);
return this.sphereMapping;
}

/*
* selects which fill shader should be used based on renderer state,
* for use with begin/endShape and immediate vertex mode.
Expand Down Expand Up @@ -2141,9 +2163,9 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
}

/* Binds a buffer to the drawing context
* when passed more than two arguments it also updates or initializes
* the data associated with the buffer
*/
* when passed more than two arguments it also updates or initializes
* the data associated with the buffer
*/
_bindBuffer(
buffer,
target,
Expand Down
24 changes: 24 additions & 0 deletions src/webgl/shaders/sphereMapping.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#define PI 3.141592

precision highp float;

uniform sampler2D uSampler;
uniform mat3 uNewNormalMatrix;
uniform float uFovY;
uniform float uAspect;

varying vec2 vTexCoord;

void main() {
float uFovX = uFovY * uAspect;
vec4 newTexColor = texture2D(uSampler, vTexCoord);
float angleY = mix(uFovY/2.0, -uFovY/2.0, vTexCoord.y);
float angleX = mix(uFovX/2.0, -uFovX/2.0, vTexCoord.x);
vec3 rotatedNormal = vec3( angleX, angleY, 1.0 );
rotatedNormal = uNewNormalMatrix * normalize(rotatedNormal);
vec2 suv;
suv.y = 0.5 + 0.5 * (-rotatedNormal.y);
suv.x = atan(rotatedNormal.z, rotatedNormal.x) / (2.0 * PI) + 0.5;
newTexColor = texture2D(uSampler, suv.xy);
gl_FragColor = newTexColor;
}
Loading