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 8 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
138 changes: 138 additions & 0 deletions examples/graphics/material-physical-cubemap-rotation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<!DOCTYPE html>
<html>
<head>
<title>PlayCanvas Physical Material</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel="icon" type="image/png" href="../playcanvas-favicon.png" />
<script src="../../build/playcanvas.js"></script>
<script src="../../build/playcanvas-extras.js"></script>
<style>
body {
margin: 0;
overflow-y: hidden;
}
</style>
</head>

<body>
<!-- The canvas element -->
<canvas id="application-canvas"></canvas>

<!-- The script -->
<script>
var canvas = document.getElementById("application-canvas");

// Create the application and start the update loop
var app = new pc.Application(canvas);
app.start();

// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);

window.addEventListener("resize", function () {
app.resizeCanvas(canvas.width, canvas.height);
});

var miniStats = new pcx.MiniStats(app);

app.scene.gammaCorrection = pc.GAMMA_SRGB;
app.scene.toneMapping = pc.TONEMAP_ACES;
// Set the skybox to the 128x128 cubemap mipmap level
app.scene.skyboxMip = 0;

// Create an entity with a camera component
var camera = new pc.Entity();
camera.addComponent("camera");
camera.translate(0, 0, 9);
app.root.addChild(camera);

// Load a cubemap asset. This DDS file was 'prefiltered' in the
// PlayCanvas Editor and then downloaded.
var cubemapAsset = new pc.Asset('helipad.dds', 'cubemap', {
url: "../assets/cubemaps/helipad.dds"
}, {
type: pc.TEXTURETYPE_RGBM
});
app.assets.add(cubemapAsset);
app.assets.load(cubemapAsset);
cubemapAsset.ready(function () {
app.scene.setSkybox(cubemapAsset.resources);
});

var NUM_SPHERES = 5;

var entity;

var createSphere = function (x, y, z) {
var material = new pc.StandardMaterial();
material.metalness = (x===2 && y===2) ? 1.0 : y / (NUM_SPHERES - 1);
material.shininess = (x===2 && y===2) ? 100 : x / (NUM_SPHERES - 1) * 100;
material.useMetalness = true;
material.update();

var sphere = new pc.Entity();
sphere.addComponent("model", {
material: material,
type: (x===2 && y===2) ? "box" : "sphere"
});
sphere.setLocalPosition(x - (NUM_SPHERES - 1) * 0.5, y - (NUM_SPHERES - 1) * 0.5, z);
sphere.setLocalScale(0.9, 0.9, 0.9);
app.root.addChild(sphere);
entity = (x===2 && y===2) ? sphere : entity;
};

var createText = function (fontAsset, message, x, y, z, rot) {
// Create a text element-based entity
var text = new pc.Entity();
text.addComponent("element", {
anchor: [0.5, 0.5, 0.5, 0.5],
fontAsset: fontAsset,
fontSize: 0.5,
pivot: [0.5, 0.5],
text: message,
type: pc.ELEMENTTYPE_TEXT
});
text.setLocalPosition(x, y, z);
text.setLocalEulerAngles(0, 0, rot);
app.root.addChild(text);
};

for (var i = 0; i < NUM_SPHERES; i++) {
for (var j = 0; j < NUM_SPHERES; j++) {
createSphere(j, i, 0);
}
}

// Load a font
var fontAsset = new pc.Asset('arial.json', "font", { url: "../assets/fonts/arial.json" });
fontAsset.on('load', function () {
createText(fontAsset, 'Glossiness', 0, -(NUM_SPHERES + 1) * 0.5, 0, 0);
createText(fontAsset, 'Metalness', -(NUM_SPHERES + 1) * 0.5, 0, 0, 90);
});
app.assets.add(fontAsset);
app.assets.load(fontAsset);

var mouse = new pc.Mouse(document.body);

var x = 0;
var y = 0;
var rot = new pc.Quat();
var rotMat4 = new pc.Mat4();
var rotMat3 = new pc.Mat3();

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

// entity.setLocalEulerAngles(0.2 * y, 0.2 * x, 0);

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 {pc.Mat3} settings.render.skyboxRotationMatrix - Rotation of skybox.
* @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
3 changes: 1 addition & 2 deletions src/graphics/program-lib/chunks/ambientPrefilteredCube.frag
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ uniform samplerCube texture_prefilteredCubeMap4;
#endif

