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

Displacement Effect #5573

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cb69ae3
feature(Displacement): Init
defektu Aug 17, 2023
bab5a3d
fix(displacement): rollback & heightMap replacement
defektu Sep 14, 2023
778ed42
fix(displacement): working state with temporary flag
defektu Sep 14, 2023
7f7b099
chore(mesh-displacement): updated mesh-displacement example
defektu Sep 15, 2023
8571a0c
fix(standart-material): displacement effect fully works now.
defektu Sep 15, 2023
4321fb1
feature(displacement-effect): displacement offset
defektu Sep 15, 2023
70eb2f7
fix(standart-material): lint
defektu Sep 16, 2023
0771893
feature(Displacement): Init
defektu Aug 17, 2023
c6daa4c
fix(displacement): rollback & heightMap replacement
defektu Sep 14, 2023
480edb2
fix(displacement): working state with temporary flag
defektu Sep 14, 2023
a9fa42c
chore(mesh-displacement): updated mesh-displacement example
defektu Sep 15, 2023
744516c
fix(standart-material): displacement effect fully works now.
defektu Sep 15, 2023
e21a60b
feature(displacement-effect): displacement offset
defektu Sep 15, 2023
60f59ec
fix(standart-material): lint
defektu Sep 16, 2023
eb7ae50
Merge branch 'displacement_effect' of https://github.com/defektu/engi…
defektu Sep 18, 2023
8289413
feat(displacement): displacementFactor added
defektu Sep 18, 2023
3738ca2
chore(mesh-displacement.tsx): added displacementFactor variable
defektu Sep 19, 2023
312bed2
fix(types-fixup): no fixup needed
defektu Sep 19, 2023
535f0b2
chore(displacement) updated example & couple changes
defektu Sep 19, 2023
d919105
fix(types): lint
defektu Sep 19, 2023
b5c4d73
fix(displcamenet) removed lit arguments
defektu Sep 19, 2023
20b5b0c
Updated property explanations.
defektu Sep 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/src/examples/graphics/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import MaterialPhysicalExample from "./material-physical";
import MaterialTranslucentSpecularExample from "./material-translucent-specular";
import MeshDecalsExample from "./mesh-decals";
import MeshDeformationExample from "./mesh-deformation";
import MeshDisplacementExample from "./mesh-displacement";
import MeshGenerationExample from "./mesh-generation";
import MeshMorphManyExample from "./mesh-morph-many";
import MeshMorphExample from "./mesh-morph";
Expand Down Expand Up @@ -87,6 +88,7 @@ export {
MaterialTranslucentSpecularExample,
MeshDecalsExample,
MeshDeformationExample,
MeshDisplacementExample,
MeshGenerationExample,
MeshMorphManyExample,
MeshMorphExample,
Expand Down
239 changes: 239 additions & 0 deletions examples/src/examples/graphics/mesh-displacement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import React from 'react';
import * as pc from '../../../../';

import { BindingTwoWay, LabelGroup, Panel, SliderInput, BooleanInput } from '@playcanvas/pcui/react';
import { Observer } from '@playcanvas/observer';
class MeshDisplacementExample {
static CATEGORY = 'Graphics';
static NAME = 'Mesh Displacement';
static WEBGPU_ENABLED = false;
mvaligursky marked this conversation as resolved.
Show resolved Hide resolved

controls(data: Observer) {
return <>
<Panel headerText='Displacement'>
<LabelGroup text='Enabled'>
<BooleanInput type='toggle' binding={new BindingTwoWay()} link={{ observer: data, path: 'script.useDisplacement.enabled' }}/>
</LabelGroup>
<LabelGroup text='Factor'>
<SliderInput binding={new BindingTwoWay()} link={{ observer: data, path: 'script.useDisplacement.displacementFactor' }} min={-1.0} max={1.0}/>
</LabelGroup>
<LabelGroup text='Offset'>
<SliderInput binding={new BindingTwoWay()} link={{ observer: data, path: 'script.useDisplacement.displacementOffset' }} min={-1.0} max={1.0}/>
</LabelGroup>
</Panel>
</>;
}

example(canvas: HTMLCanvasElement, deviceType: string, data: any): void {

const assets = {
orbitCamera: new pc.Asset('script', 'script', { url: '/static/scripts/camera/orbit-camera.js' }),
helipad: new pc.Asset('helipad-env-atlas', 'texture', { url: '/static/assets/cubemaps/helipad-env-atlas.png' }, { type: pc.TEXTURETYPE_RGBP, mipmaps: false }),
normal: new pc.Asset("normal", "texture", { url: "/static/assets/textures/seaside-rocks01-normal.jpg" }),
diffuse: new pc.Asset("diffuse", "texture", { url: "/static/assets/textures/seaside-rocks01-color.jpg" }),
other: new pc.Asset("other", "texture", { url: "/static/assets/textures/seaside-rocks01-gloss.jpg" })
};

const gfxOptions = {
deviceTypes: [deviceType],
glslangUrl: '/static/lib/glslang/glslang.js',
twgslUrl: '/static/lib/twgsl/twgsl.js'
};

pc.createGraphicsDevice(canvas, gfxOptions).then((device: pc.GraphicsDevice) => {

const createOptions = new pc.AppOptions();
createOptions.graphicsDevice = device;
createOptions.keyboard = new pc.Keyboard(document.body);
createOptions.mouse = new pc.Mouse(document.body);
createOptions.touch = new pc.TouchDevice(document.body);

createOptions.componentSystems = [
// @ts-ignore
pc.RenderComponentSystem,
// @ts-ignore
pc.CameraComponentSystem,
// @ts-ignore
pc.LightComponentSystem,
// @ts-ignore
pc.ScriptComponentSystem
];
createOptions.resourceHandlers = [
// @ts-ignore
pc.TextureHandler,
// @ts-ignore
pc.ScriptHandler
];

const app = new pc.AppBase(canvas);
app.init(createOptions);

// 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);

const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets);
assetListLoader.load(() => {

app.start();

app.scene.toneMapping = pc.TONEMAP_ACES;
app.scene.ambientLight.set(0, 0, 0);
app.scene.ambientLuminance = 0;
app.scene.envAtlas = assets.helipad.resource;
app.scene.skyboxMip = 1;
app.scene.skyboxIntensity = 0.1;
app.scene.setSkybox(assets.helipad.resources);

// Set default parameters
data.set('script', {
useDisplacement: {
enabled: true,
displacementFactor: -0.1,
displacementOffset: 0.2
}
});

// Create a Material for ground plane
const planeMaterial = new pc.StandardMaterial();
planeMaterial.gloss = 0.0;
planeMaterial.metalness = 0.7;
planeMaterial.useMetalness = true;
planeMaterial.update();

// Create an Entity with a Render component for ground plane
const plane = new pc.Entity();
plane.addComponent('render', {
type: 'plane',
material: planeMaterial
});
plane.setLocalScale(new pc.Vec3(100, 0, 100));
plane.setLocalPosition(0, 0, 0);
app.root.addChild(plane);

// Helper function to create a Sphere
const createSphere = function (x: number, y: number, z: number, material: pc.Material, hiDef: boolean) {
// Create an Entity with Render component
const sphere = new pc.Entity();
sphere.addComponent("render", {
material: material,
type: "sphere"
});

// Create a highly defined sphere mesh
if (hiDef) {
const sphereHidef = pc.createSphere(device, {
radius: 0.5,
latitudeBands: 512,
longitudeBands: 512,
calculateTangents: true
});
sphere.render.meshInstances[0].mesh = sphereHidef;
}

// Set sphere position & scale and add to scene
sphere.setLocalPosition(x, y, z);
sphere.setLocalScale(3.0, 3.0, 3.0);
app.root.addChild(sphere);
};

// Create a material
const material = new pc.StandardMaterial();
material.diffuseMap = assets.diffuse.resource;
material.metalnessMap = assets.other.resource;
material.metalnessMapChannel = "r";
material.glossMap = assets.other.resource;
material.glossMapChannel = "g";
material.normalMap = assets.normal.resource;
material.diffuse = new pc.Color(0.6, 0.6, 0.9);
material.diffuseTint = true;
material.metalness = 0.6;
material.gloss = 0.8;
material.bumpiness = 0.7;
material.useMetalness = true;

// Set materials height map for displacement to use
material.heightMap = assets.other.resource;

// Set materials displacement parameters
material.useDisplacement = data.get('script.useDisplacement.enabled');
material.displacementFactor = data.get('script.useDisplacement.displacementFactor');
material.displacementOffset = data.get('script.useDisplacement.displacementOffset');
material.update();

// Add sphere with specified material with helper function
const sphere = createSphere(0, 2.5, 0, material, true);

// Create an Entity with a light component
const lightOmni = new pc.Entity("Omni");
lightOmni.addComponent("light", {
type: "omni",
color: new pc.Color(1, 1, 1),
range: 25,
penumbraSize: 32,
shadowType: pc.SHADOW_PCSS,
intensity: 4.0,
castShadows: true,
shadowBias: 0.01,
normalOffsetBias: 0.01,
shadowResolution: 2048
});
lightOmni.setLocalPosition(-4, 7, 0);

app.root.addChild(lightOmni);

// Create an Entity with a camera component
const camera = new pc.Entity();
camera.addComponent("camera", {
clearColor: new pc.Color(0.4, 0.45, 0.5)
});
camera.setLocalPosition(0, 5, 11);

// Add orbit script to camera component
camera.addComponent("script");
camera.script.create("orbitCamera", {
attributes: {
inertiaFactor: 0.2,
focusEntity: sphere,
distanceMax: 500,
frameOnStart: false
}
});
camera.script.create("orbitCameraInputMouse");
camera.script.create("orbitCameraInputTouch");
app.root.addChild(camera);

data.on('*:set', (path: string, value: any) => {
switch (path) {
case 'script.useDisplacement.enabled':
material.useDisplacement = value;
material.update();
break;
case 'script.useDisplacement.displacementFactor':
material.displacementFactor = value;
material.update();
break;
case 'script.useDisplacement.displacementOffset':
material.displacementOffset = value;
material.update();
break;
}
});

let resizeControlPanel = true;
app.on("update", function () {
// resize control panel to fit the content better
if (resizeControlPanel) {
const panel = window.top.document.getElementById('controlPanel');
if (panel) {
panel.style.width = '360px';
resizeControlPanel = false;
}
}
});
});
});
}
}

