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

Add support for Cube Map Rotation and expose Skybox Rotation #2527

Merged
merged 20 commits into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
17 changes: 17 additions & 0 deletions examples/graphics/material-physical.html
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,23 @@
});
app.assets.add(fontAsset);
app.assets.load(fontAsset);

//rotate the skybox using mouse input
var mouse = new pc.Mouse(document.body);

var x = 0;
var y = 0;
var rot = new pc.Quat();

mouse.on('mousemove', function (event) {
if (event.buttons[pc.MOUSEBUTTON_LEFT]) {
x += event.dx;
y += event.dy;

rot.setFromEulerAngles(0.2 * y, 0.2 * x, 0);
app.scene.skyboxRotation = rot;
}
});
</script>
</body>
</html>
2 changes: 2 additions & 0 deletions src/framework/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,7 @@ Object.assign(Application.prototype, {
* @param {number|null} [settings.render.skybox] - The asset ID of the cube map texture to be used as the scene's skybox. Defaults to null.
* @param {number} settings.render.skyboxIntensity - Multiplier for skybox intensity.
* @param {number} settings.render.skyboxMip - The mip level of the skybox to be displayed. Only valid for prefiltered cubemap skyboxes.
* @param {number[]} settings.render.skyboxRotation - Rotation of skybox.
raytranuk marked this conversation as resolved.
Show resolved Hide resolved
* @param {number} settings.render.lightmapSizeMultiplier - The lightmap resolution multiplier.
* @param {number} settings.render.lightmapMaxResolution - The maximum lightmap resolution.
* @param {number} settings.render.lightmapMode - The lightmap baking mode. Can be:
Expand All @@ -1719,6 +1720,7 @@ Object.assign(Application.prototype, {
* fog_start: 1,
* global_ambient: [0, 0, 0],
* skyboxIntensity: 1,
* skyboxRotation: [0, 0, 0, 1],
* fog_color: [0, 0, 0],
* lightmapMode: 1,
* fog: 'none',
Expand Down
4 changes: 3 additions & 1 deletion src/graphics/program-lib/chunks/ambientPrefilteredCube.frag
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ uniform samplerCube texture_prefilteredCubeMap4;
#endif

void addAmbient() {
vec3 fixedReflDir = fixSeamsStatic(dNormalW, 1.0 - 1.0 / 4.0);
vec3 fixedReflDir = fixSeamsStatic(cubeMapRotate(dNormalW), 1.0 - 1.0 / 4.0);
#ifndef RIGHT_HANDED_CUBEMAP
fixedReflDir.x *= -1.0;
raytranuk marked this conversation as resolved.
Show resolved Hide resolved
#endif
dDiffuseLight += processEnvironment($DECODE(textureCube(texture_prefilteredCubeMap4, fixedReflDir)).rgb);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ uniform samplerCube texture_prefilteredCubeMap128;
#endif

void addAmbient() {
vec3 fixedReflDir = fixSeamsStatic(dNormalW, 1.0 - 1.0 / 4.0);
vec3 fixedReflDir = fixSeamsStatic(cubeMapRotate(dNormalW), 1.0 - 1.0 / 4.0);
#ifndef RIGHT_HANDED_CUBEMAP
fixedReflDir.x *= -1.0;
#endif
dDiffuseLight += processEnvironment($DECODE( textureCubeLodEXT(texture_prefilteredCubeMap128, fixedReflDir, 5.0) ).rgb);
}
2 changes: 1 addition & 1 deletion src/graphics/program-lib/chunks/ambientSH.frag
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
uniform vec3 ambientSH[9];

void addAmbient() {
vec3 n = dNormalW;
vec3 n = cubeMapRotate(dNormalW);

vec3 color =
ambientSH[0] +
Expand Down
2 changes: 2 additions & 0 deletions src/graphics/program-lib/chunks/chunks.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import combineDiffuseSpecularOldPS from './combineDiffuseSpecularOld.frag';
import cookiePS from './cookie.frag';
import cubeMapProjectBoxPS from './cubeMapProjectBox.frag';
import cubeMapProjectNonePS from './cubeMapProjectNone.frag';
import cubeMapRotatePS from './cubeMapRotate.frag';
import detailModesPS from './detailModes.frag';
import diffusePS from './diffuse.frag';
import diffuseDetailMapPS from './diffuseDetailMap.frag';
Expand Down Expand Up @@ -232,6 +233,7 @@ var shaderChunks = {
cookiePS: cookiePS,
cubeMapProjectBoxPS: cubeMapProjectBoxPS,
cubeMapProjectNonePS: cubeMapProjectNonePS,
cubeMapRotatePS: cubeMapRotatePS,
detailModesPS: detailModesPS,
diffusePS: diffusePS,
diffuseDetailMapPS: diffuseDetailMapPS,
Expand Down
2 changes: 2 additions & 0 deletions src/graphics/program-lib/chunks/cubeMapProjectBox.frag
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
uniform vec3 envBoxMin, envBoxMax;

vec3 cubeMapProject(vec3 nrdir) {
nrdir = cubeMapRotate(nrdir);

vec3 rbmax = (envBoxMax - vPositionW) / nrdir;
vec3 rbmin = (envBoxMin - vPositionW) / nrdir;

Expand Down
2 changes: 1 addition & 1 deletion src/graphics/program-lib/chunks/cubeMapProjectNone.frag
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
vec3 cubeMapProject(vec3 dir) {
return dir;
return cubeMapRotate(dir);
}
11 changes: 11 additions & 0 deletions src/graphics/program-lib/chunks/cubeMapRotate.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifdef CUBEMAP_ROTATION
uniform mat3 cubeMapRotationMatrix;
#endif

vec3 cubeMapRotate(vec3 refDir) {
#ifdef CUBEMAP_ROTATION
return refDir * cubeMapRotationMatrix;
#else
return refDir;
#endif
}
2 changes: 2 additions & 0 deletions src/graphics/program-lib/chunks/reflectionCube.frag
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ uniform float material_reflectivity;

vec3 calcReflection(vec3 tReflDirW, float tGlossiness) {
vec3 lookupVec = fixSeams(cubeMapProject(tReflDirW));
#ifndef RIGHT_HANDED_CUBEMAP
lookupVec.x *= -1.0;
#endif
return $textureCubeSAMPLE(texture_cubeMap, lookupVec).rgb;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ vec3 calcReflection(vec3 tReflDirW, float tGlossiness) {
// We fix mip0 to 128x128, so code is rather static.
// Mips smaller than 4x4 aren't great even for diffuse. Don't forget that we don't have bilinear filtering between different faces.

vec3 refl = cubeMapProject(tReflDirW) * vec3(-1.0, 1.0, 1.0);
vec3 refl = cubeMapProject(tReflDirW);
#ifndef RIGHT_HANDED_CUBEMAP
refl.x *= -1.0;
#endif
vec3 seam = calcSeam(refl);
vec4 c0 = textureCube(texture_prefilteredCubeMap128, applySeam(refl, seam, 1.0 / 128.0));
vec4 c1 = textureCube(texture_prefilteredCubeMap64, applySeam(refl, seam, 2.0 / 128.0));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ uniform float material_reflectivity;
vec3 calcReflection(vec3 tReflDirW, float tGlossiness) {
float bias = saturate(1.0 - tGlossiness) * 5.0; // multiply by max mip level
vec3 fixedReflDir = fixSeams(cubeMapProject(tReflDirW), bias);
#ifndef RIGHT_HANDED_CUBEMAP
fixedReflDir.x *= -1.0;

#endif
vec3 refl = processEnvironment($DECODE( textureCubeLodEXT(texture_prefilteredCubeMap128, fixedReflDir, bias) ).rgb);

return refl;
Expand Down
1 change: 0 additions & 1 deletion src/graphics/program-lib/chunks/skybox.vert
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@ void main(void) {

gl_Position.z = gl_Position.w - 0.00001;
vViewDir = aPosition;
vViewDir.x *= -1.0;
}
14 changes: 13 additions & 1 deletion src/graphics/program-lib/chunks/skyboxHDR.frag
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,20 @@ varying vec3 vViewDir;

uniform samplerCube texture_cubeMap;

#ifdef CUBEMAP_ROTATION
uniform mat3 cubeMapRotationMatrix;
#endif

void main(void) {
vec3 color = processEnvironment($textureCubeSAMPLE(texture_cubeMap, fixSeamsStatic(vViewDir, $FIXCONST)).rgb);
#ifdef CUBEMAP_ROTATION
vec3 dir=vViewDir * cubeMapRotationMatrix;
#else
vec3 dir=vViewDir;
#endif
#ifndef RIGHT_HANDED_CUBEMAP
dir.x *= -1.0;
#endif
vec3 color = processEnvironment($textureCubeSAMPLE(texture_cubeMap, fixSeamsStatic(dir, $FIXCONST)).rgb);
color = toneMap(color);
color = gammaCorrectOutput(color);
gl_FragColor = vec4(color, 1.0);
Expand Down
5 changes: 4 additions & 1 deletion src/graphics/program-lib/programs/skybox.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { gammaCode, precisionCode, tonemapCode } from './common.js';
var skybox = {
generateKey: function (options) {
var key = "skybox" + options.rgbm + " " + options.hdr + " " + options.fixSeams + "" +
options.toneMapping + "" + options.gamma + "" + options.useIntensity + "" + options.mip;
options.toneMapping + "" + options.gamma + "" + options.useIntensity + "" +
options.useCubeMapRotation + "" + options.useRightHandedCubeMap + "" + options.mip;
return key;
},

Expand All @@ -15,6 +16,8 @@ var skybox = {

var fshader;
fshader = precisionCode(device);
fshader += options.useCubeMapRotation ? '#define CUBEMAP_ROTATION\n' : '';
fshader += options.useRightHandedCubeMap ? '#define RIGHT_HANDED_CUBEMAP\n' : '';
fshader += options.mip ? shaderChunks.fixCubemapSeamsStretchPS : shaderChunks.fixCubemapSeamsNonePS;
fshader += options.useIntensity ? shaderChunks.envMultiplyPS : shaderChunks.envConstPS;
fshader += gammaCode(options.gamma);
Expand Down
10 changes: 10 additions & 0 deletions src/graphics/program-lib/programs/standard.js
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,17 @@ var standard = {
code += options.fixSeams ? chunks.fixCubemapSeamsStretchPS : chunks.fixCubemapSeamsNonePS;
}

if (options.useCubeMapRotation) {
code += "#define CUBEMAP_ROTATION\n";
}

if (options.useRightHandedCubeMap) {
code += "#define RIGHT_HANDED_CUBEMAP\n";
}

if (needsNormal) {
code += chunks.cubeMapRotatePS;

code += options.cubeMapProjection > 0 ? chunks.cubeMapProjectBoxPS : chunks.cubeMapProjectNonePS;
code += options.skyboxIntensity ? chunks.envMultiplyPS : chunks.envConstPS;
}
Expand Down
2 changes: 2 additions & 0 deletions src/graphics/texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ function Texture(graphicsDevice, options) {
this._flipY = true;
this._premultiplyAlpha = false;

this._isRenderTarget = false;

this._mipmaps = true;

this._minFilter = FILTER_LINEAR_MIPMAP_LINEAR;
Expand Down
4 changes: 4 additions & 0 deletions src/scene/materials/standard-material-options-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
TONEMAP_LINEAR
} from '../constants.js';

import { Quat } from '../../math/quat.js';

function StandardMaterialOptionsBuilder() {
this._mapXForms = null;
}
Expand Down Expand Up @@ -185,6 +187,8 @@ StandardMaterialOptionsBuilder.prototype._updateEnvOptions = function (options,
options.fixSeams = prefilteredCubeMap128 ? prefilteredCubeMap128.fixCubemapSeams : (stdMat.cubeMap ? stdMat.cubeMap.fixCubemapSeams : false);
options.prefilteredCubemap = !!prefilteredCubeMap128;
options.skyboxIntensity = (prefilteredCubeMap128 && globalSky128 && prefilteredCubeMap128 === globalSky128) && (scene.skyboxIntensity !== 1);
options.useCubeMapRotation = (stdMat.useSkybox && scene && scene.skyboxRotation && !scene.skyboxRotation.equals(Quat.IDENTITY));
options.useRightHandedCubeMap = stdMat.cubeMap ? stdMat.cubeMap._isRenderTarget : (stdMat.useSkybox && scene && scene._skyboxIsRenderTarget );
};

StandardMaterialOptionsBuilder.prototype._updateLightOptions = function (options, stdMat, objDefs, sortedLights, staticLightList) {
Expand Down
7 changes: 7 additions & 0 deletions src/scene/materials/standard-material.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { StandardMaterialOptionsBuilder } from './standard-material-options-buil
import { Application } from '../../framework/application.js';

import { standardMaterialCubemapParameters, standardMaterialTextureParameters } from './standard-material-parameters.js';
import { Quat } from '../../math/quat.js';

/**
* @class
Expand Down Expand Up @@ -292,6 +293,8 @@ import { standardMaterialCubemapParameters, standardMaterialTextureParameters }
* * fastTbn: Use slightly cheaper normal mapping code (skip tangent space normalization). Can look buggy sometimes.
* * refraction: if refraction is used.
* * skyboxIntensity: if reflected skybox intensity should be modulated.
* * useCubeMapRotation: if cube map rotation is enabled.
* * useRightHandedCubeMap: if the cube map uses a right-handed coordinate system. The convention for pre-generated cubemaps is left-handed.
* * useTexCubeLod: if textureCubeLodEXT function should be used to read prefiltered cubemaps. Usually true of iOS, false on other devices due to quality/performance balance.
* * useInstancing: if hardware instancing compatible shader should be generated. Transform is read from per-instance {@link pc.VertexBuffer} instead of shader's uniforms.
* * useMorphPosition: if morphing code should be generated to morph positions.
Expand Down Expand Up @@ -1007,6 +1010,10 @@ Object.assign(StandardMaterial.prototype, {
} else {
console.log("Can't use prefiltered cubemap: " + allMips + ", " + useTexCubeLod + ", " + prefilteredCubeMap128._levels);
}

if (this.useSkybox && !scene.skyboxRotation.equals(Quat.IDENTITY) && scene._skyboxRotationMat3) {
this._setParameter('cubeMapRotationMatrix', scene._skyboxRotationMat3.data);
}
}

// Minimal options for Depth and Shadow passes
Expand Down
44 changes: 44 additions & 0 deletions src/scene/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Color } from '../core/color.js';
import { EventHandler } from '../core/event-handler.js';

import { Vec3 } from '../math/vec3.js';
import { Mat3 } from '../math/mat3.js';

import { CULLFACE_FRONT, PIXELFORMAT_RGBA32F, TEXTURETYPE_RGBM } from '../graphics/graphics.js';

Expand All @@ -12,6 +13,8 @@ import { Material } from './materials/material.js';
import { MeshInstance } from './mesh-instance.js';
import { Model } from './model.js';
import { StandardMaterial } from './materials/standard-material.js';
import { Quat } from '../math/quat.js';
import { Mat4 } from '../math/mat4.js';

/**
* @class
Expand Down Expand Up @@ -65,6 +68,7 @@ import { StandardMaterial } from './materials/standard-material.js';
* @property {pc.Texture} skyboxPrefiltered8 The prefiltered cubemap texture (size 8x8) used as the scene's skybox, if mip level 5. Defaults to null.
* @property {pc.Texture} skyboxPrefiltered4 The prefiltered cubemap texture (size 4x4) used as the scene's skybox, if mip level 6. Defaults to null.
* @property {number} skyboxIntensity Multiplier for skybox intensity. Defaults to 1.
* @property {pc.Quat} skyboxRotation The rotation of the skybox to be displayed. Defaults to {@link pc.Quat.IDENTITY}.
* @property {number} skyboxMip The mip level of the skybox to be displayed. Only valid
* for prefiltered cubemap skyboxes. Defaults to 0 (base level).
* @property {number} lightmapSizeMultiplier The lightmap resolution multiplier.
Expand Down Expand Up @@ -116,6 +120,12 @@ function Scene() {
this._skyboxIntensity = 1;
this._skyboxMip = 0;

this._skyboxRotation = new Quat();
this._skyboxRotationMat3 = null;
this._skyboxRotationMat4 = null;

this._skyboxIsRenderTarget = false;

this.lightmapSizeMultiplier = 1;
this.lightmapMaxResolution = 2048;
this.lightmapMode = BAKE_COLORDIR;
Expand Down Expand Up @@ -215,6 +225,19 @@ Object.defineProperty(Scene.prototype, 'skyboxIntensity', {
}
});

Object.defineProperty(Scene.prototype, 'skyboxRotation', {
get: function () {
return this._skyboxRotation;
},
set: function (value) {
if (!this._skyboxRotation.equals(value)) {
this._skyboxRotation.copy(value);
this._resetSkyboxModel();
this.updateShaders = true;
}
}
});

Object.defineProperty(Scene.prototype, 'skyboxMip', {
get: function () {
return this._skyboxMip;
Expand Down Expand Up @@ -349,6 +372,10 @@ Scene.prototype.applySettings = function (settings) {
this._skyboxIntensity = settings.render.skyboxIntensity === undefined ? 1 : settings.render.skyboxIntensity;
this._skyboxMip = settings.render.skyboxMip === undefined ? 0 : settings.render.skyboxMip;

if (settings.render.skyboxRotation !== undefined) {
this._skyboxRotation.set(settings.render.skyboxRotation);
}

this._resetSkyboxModel();
this.updateShaders = true;
};
Expand All @@ -372,6 +399,12 @@ Scene.prototype._updateSkybox = function (device) {
return;
}

if (usedTex._isRenderTarget) {
this._skyboxIsRenderTarget = true;
} else {
this._skyboxIsRenderTarget = false;
}

var material = new Material();
var scene = this;
material.updateShader = function (dev, sc, defs, staticLightList, pass) {
Expand All @@ -380,6 +413,8 @@ Scene.prototype._updateSkybox = function (device) {
rgbm: usedTex.type === TEXTURETYPE_RGBM,
hdr: (usedTex.type === TEXTURETYPE_RGBM || usedTex.format === PIXELFORMAT_RGBA32F),
useIntensity: scene.skyboxIntensity !== 1,
useCubeMapRotation: !scene.skyboxRotation.equals(Quat.IDENTITY),
useRightHandedCubeMap: scene._skyboxIsRenderTarget,
mip: usedTex.fixCubemapSeams ? scene.skyboxMip : 0,
fixSeams: usedTex.fixCubemapSeams,
gamma: (pass === SHADER_FORWARDHDR ? (scene.gammaCorrection ? GAMMA_SRGBHDR : GAMMA_NONE) : scene.gammaCorrection),
Expand All @@ -390,6 +425,15 @@ Scene.prototype._updateSkybox = function (device) {

material.updateShader();
material.setParameter("texture_cubeMap", usedTex);

if (!this.skyboxRotation.equals(Quat.IDENTITY)) {
if (!this._skyboxRotationMat4) this._skyboxRotationMat4 = new Mat4();
if (!this._skyboxRotationMat3) this._skyboxRotationMat3 = new Mat3();
this._skyboxRotationMat4.setTRS(pc.Vec3.ZERO, this._skyboxRotation, pc.Vec3.ONE);
this._skyboxRotationMat4.invertTo3x3(this._skyboxRotationMat3);
material.setParameter("cubeMapRotationMatrix", this._skyboxRotationMat3.data);
}

material.cull = CULLFACE_FRONT;
material.depthWrite = false;

Expand Down