AEC Aesthetics (#2225)
* Addede a bunch of abstract hdris as well as the option to cycle cle though them at runtime * Added an extended PMREMGenerator that allows for the rotation of the hdri image.Now our IBL sources are finally straight not tilded 90 deg anymore. Added artificial shininess to all objects configurable in real time from the sandbox. * Added color grading operations to our standard shader and material. Updated the sandbox to reflect the required AEC Aesthetics properties
This commit is contained in:
committed by
GitHub
parent
c64c15a55c
commit
1010c9d23a
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { Box3, SectionTool, TreeNode } from '@speckle/viewer'
|
||||
import { Box3, SectionTool, SpeckleStandardMaterial, TreeNode } from '@speckle/viewer'
|
||||
import {
|
||||
CanonicalView,
|
||||
DebugViewer,
|
||||
@@ -25,9 +25,18 @@ import { FilteringExtension } from '@speckle/viewer'
|
||||
import { MeasurementsExtension } from '@speckle/viewer'
|
||||
import { CameraController } from '@speckle/viewer'
|
||||
import { UpdateFlags } from '@speckle/viewer'
|
||||
import { Viewer } from '@speckle/viewer'
|
||||
import { Viewer, AssetType, Assets } from '@speckle/viewer'
|
||||
import hdri0 from '../assets/sample-hdri.png'
|
||||
import hdri1 from '../assets/1.png'
|
||||
import hdri2 from '../assets/2.png'
|
||||
import hdri3 from '../assets/3.png'
|
||||
import hdri4 from '../assets/4.png'
|
||||
import hdri5 from '../assets/5.png'
|
||||
import hdri6 from '../assets/6.png'
|
||||
|
||||
import { Euler, Vector3 } from 'three'
|
||||
import { GeometryType } from '@speckle/viewer'
|
||||
import { MeshBatch } from '@speckle/viewer'
|
||||
|
||||
export default class Sandbox {
|
||||
private viewer: Viewer
|
||||
@@ -50,7 +59,9 @@ export default class Sandbox {
|
||||
worldOrigin: { x: 0, y: 0, z: 0 },
|
||||
pixelThreshold: 0.5,
|
||||
exposure: 0.5,
|
||||
tonemapping: 4 //'ACESFilmicToneMapping'
|
||||
tonemapping: 4, //'ACESFilmicToneMapping',
|
||||
contrast: 1,
|
||||
saturation: 1
|
||||
}
|
||||
|
||||
public pipelineParams = {
|
||||
@@ -120,6 +131,11 @@ export default class Sandbox {
|
||||
precision: 2
|
||||
}
|
||||
|
||||
public hdriParams = {
|
||||
id: '/assets/2.png',
|
||||
minRoughness: 0.5
|
||||
}
|
||||
|
||||
public constructor(
|
||||
container: HTMLElement,
|
||||
viewer: DebugViewer,
|
||||
@@ -145,8 +161,8 @@ export default class Sandbox {
|
||||
this.properties = []
|
||||
|
||||
viewer.on(ViewerEvent.LoadComplete, async (url: string) => {
|
||||
this.addStreamControls(url)
|
||||
this.addViewControls()
|
||||
// this.addStreamControls(url)
|
||||
// this.addViewControls()
|
||||
this.addBatches()
|
||||
this.properties = await this.viewer.getObjectProperties()
|
||||
this.batchesParams.totalBvhSize = this.getBVHSize()
|
||||
@@ -456,15 +472,6 @@ export default class Sandbox {
|
||||
})
|
||||
screenshot.on('click', async () => {
|
||||
console.warn(await this.viewer.screenshot())
|
||||
// const start = performance.now()
|
||||
// const nodes = this.viewer.getWorldTree().root.all(
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// (node: any) => node.model.raw.id === 'c35234a1e8584b159f7e8be59323cd64'
|
||||
// )
|
||||
// console.log(nodes)
|
||||
// this.viewer.cancelLoad(
|
||||
// 'https://latest.speckle.dev/streams/97750296c2/objects/c3138e24a866d447eb86b2a8107b2c09'
|
||||
// )
|
||||
})
|
||||
|
||||
const rotate = this.tabs.pages[0].addButton({
|
||||
@@ -486,7 +493,7 @@ export default class Sandbox {
|
||||
|
||||
const canonicalViewsFolder = this.tabs.pages[0].addFolder({
|
||||
title: 'Canonical Views',
|
||||
expanded: true
|
||||
expanded: false
|
||||
})
|
||||
const sides = ['front', 'back', 'top', 'bottom', 'right', 'left', '3d']
|
||||
for (let k = 0; k < sides.length; k++) {
|
||||
@@ -500,6 +507,130 @@ export default class Sandbox {
|
||||
.setCameraView(sides[k] as CanonicalView, true)
|
||||
})
|
||||
}
|
||||
|
||||
const hdriFolder = this.tabs.pages[0].addFolder({ title: 'HDRI', expanded: true })
|
||||
|
||||
hdriFolder
|
||||
.addInput(this.hdriParams, 'id', {
|
||||
label: 'HDRI',
|
||||
options: {
|
||||
Default: hdri0,
|
||||
Mild: hdri1,
|
||||
Mild2: hdri2,
|
||||
Sharp: hdri4,
|
||||
Bright: hdri6
|
||||
}
|
||||
})
|
||||
.on('change', async (value) => {
|
||||
this.viewer.getRenderer().indirectIBL = await Assets.getEnvironment(
|
||||
{
|
||||
id: this.hdriParams.id,
|
||||
src: value.value,
|
||||
type: AssetType.TEXTURE_EXR
|
||||
},
|
||||
this.viewer.getRenderer().renderer
|
||||
)
|
||||
this.viewer.requestRender()
|
||||
})
|
||||
|
||||
hdriFolder
|
||||
.addInput(this.sceneParams, 'exposure', {
|
||||
min: 0,
|
||||
max: 1
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.getRenderer().renderer.toneMappingExposure =
|
||||
this.sceneParams.exposure
|
||||
this.viewer.requestRender()
|
||||
})
|
||||
hdriFolder
|
||||
.addInput(this.sceneParams, 'contrast', {
|
||||
min: 0,
|
||||
max: 2
|
||||
})
|
||||
.on('change', () => {
|
||||
const batches = this.viewer
|
||||
.getRenderer()
|
||||
.batcher.getBatches(undefined, GeometryType.MESH) as MeshBatch[]
|
||||
batches.forEach((batch: MeshBatch) => {
|
||||
const materials = batch.materials as SpeckleStandardMaterial[]
|
||||
materials.forEach((material: SpeckleStandardMaterial) => {
|
||||
material.userData.contrast.value = this.sceneParams.contrast
|
||||
material.needsCopy = true
|
||||
})
|
||||
})
|
||||
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
||||
})
|
||||
hdriFolder
|
||||
.addInput(this.sceneParams, 'saturation', {
|
||||
min: 0,
|
||||
max: 2
|
||||
})
|
||||
.on('change', () => {
|
||||
const batches = this.viewer
|
||||
.getRenderer()
|
||||
.batcher.getBatches(undefined, GeometryType.MESH) as MeshBatch[]
|
||||
batches.forEach((batch: MeshBatch) => {
|
||||
const materials = batch.materials as SpeckleStandardMaterial[]
|
||||
materials.forEach((material: SpeckleStandardMaterial) => {
|
||||
material.userData.saturation.value = this.sceneParams.saturation
|
||||
material.needsCopy = true
|
||||
})
|
||||
})
|
||||
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
||||
})
|
||||
hdriFolder
|
||||
.addInput(this.hdriParams, 'minRoughness', {
|
||||
label: 'Shininess',
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.05
|
||||
})
|
||||
.on('change', (value) => {
|
||||
const batches = this.viewer
|
||||
.getRenderer()
|
||||
.batcher.getBatches(undefined, GeometryType.MESH) as MeshBatch[]
|
||||
batches.forEach((batch: MeshBatch) => {
|
||||
const materials = batch.materials
|
||||
materials.forEach((material: SpeckleStandardMaterial) => {
|
||||
material.roughness = Math.min(
|
||||
material.userData.originalRoughness,
|
||||
1 - this.hdriParams.minRoughness
|
||||
)
|
||||
material.needsCopy = true
|
||||
})
|
||||
})
|
||||
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
||||
})
|
||||
}
|
||||
|
||||
public async applyParams() {
|
||||
this.viewer.getRenderer().indirectIBL = await Assets.getEnvironment(
|
||||
{
|
||||
id: 'Mild2',
|
||||
src: hdri2,
|
||||
type: AssetType.TEXTURE_EXR
|
||||
},
|
||||
this.viewer.getRenderer().renderer
|
||||
)
|
||||
this.viewer.getRenderer().renderer.toneMappingExposure = this.sceneParams.exposure
|
||||
const batches = this.viewer
|
||||
.getRenderer()
|
||||
.batcher.getBatches(undefined, GeometryType.MESH) as MeshBatch[]
|
||||
batches.forEach((batch: MeshBatch) => {
|
||||
const materials = batch.materials as SpeckleStandardMaterial[]
|
||||
materials.forEach((material: SpeckleStandardMaterial) => {
|
||||
material.userData.contrast.value = this.sceneParams.contrast
|
||||
material.userData.saturation.value = this.sceneParams.saturation
|
||||
material.userData.originalRoughness = material.roughness
|
||||
material.roughness = Math.min(
|
||||
material.userData.originalRoughness,
|
||||
1 - this.hdriParams.minRoughness
|
||||
)
|
||||
material.needsCopy = true
|
||||
})
|
||||
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
||||
})
|
||||
}
|
||||
|
||||
makeSceneUI() {
|
||||
@@ -542,16 +673,16 @@ export default class Sandbox {
|
||||
expanded: true
|
||||
})
|
||||
|
||||
postFolder
|
||||
.addInput(this.sceneParams, 'exposure', {
|
||||
min: 0,
|
||||
max: 1
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.getRenderer().renderer.toneMappingExposure =
|
||||
this.sceneParams.exposure
|
||||
this.viewer.requestRender()
|
||||
})
|
||||
// postFolder
|
||||
// .addInput(this.sceneParams, 'exposure', {
|
||||
// min: 0,
|
||||
// max: 1
|
||||
// })
|
||||
// .on('change', () => {
|
||||
// this.viewer.getRenderer().renderer.toneMappingExposure =
|
||||
// this.sceneParams.exposure
|
||||
// this.viewer.requestRender()
|
||||
// })
|
||||
|
||||
postFolder
|
||||
.addInput(this.sceneParams, 'tonemapping', {
|
||||
|
||||
@@ -111,6 +111,8 @@ const createViewer = async (containerName: string, stream: string) => {
|
||||
sandbox.makeMeasurementsUI()
|
||||
|
||||
await sandbox.loadUrl(stream)
|
||||
|
||||
await sandbox.applyParams()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
@@ -119,7 +121,7 @@ const getStream = () => {
|
||||
// prettier-ignore
|
||||
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D'
|
||||
// Revit sample house (good for bim-like stuff with many display meshes)
|
||||
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
|
||||
'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
|
||||
// 'https://latest.speckle.dev/streams/58b5648c4d/commits/60371ecb2d'
|
||||
// 'Super' heavy revit shit
|
||||
// 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5'
|
||||
@@ -248,7 +250,7 @@ const getStream = () => {
|
||||
// 'https://latest.speckle.dev/streams/f92e060177/commits/038a587267'
|
||||
// 'https://latest.speckle.dev/streams/3f895e614f/commits/8a3e424997'
|
||||
// 'https://latest.speckle.dev/streams/f92e060177/commits/f51ee777d5'
|
||||
'https://latest.speckle.dev/streams/f92e060177/commits/bbd821e3a1'
|
||||
// 'https://latest.speckle.dev/streams/f92e060177/commits/bbd821e3a1'
|
||||
// Big curves
|
||||
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/49dad07ae2'
|
||||
// 'https://speckle.xyz/streams/7ce9010d71/commits/afda4ffdf8'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Viewer } from './modules/Viewer'
|
||||
import {
|
||||
AssetType,
|
||||
DefaultLightConfiguration,
|
||||
DefaultViewerParams,
|
||||
IViewer,
|
||||
@@ -66,6 +67,7 @@ import SpeckleStandardMaterial from './modules/materials/SpeckleStandardMaterial
|
||||
import SpeckleTextMaterial from './modules/materials/SpeckleTextMaterial'
|
||||
import { SpeckleText } from './modules/objects/SpeckleText'
|
||||
import { NodeRenderView } from './modules/tree/NodeRenderView'
|
||||
import { Assets } from './modules/Assets'
|
||||
|
||||
export {
|
||||
Viewer,
|
||||
@@ -105,7 +107,9 @@ export {
|
||||
SpeckleStandardMaterial,
|
||||
SpeckleTextMaterial,
|
||||
SpeckleText,
|
||||
NodeRenderView
|
||||
NodeRenderView,
|
||||
Assets,
|
||||
AssetType
|
||||
}
|
||||
|
||||
export type {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import {
|
||||
Texture,
|
||||
PMREMGenerator,
|
||||
WebGLRenderer,
|
||||
TextureLoader,
|
||||
Color,
|
||||
DataTexture
|
||||
DataTexture,
|
||||
Matrix4,
|
||||
Euler
|
||||
} from 'three'
|
||||
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js'
|
||||
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
|
||||
import { FontLoader, Font } from 'three/examples/jsm/loaders/FontLoader.js'
|
||||
import { Asset, AssetType } from '../IViewer'
|
||||
import Logger from 'js-logger'
|
||||
import { RotatablePMREMGenerator } from './objects/RotatablePMREMGenerator'
|
||||
|
||||
export class Assets {
|
||||
private static _cache: { [name: string]: Texture | Font } = {}
|
||||
@@ -32,8 +34,11 @@ export class Assets {
|
||||
}
|
||||
|
||||
private static hdriToPMREM(renderer: WebGLRenderer, hdriTex: Texture): Texture {
|
||||
const generator = new PMREMGenerator(renderer)
|
||||
generator.compileEquirectangularShader()
|
||||
const generator = new RotatablePMREMGenerator(renderer)
|
||||
const mat = new Matrix4().makeRotationFromEuler(
|
||||
new Euler(-Math.PI * 0.5, 0, -Math.PI * 0.5)
|
||||
)
|
||||
generator.compileProperEquirectShader(mat)
|
||||
const pmremRT = generator.fromEquirectangular(hdriTex)
|
||||
generator.dispose()
|
||||
return pmremRT.texture
|
||||
|
||||
@@ -41,7 +41,7 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
/** Viewer components */
|
||||
protected tree: WorldTree = new WorldTree()
|
||||
protected world: World = new World()
|
||||
public static Assets: Assets
|
||||
public static readonly theAssets: Assets = new Assets()
|
||||
public speckleRenderer: SpeckleRenderer
|
||||
protected propertyManager: PropertyManager
|
||||
|
||||
@@ -143,7 +143,6 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
this.speckleRenderer.create(this.container)
|
||||
window.addEventListener('resize', this.resize.bind(this), false)
|
||||
|
||||
new Assets()
|
||||
this.propertyManager = new PropertyManager()
|
||||
|
||||
this.frame()
|
||||
|
||||
@@ -29,7 +29,9 @@ class SpeckleStandardMaterial extends ExtendedMeshStandardMaterial {
|
||||
uShadowViewer_low: new Vector3(),
|
||||
uTransforms: [new Matrix4()],
|
||||
tTransforms: null,
|
||||
objCount: 1
|
||||
objCount: 1,
|
||||
contrast: 1,
|
||||
saturation: 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,62 @@ uniform float opacity;
|
||||
#endif
|
||||
|
||||
varying vec3 vViewPosition;
|
||||
#define CUSTOM_TONEMAPPING
|
||||
|
||||
#ifdef CUSTOM_TONEMAPPING
|
||||
|
||||
uniform float contrast;
|
||||
uniform float saturation;
|
||||
uniform float whitescale;
|
||||
vec3 EvalLogContrastFunc(vec3 col, float eps, float logMidpoint, float contrastFactor)
|
||||
{
|
||||
vec3 x = max(vec3(0.), col);
|
||||
vec3 logX = log2(x+vec3(eps));
|
||||
vec3 adjX = vec3(logMidpoint) + (logX - vec3(logMidpoint)) * contrastFactor;
|
||||
vec3 ret = max(vec3(0.0), exp2(adjX) - vec3(eps));
|
||||
return ret;
|
||||
}
|
||||
|
||||
vec3 evalSaturation(vec3 rgbVal, float saturationFactor){
|
||||
vec3 lumaWeights = vec3(.25,.50,.25);
|
||||
vec3 grey = vec3(dot(lumaWeights,rgbVal));
|
||||
return grey + saturationFactor*(rgbVal-grey);
|
||||
}
|
||||
|
||||
vec3 evalExposure(vec3 rgbVal, float exposureFactor){
|
||||
return rgbVal * exp2(exposureFactor);
|
||||
}
|
||||
|
||||
vec3 filmicTonemap(vec3 x) {
|
||||
float A = 0.15;
|
||||
float B = 0.50;
|
||||
float C = 0.10;
|
||||
float D = 0.20;
|
||||
float E = 0.02;
|
||||
float F = 0.30;
|
||||
float W = 11.2;
|
||||
return ((x*(A*x+C*B)+D*E) / (x*(A*x+B)+D*F))- E / F;
|
||||
}
|
||||
|
||||
|
||||
vec3 applyFilmicToneMap( vec3 color)
|
||||
{
|
||||
color = 2.0 * filmicTonemap( color);
|
||||
vec3 whiteScale = 1.0 / filmicTonemap(vec3(11.2));
|
||||
color *= whiteScale;
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 postProcess(in vec3 _color, float exposureFactor, float contrastFactor, float saturationFactor){
|
||||
vec3 color = _color;
|
||||
|
||||
// color.rgb *= exposureFactor;
|
||||
color.rgb = evalSaturation(color.rgb, saturationFactor);
|
||||
color = EvalLogContrastFunc(color, 0.0001, 0.18, contrastFactor);
|
||||
color.rgb = ACESFilmicToneMapping( color );//applyFilmicToneMap(color.rgb);
|
||||
return color;
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <common>
|
||||
#include <packing>
|
||||
@@ -137,7 +193,14 @@ void main() {
|
||||
#endif
|
||||
|
||||
#include <output_fragment>
|
||||
#include <tonemapping_fragment>
|
||||
// #include <tonemapping_fragment> // COMMENTED OUT
|
||||
#ifdef TONE_MAPPING
|
||||
#ifdef CUSTOM_TONEMAPPING
|
||||
gl_FragColor.rgb = postProcess(gl_FragColor.rgb, toneMappingExposure, contrast, saturation);
|
||||
#else
|
||||
gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );
|
||||
#endif
|
||||
#endif
|
||||
#include <encodings_fragment>
|
||||
#include <fog_fragment>
|
||||
#include <premultiplied_alpha_fragment>
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Matrix4, NoBlending, PMREMGenerator, ShaderMaterial } from 'three'
|
||||
|
||||
export class RotatablePMREMGenerator extends PMREMGenerator {
|
||||
constructor(renderer) {
|
||||
super(renderer)
|
||||
}
|
||||
|
||||
public compileProperEquirectShader(rotationMatrix?: Matrix4) {
|
||||
const fixedEnvFlip = function _getEquirectMaterial() {
|
||||
return new ShaderMaterial({
|
||||
name: 'EquirectangularToCubeUV',
|
||||
|
||||
uniforms: {
|
||||
envMap: { value: null },
|
||||
rotationMatrix: { value: rotationMatrix }
|
||||
},
|
||||
|
||||
vertexShader: `
|
||||
precision mediump float;
|
||||
precision mediump int;
|
||||
attribute float faceIndex;
|
||||
varying vec3 vOutputDirection;
|
||||
// RH coordinate system; PMREM face-indexing convention
|
||||
vec3 getDirection( vec2 uv, float face ) {
|
||||
uv = 2.0 * uv - 1.0;
|
||||
vec3 direction = vec3( uv, 1.0 );
|
||||
if ( face == 0.0 ) {
|
||||
direction = direction.zyx; // ( 1, v, u ) pos x
|
||||
} else if ( face == 1.0 ) {
|
||||
direction = direction.xzy;
|
||||
direction.xz *= -1.0; // ( -u, 1, -v ) pos y
|
||||
} else if ( face == 2.0 ) {
|
||||
direction.x *= -1.0; // ( -u, v, 1 ) pos z
|
||||
} else if ( face == 3.0 ) {
|
||||
direction = direction.zyx;
|
||||
direction.xz *= -1.0; // ( -1, v, -u ) neg x
|
||||
} else if ( face == 4.0 ) {
|
||||
direction = direction.xzy;
|
||||
direction.xy *= -1.0; // ( -u, -1, v ) neg y
|
||||
} else if ( face == 5.0 ) {
|
||||
direction.z *= -1.0; // ( u, v, -1 ) neg z
|
||||
}
|
||||
return direction;
|
||||
}
|
||||
void main() {
|
||||
vOutputDirection = getDirection( uv, faceIndex );
|
||||
gl_Position = vec4( position, 1.0 );
|
||||
}
|
||||
`,
|
||||
|
||||
fragmentShader: /* glsl */ `
|
||||
precision mediump float;
|
||||
precision mediump int;
|
||||
varying vec3 vOutputDirection;
|
||||
uniform sampler2D envMap;
|
||||
uniform mat4 rotationMatrix;
|
||||
#include <common>
|
||||
void main() {
|
||||
|
||||
vec3 outputDirection = normalize( vOutputDirection );
|
||||
outputDirection = normalize((rotationMatrix * vec4(vOutputDirection, 0.)).xyz);
|
||||
vec2 uv = equirectUv( outputDirection );
|
||||
gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 );
|
||||
}
|
||||
`,
|
||||
|
||||
blending: NoBlending,
|
||||
depthTest: false,
|
||||
depthWrite: false
|
||||
})
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this._equirectMaterial = fixedEnvFlip()
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this._compileMaterial(this._equirectMaterial)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user