Skip to content

Commit d7b5d58

Browse files
authored
Merge pull request #6808 from perminder-17/sphere-Mapping
Added a Method (`panorama(img)`) which adds a sphereMapped Background.
2 parents 4c13650 + a063911 commit d7b5d58

File tree

3 files changed

+107
-14
lines changed

3 files changed

+107
-14
lines changed

src/webgl/light.js

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ import p5 from '../core/main';
115115
* @param {p5.Color} color color as a <a href="#/p5.Color">p5.Color</a>
116116
* @chainable
117117
*/
118-
p5.prototype.ambientLight = function(v1, v2, v3, a) {
118+
p5.prototype.ambientLight = function (v1, v2, v3, a) {
119119
this._assert3d('ambientLight');
120120
p5._validateParameters('ambientLight', arguments);
121121
const color = this.color(...arguments);
@@ -229,7 +229,7 @@ p5.prototype.ambientLight = function(v1, v2, v3, a) {
229229
* @param {p5.Color} color color as a <a href="#/p5.Color">p5.Color</a>
230230
* @chainable
231231
*/
232-
p5.prototype.specularColor = function(v1, v2, v3) {
232+
p5.prototype.specularColor = function (v1, v2, v3) {
233233
this._assert3d('specularColor');
234234
p5._validateParameters('specularColor', arguments);
235235
const color = this.color(...arguments);
@@ -327,7 +327,7 @@ p5.prototype.specularColor = function(v1, v2, v3) {
327327
* @param {p5.Vector} direction
328328
* @chainable
329329
*/
330-
p5.prototype.directionalLight = function(v1, v2, v3, x, y, z) {
330+
p5.prototype.directionalLight = function (v1, v2, v3, x, y, z) {
331331
this._assert3d('directionalLight');
332332
p5._validateParameters('directionalLight', arguments);
333333

@@ -454,7 +454,7 @@ p5.prototype.directionalLight = function(v1, v2, v3, x, y, z) {
454454
* @param {p5.Vector} position
455455
* @chainable
456456
*/
457-
p5.prototype.pointLight = function(v1, v2, v3, x, y, z) {
457+
p5.prototype.pointLight = function (v1, v2, v3, x, y, z) {
458458
this._assert3d('pointLight');
459459
p5._validateParameters('pointLight', arguments);
460460

@@ -592,13 +592,60 @@ p5.prototype.pointLight = function(v1, v2, v3, x, y, z) {
592592
* @alt
593593
* light with slider having a slider for varying roughness
594594
*/
595-
p5.prototype.imageLight = function(img){
595+
p5.prototype.imageLight = function (img) {
596596
// activeImageLight property is checked by _setFillUniforms
597597
// for sending uniforms to the fillshader
598598
this._renderer.activeImageLight = img;
599599
this._renderer._enableLighting = true;
600600
};
601601

602+
/**
603+
* The `panorama(img)` method transforms images containing
604+
* 360-degree content, such as maps or HDRIs, into immersive
605+
* 3D backgrounds that surround your scene. This is similar to calling
606+
* `background(color)`; call `panorama(img)` before drawing your
607+
* scene to create a 360-degree background from your image. It
608+
* operates on the concept of sphere mapping, where the image is
609+
* mapped onto an infinitely large sphere based on the angles of the
610+
* camera.
611+
*
612+
* To enable 360-degree viewing, either use `orbitControl()` or try changing
613+
* the orientation of the camera to see different parts of the background.
614+
*
615+
* @method panorama
616+
* @param {p5.Image} img A 360-degree image to use as a background panorama
617+
* @example
618+
* <div class="notest">
619+
* <code>
620+
* let img;
621+
* function preload() {
622+
* img = loadImage('assets/outdoor_spheremap.jpg');
623+
* }
624+
* function setup() {
625+
* createCanvas(100 ,100 ,WEBGL);
626+
* }
627+
* function draw() {
628+
* panorama(img);
629+
* imageMode(CENTER);
630+
* orbitControl();
631+
* noStroke();
632+
* push();
633+
* imageLight(img);
634+
* specularMaterial('green');
635+
* shininess(200);
636+
* metalness(100);
637+
* sphere(25);
638+
* pop();
639+
* }
640+
* </code>
641+
* </div>
642+
* @alt
643+
* The image transformed into a panoramic scene.
644+
*/
645+
p5.prototype.panorama = function (img) {
646+
this.filter(this._renderer._getSphereMapping(img));
647+
};
648+
602649
/**
603650
* Places an ambient and directional light in the scene.
604651
* The lights are set to ambientLight(128, 128, 128) and
@@ -634,7 +681,7 @@ p5.prototype.imageLight = function(img){
634681
* @alt
635682
* the light is partially ambient and partially directional
636683
*/
637-
p5.prototype.lights = function() {
684+
p5.prototype.lights = function () {
638685
this._assert3d('lights');
639686
// Both specify gray by default.
640687
const grayColor = this.color('rgb(128,128,128)');
@@ -698,7 +745,7 @@ p5.prototype.lights = function() {
698745
* @alt
699746
* Two spheres with different falloff values show different intensity of light
700747
*/
701-
p5.prototype.lightFalloff = function(
748+
p5.prototype.lightFalloff = function (
702749
constantAttenuation,
703750
linearAttenuation,
704751
quadraticAttenuation
@@ -892,7 +939,7 @@ p5.prototype.lightFalloff = function(
892939
* @param {Number} [angle]
893940
* @param {Number} [concentration]
894941
*/
895-
p5.prototype.spotLight = function(
942+
p5.prototype.spotLight = function (
896943
v1,
897944
v2,
898945
v3,
@@ -1153,7 +1200,7 @@ p5.prototype.spotLight = function(
11531200
* Three white spheres. Each appears as a different
11541201
* color due to lighting.
11551202
*/
1156-
p5.prototype.noLights = function(...args) {
1203+
p5.prototype.noLights = function (...args) {
11571204
this._assert3d('noLights');
11581205
p5._validateParameters('noLights', args);
11591206

src/webgl/p5.RendererGL.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ const webgl2CompatibilityShader = readFileSync(
4242
);
4343

4444
const defaultShaders = {
45+
sphereMappingFrag: readFileSync(
46+
join(__dirname, '/shaders/sphereMapping.frag'),
47+
'utf-8'
48+
),
4549
immediateVert: readFileSync(
4650
join(__dirname, '/shaders/immediate.vert'),
4751
'utf-8'
@@ -80,6 +84,7 @@ const defaultShaders = {
8084
imageLightDiffusedFrag: readFileSync(join(__dirname, '/shaders/imageLightDiffused.frag'), 'utf-8'),
8185
imageLightSpecularFrag: readFileSync(join(__dirname, '/shaders/imageLightSpecular.frag'), 'utf-8')
8286
};
87+
let sphereMapping = defaultShaders.sphereMappingFrag;
8388
for (const key in defaultShaders) {
8489
defaultShaders[key] = webgl2CompatibilityShader + defaultShaders[key];
8590
}
@@ -497,7 +502,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
497502
this.curStrokeColor = this._cachedStrokeStyle = [0, 0, 0, 1];
498503

499504
this.curBlendMode = constants.BLEND;
500-
this.preEraseBlend=undefined;
505+
this.preEraseBlend = undefined;
501506
this._cachedBlendMode = undefined;
502507
if (this.webglVersion === constants.WEBGL2) {
503508
this.blendExt = this.GL;
@@ -559,6 +564,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
559564
this.executeRotateAndMove = false;
560565

561566
this.specularShader = undefined;
567+
this.sphereMapping = undefined;
562568
this.diffusedShader = undefined;
563569
this._defaultLightShader = undefined;
564570
this._defaultImmediateModeShader = undefined;
@@ -1127,6 +1133,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
11271133
this._pInst.resetMatrix();
11281134
this._pInst.image(fbo, -target.width / 2, -target.height / 2,
11291135
target.width, target.height);
1136+
this._pInst.clearDepth();
11301137
this._pInst.pop();
11311138
this._pInst.pop();
11321139
}
@@ -1186,7 +1193,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
11861193
this.curFillColor = this._cachedFillStyle.slice();
11871194
this.curStrokeColor = this._cachedStrokeStyle.slice();
11881195
// Restore blend mode
1189-
this.curBlendMode=this.preEraseBlend;
1196+
this.curBlendMode = this.preEraseBlend;
11901197
this.blendMode(this.preEraseBlend);
11911198
// Ensure that _applyBlendMode() sets preEraseBlend back to the original blend mode
11921199
this._isErasing = false;
@@ -1676,6 +1683,21 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
16761683
return this._getImmediateStrokeShader();
16771684
}
16781685

1686+
_getSphereMapping(img) {
1687+
if (!this.sphereMapping) {
1688+
this.sphereMapping = this._pInst.createFilterShader(
1689+
sphereMapping
1690+
);
1691+
}
1692+
this.uNMatrix.inverseTranspose(this.uMVMatrix);
1693+
this.uNMatrix.invert3x3(this.uNMatrix);
1694+
this.sphereMapping.setUniform('uFovY', this._curCamera.cameraFOV);
1695+
this.sphereMapping.setUniform('uAspect', this._curCamera.aspectRatio);
1696+
this.sphereMapping.setUniform('uNewNormalMatrix', this.uNMatrix.mat3);
1697+
this.sphereMapping.setUniform('uSampler', img);
1698+
return this.sphereMapping;
1699+
}
1700+
16791701
/*
16801702
* selects which fill shader should be used based on renderer state,
16811703
* for use with begin/endShape and immediate vertex mode.
@@ -2141,9 +2163,9 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
21412163
}
21422164

21432165
/* Binds a buffer to the drawing context
2144-
* when passed more than two arguments it also updates or initializes
2145-
* the data associated with the buffer
2146-
*/
2166+
* when passed more than two arguments it also updates or initializes
2167+
* the data associated with the buffer
2168+
*/
21472169
_bindBuffer(
21482170
buffer,
21492171
target,

src/webgl/shaders/sphereMapping.frag

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#define PI 3.141592
2+
3+
precision highp float;
4+
5+
uniform sampler2D uSampler;
6+
uniform mat3 uNewNormalMatrix;
7+
uniform float uFovY;
8+
uniform float uAspect;
9+
10+
varying vec2 vTexCoord;
11+
12+
void main() {
13+
float uFovX = uFovY * uAspect;
14+
vec4 newTexColor = texture2D(uSampler, vTexCoord);
15+
float angleY = mix(uFovY/2.0, -uFovY/2.0, vTexCoord.y);
16+
float angleX = mix(uFovX/2.0, -uFovX/2.0, vTexCoord.x);
17+
vec3 rotatedNormal = vec3( angleX, angleY, 1.0 );
18+
rotatedNormal = uNewNormalMatrix * normalize(rotatedNormal);
19+
vec2 suv;
20+
suv.y = 0.5 + 0.5 * (-rotatedNormal.y);
21+
suv.x = atan(rotatedNormal.z, rotatedNormal.x) / (2.0 * PI) + 0.5;
22+
newTexColor = texture2D(uSampler, suv.xy);
23+
gl_FragColor = newTexColor;
24+
}

0 commit comments

Comments
 (0)