export default MeshDisplacementExample;
3 changes: 3 additions & 0 deletions src/scene/materials/standard-material-options-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class StandardMaterialOptionsBuilder {
options.litOptions.vertexColors = true;
}
}

if (stdMat[mname]) {
let allow = true;
if (stdMat[uname] === 0 && !hasUv0) allow = false;
Expand Down Expand Up @@ -287,6 +288,8 @@ class StandardMaterialOptionsBuilder {
options.litOptions.useIridescence = stdMat.useIridescence && stdMat.iridescence !== 0.0;
options.litOptions.useMetalness = stdMat.useMetalness;
options.litOptions.useDynamicRefraction = stdMat.useDynamicRefraction;

options.litOptions.useDisplacement = stdMat.useDisplacement;
defektu marked this conversation as resolved.
Show resolved Hide resolved
}

_updateEnvOptions(options, stdMat, scene) {
Expand Down
4 changes: 4 additions & 0 deletions src/scene/materials/standard-material-parameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ const standardMaterialParameterTypes = {
..._textureParameter('height', true, false),
heightMapFactor: 'number',

useDisplacement: "boolean",
displacementFactor: "number",
displacementOffset: "number",

alphaToCoverage: 'boolean',
alphaTest: 'number',
alphaFade: 'number',
Expand Down
20 changes: 19 additions & 1 deletion src/scene/materials/standard-material.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,13 @@ let _params = new Set();
* @property {number} heightMapRotation Controls the 2D rotation (in degrees) of the height map.
* @property {number} heightMapFactor Height map multiplier. Affects the strength of the parallax
* effect.
* @property {boolean} useDisplacement Use heightMap as vertex displacement. When enabled, vertex'
* displaced according to heightMap's red channel. This effect takes UV0 channel for displacement
* and does not support offsetting or translating maps.
* @property {number} displacementFactor Displacement multiplier. Affects the strength of the
* displacement effect.
* @property {number} displacementOffset Displacement height offset. Shifts displacement effect in
* initial vertex normal direction.
* @property {import('../../platform/graphics/texture.js').Texture|null} envAtlas The prefiltered
* environment lighting atlas (default is null). This setting overrides cubeMap and sphereMap and
* will replace the scene lighting environment.
Expand Down Expand Up @@ -816,6 +823,11 @@ class StandardMaterial extends Material {
this._setParameter('material_heightMapFactor', getUniform('heightMapFactor'));
}

if (this.useDisplacement) {
this._setParameter('material_displacementFactor', this.displacementFactor);
this._setParameter('material_displacementOffset', this.displacementOffset);
}

const isPhong = this.shadingModel === SPECULAR_PHONG;

// set overridden environment textures
Expand Down Expand Up @@ -865,7 +877,8 @@ class StandardMaterial extends Material {

// Minimal options for Depth and Shadow passes
const shaderPassInfo = ShaderPass.get(device).getByIndex(pass);
const minimalOptions = pass === SHADER_DEPTH || pass === SHADER_PICK || shaderPassInfo.isShadow;
const minimalOptions = this.useDisplacement ? false : pass === SHADER_DEPTH || pass === SHADER_PICK || shaderPassInfo.isShadow;

let options = minimalOptions ? standard.optionsContextMin : standard.optionsContext;

if (minimalOptions)
Expand Down Expand Up @@ -1144,6 +1157,9 @@ function _defineMaterialProps() {
_defineFloat('heightMapFactor', 1, (material, device, scene) => {
return material.heightMapFactor * 0.025;
});
_defineFloat('displacementFactor', 0, (material, device, scene) => {
return material.displacementFactor;
});
_defineFloat('opacity', 1);
_defineFloat('alphaFade', 1);
_defineFloat('alphaTest', 0); // NOTE: overwrites Material.alphaTest
Expand Down Expand Up @@ -1229,6 +1245,8 @@ function _defineMaterialProps() {
_defineFlag('sheenGlossInvert', false);
_defineFlag('clearCoatGlossInvert', false);

_defineFlag('useDisplacement', false);

_defineTex2D('diffuse');
_defineTex2D('specular');
_defineTex2D('emissive');
Expand Down
13 changes: 12 additions & 1 deletion src/scene/shader-lib/chunks/common/vert/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,22 @@ mat4 getModelMatrix() {
return matrix_model;
#endif
}

#ifdef DISPLACEMENT
uniform sampler2D texture_heightMap;
uniform float material_displacementFactor;
uniform float material_displacementOffset;
#endif
vec4 getPosition() {
dModelMatrix = getModelMatrix();

defektu marked this conversation as resolved.
Show resolved Hide resolved
vec3 localPos = vertex_position;

#ifdef DISPLACEMENT
vec4 dMapSample = texture2D(texture_heightMap, vertex_texCoord0.xy);
float displacer = dMapSample.r * material_displacementFactor + material_displacementOffset;
localPos += vertex_normal * displacer;
#endif

#ifdef NINESLICED
// outer and inner vertices are at the same position, scale both
localPos.xz *= outerScale;
Expand Down
2 changes: 2 additions & 0 deletions src/scene/shader-lib/programs/lit-shader-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class LitShaderOptions {

useHeights = false;

useDisplacement = false;

useNormals = false;

useClearCoatNormals = false;
Expand Down
4 changes: 4 additions & 0 deletions src/scene/shader-lib/programs/lit-shader.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class LitShader {
(options.clusteredLightingEnabled && !this.shadowPass) ||
options.useClearCoatNormals;
this.needsNormal = this.needsNormal && !this.shadowPass;
this.needsNormal ||= options.useDisplacement;
this.needsSceneColor = options.useDynamicRefraction;
this.needsScreenSize = options.useDynamicRefraction;
this.needsTransforms = options.useDynamicRefraction;
Expand Down Expand Up @@ -380,6 +381,9 @@ class LitShader {
if (options.pixelSnap) {
code += "#define PIXELSNAP\n";
}
if (options.useDisplacement) {
code += "#define DISPLACEMENT\n";
}

code = this._vsAddTransformCode(code, device, chunks, options);

Expand Down
3 changes: 3 additions & 0 deletions utils/types-fixup.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@ const standardMaterialProps = [
['cubeMap', 'Texture|null'],
['cubeMapProjection', 'number'],
['cubeMapProjectionBox', 'BoundingBox'],
['useDisplacement', 'boolean'],
['displacementFactor', 'number'],
['displacementOffset', 'number'],
['diffuse', 'Color'],
['diffuseDetailMap', 'Texture|null'],
['diffuseDetailMapChannel', 'string'],
Expand Down