void addAmbient() {
vec3 fixedReflDir = fixSeamsStatic(dNormalW, 1.0 - 1.0 / 4.0);
fixedReflDir.x *= -1.0;
vec3 fixedReflDir = fixSeamsStatic(cubeMapRotate0(dNormalW), 1.0 - 1.0 / 4.0);
dDiffuseLight += processEnvironment($DECODE(textureCube(texture_prefilteredCubeMap4, fixedReflDir)).rgb);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ uniform samplerCube texture_prefilteredCubeMap128;
#endif

void addAmbient() {
vec3 fixedReflDir = fixSeamsStatic(dNormalW, 1.0 - 1.0 / 4.0);
fixedReflDir.x *= -1.0;
vec3 fixedReflDir = fixSeamsStatic(cubeMapRotate0(dNormalW), 1.0 - 1.0 / 4.0);

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 = cubeMapRotate1(dNormalW);

vec3 color =
ambientSH[0] +
Expand Down
4 changes: 4 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 @@ -163,6 +164,7 @@ import skinTexVS from './skinTex.vert';
import skyboxPS from './skybox.frag';
import skyboxVS from './skybox.vert';
import skyboxHDRPS from './skyboxHDR.frag';
import skyboxHDRRotPS from './skyboxHDRRot.frag';
import skyboxPrefilteredCubePS from './skyboxPrefilteredCube.frag';
import specularPS from './specular.frag';
import specularAaNonePS from './specularAaNone.frag';
Expand Down Expand Up @@ -232,6 +234,7 @@ var shaderChunks = {
cookiePS: cookiePS,
cubeMapProjectBoxPS: cubeMapProjectBoxPS,
cubeMapProjectNonePS: cubeMapProjectNonePS,
cubeMapRotatePS: cubeMapRotatePS,
detailModesPS: detailModesPS,
diffusePS: diffusePS,
diffuseDetailMapPS: diffuseDetailMapPS,
Expand Down Expand Up @@ -365,6 +368,7 @@ var shaderChunks = {
skyboxPS: skyboxPS,
skyboxVS: skyboxVS,
skyboxHDRPS: skyboxHDRPS,
skyboxHDRRotPS: skyboxHDRRotPS,
skyboxPrefilteredCubePS: skyboxPrefilteredCubePS,
specularPS: specularPS,
specularAaNonePS: specularAaNonePS,
Expand Down
5 changes: 4 additions & 1 deletion 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 = cubeMapRotate1(nrdir);

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

Expand All @@ -13,5 +15,6 @@ vec3 cubeMapProject(vec3 nrdir) {

vec3 posonbox = vPositionW + nrdir * fa;
vec3 envBoxPos = (envBoxMin + envBoxMax) * 0.5;
return posonbox - envBoxPos;

return (posonbox - envBoxPos)*vec3(-1,1,1);
}
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;
cubeMapRotate0(dir);
}
19 changes: 19 additions & 0 deletions src/graphics/program-lib/chunks/cubeMapRotate.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifdef CUBEMAPROT
uniform mat3 cubeMapRotationMatrix;
#endif

vec3 cubeMapRotate0(vec3 refDir) {
#ifdef CUBEMAPROT
return refDir*cubeMapRotationMatrix;
#else
return refDir*vec3(-1,1,1);
#endif
}

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

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

Expand Down
2 changes: 1 addition & 1 deletion src/graphics/program-lib/chunks/reflectionDpAtlas.frag
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ vec2 getDpAtlasUv(vec2 uv, float mip) {
}

vec3 calcReflection(vec3 tReflDirW, float tGlossiness) {
vec3 reflDir = normalize(cubeMapProject(tReflDirW));
vec3 reflDir = normalize(cubeMapProject(tReflDirW)*vec3(-1,1,1));

// Convert vector to DP coords
bool up = reflDir.y > 0.0;
Expand Down
21 changes: 10 additions & 11 deletions src/graphics/program-lib/chunks/reflectionPrefilteredCube.frag
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,24 @@ vec3 calcReflection(vec3 tReflDirW, float tGlossiness) {
// Mips smaller than 4x4 aren't great even for diffuse. Don't forget that we don't have bilinear filtering between different faces.

float bias = saturate(1.0 - tGlossiness) * 5.0; // multiply by max mip level
vec3 mirror = vec3(-1.0, 1.0, 1.0);
vec3 refl = cubeMapProject(tReflDirW);
vec4 cubes0;
vec4 cubes1;
if (bias < 1.0) {
cubes0 = textureCube(texture_prefilteredCubeMap128, fixSeams(refl, 0.0) * mirror);
cubes1 = textureCube(texture_prefilteredCubeMap64, fixSeams(refl, 1.0) * mirror);
cubes0 = textureCube(texture_prefilteredCubeMap128, fixSeams(refl, 0.0));
cubes1 = textureCube(texture_prefilteredCubeMap64, fixSeams(refl, 1.0));
} else if (bias < 2.0) {
cubes0 = textureCube(texture_prefilteredCubeMap64, fixSeams(refl, 1.0) * mirror);
cubes1 = textureCube(texture_prefilteredCubeMap32, fixSeams(refl, 2.0) * mirror);
cubes0 = textureCube(texture_prefilteredCubeMap64, fixSeams(refl, 1.0));
cubes1 = textureCube(texture_prefilteredCubeMap32, fixSeams(refl, 2.0));
} else if (bias < 3.0) {
cubes0 = textureCube(texture_prefilteredCubeMap32, fixSeams(refl, 2.0) * mirror);
cubes1 = textureCube(texture_prefilteredCubeMap16, fixSeams(refl, 3.0) * mirror);
cubes0 = textureCube(texture_prefilteredCubeMap32, fixSeams(refl, 2.0));
cubes1 = textureCube(texture_prefilteredCubeMap16, fixSeams(refl, 3.0));
} else if (bias < 4.0) {
cubes0 = textureCube(texture_prefilteredCubeMap16, fixSeams(refl, 3.0) * mirror);
cubes1 = textureCube(texture_prefilteredCubeMap8, fixSeams(refl, 4.0) * mirror);
cubes0 = textureCube(texture_prefilteredCubeMap16, fixSeams(refl, 3.0));
cubes1 = textureCube(texture_prefilteredCubeMap8, fixSeams(refl, 4.0));
} else {
cubes0 = textureCube(texture_prefilteredCubeMap8, fixSeams(refl, 4.0) * mirror);
cubes1 = textureCube(texture_prefilteredCubeMap4, fixSeams(refl, 5.0) * mirror);
cubes0 = textureCube(texture_prefilteredCubeMap8, fixSeams(refl, 4.0));
cubes1 = textureCube(texture_prefilteredCubeMap4, fixSeams(refl, 5.0));
}

vec4 cubeFinal = mix(cubes0, cubes1, fract(bias));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ 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);
fixedReflDir.x *= -1.0;

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

Expand Down
11 changes: 11 additions & 0 deletions src/graphics/program-lib/chunks/skyboxHDRRot.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
varying vec3 vViewDir;

uniform samplerCube texture_cubeMap;
uniform mat3 cubeMapRotationMatrix;

void main(void) {
vec3 color = processEnvironment($textureCubeSAMPLE(texture_cubeMap, fixSeamsStatic((vViewDir*vec3(-1,1,1))*cubeMapRotationMatrix, $FIXCONST)).rgb);
color = toneMap(color);
color = gammaCorrectOutput(color);
gl_FragColor = vec4(color, 1.0);
}
5 changes: 3 additions & 2 deletions src/graphics/program-lib/programs/skybox.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ 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.useRotation + "" + options.mip;
return key;
},

Expand All @@ -20,7 +20,8 @@ var skybox = {
fshader += gammaCode(options.gamma);
fshader += tonemapCode(options.toneMapping);
fshader += shaderChunks.rgbmPS;
fshader += shaderChunks.skyboxHDRPS
var skyboxChunkPS = options.useRotation ? shaderChunks.skyboxHDRRotPS : shaderChunks.skyboxHDRPS;
fshader += skyboxChunkPS
.replace(/\$textureCubeSAMPLE/g, options.rgbm ? "textureCubeRGBM" : (options.hdr ? "textureCube" : "textureCubeSRGB"))
.replace(/\$FIXCONST/g, (1 - 1 / mip2size[options.mip]) + "");

Expand Down
6 changes: 6 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,13 @@ var standard = {
code += options.fixSeams ? chunks.fixCubemapSeamsStretchPS : chunks.fixCubemapSeamsNonePS;
}

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

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

code += options.cubeMapProjection > 0 ? chunks.cubeMapProjectBoxPS : chunks.cubeMapProjectNonePS;
code += options.skyboxIntensity ? chunks.envMultiplyPS : chunks.envConstPS;
}
Expand Down
Loading