Преглед изворни кода

Add progressive hdr shadows experiment, rearrange examples.

master
Palash Bansal пре 2 година
родитељ
комит
c38b90fb0a
No account linked to committer's email address

+ 21
- 17
examples/index.html Прегледај датотеку

@@ -374,14 +374,6 @@
<li><a href="./pmat-material-export/">PMAT Material Export </a></li>
<li><a href="./svg-geometry-playground/">SVG Geometry Playground </a></li>
</ul>
<h2 class="category">UI Config</h2>
<ul>
<li><a href="./material-uiconfig/">Material UI </a></li>
<li><a href="./object-uiconfig/">Object UI </a></li>
<li><a href="./camera-uiconfig/">Camera UI </a></li>
<li><a href="./scene-uiconfig/">Scene UI </a></li>
<li><a href="./viewer-uiconfig/">Viewer UI </a></li>
</ul>
<h2 class="category">UI Plugins</h2>
<ul>
<li><a href="./tweakpane-ui-plugin/">Tweakpane UI Plugin </a></li>
@@ -394,7 +386,17 @@
<li><a href="./gltf-camera-animation/">glTF Camera Animation </a></li>
<li><a href="./gltf-animation-page-scroll/">glTF Animation Page Scroll </a></li>
</ul>
<h2 class="category">Materials</h2>
<h2 class="category">Samples</h2>
<ul>
<li><a href="./html-js-sample/">HTML/JS Sample </a></li>
<li><a href="./react-js-sample/">React/JS Sample </a></li>
<li><a href="./react-jsx-sample/">React/JSX Sample </a></li>
<li><a href="./react-tsx-sample/">React/TSX Sample </a></li>
<li><a href="./vue-html-sample/">Vue/HTML Sample </a></li>
<li><a href="./vue-sfc-sample/">Vue/SFC Sample </a></li>
<li><a href="./svelte-sample/">Svelte Sample </a></li>
</ul>
<h2 class="category">Material Extensions</h2>
<ul>
<li><a href="./clearcoat-tint-plugin/">Clearcoat Tint Plugin</a></li>
<li><a href="./fragment-clipping-extension-plugin/">Fragment Clipping Extension Plugin </a></li>
@@ -415,15 +417,17 @@
<li><a href="./3dm-to-glb/">Convert 3DM to GLB </a></li>
<li><a href="./hdr-to-exr/">Convert HDR to EXR </a></li>
</ul>
<h2 class="category">Samples</h2>
<h2 class="category">Experiments</h2>
<ul>
<li><a href="./html-js-sample/">HTML/JS Sample </a></li>
<li><a href="./react-js-sample/">React/JS Sample </a></li>
<li><a href="./react-jsx-sample/">React/JSX Sample </a></li>
<li><a href="./react-tsx-sample/">React/TSX Sample </a></li>
<li><a href="./vue-html-sample/">Vue/HTML Sample </a></li>
<li><a href="./vue-sfc-sample/">Vue/SFC Sample </a></li>
<li><a href="./svelte-sample/">Svelte Sample </a></li>
<li><a href="./progressive-hdr-shadows-exp/">Progressive HDR Environment Shadows</a></li>
</ul>
<h2 class="category">UI Config</h2>
<ul>
<li><a href="./material-uiconfig/">Material UI </a></li>
<li><a href="./object-uiconfig/">Object UI </a></li>
<li><a href="./camera-uiconfig/">Camera UI </a></li>
<li><a href="./scene-uiconfig/">Scene UI </a></li>
<li><a href="./viewer-uiconfig/">Viewer UI </a></li>
</ul>
<h2 class="category">Lights</h2>
<ul>

+ 36
- 0
examples/progressive-hdr-shadows-exp/index.html Прегледај датотеку

@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Object UiConfig</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"threepipe": "./../../dist/index.mjs",
"@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs"
}
}

</script>
<style id="example-style">
html, body, #canvas-container, #mcanvas {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>
<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
<script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script>
</head>
<body>
<div id="canvas-container">
<canvas id="mcanvas"></canvas>
</div>

</body>

+ 255
- 0
examples/progressive-hdr-shadows-exp/script.ts Прегледај датотеку

@@ -0,0 +1,255 @@
import {
_testFinish,
BaseGroundPlugin,
BasicShadowMap,
Color,
DataUtils,
DirectionalLight,
IObject3D,
MaterialExtension,
ProgressivePlugin,
ShaderChunk,
shaderReplaceString,
ThreeViewer,
Vector3,
} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

const hdris = [
'https://threejs.org/examples/textures/equirectangular/quarry_01_1k.hdr',
'https://threejs.org/examples/textures/equirectangular/spot1Lux.hdr',
'https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr',
'https://dist.pixotronics.com/webgi/assets/hdr/gem_2.hdr',
'https://hdrihaven.r2cache.com/hdr/1k/studio_small_04_1k.hdr',
'https://hdrihaven.r2cache.com/hdr/1k/studio_small_03_1k.hdr',
'https://threejs.org/examples/textures/equirectangular/pedestrian_overpass_1k.hdr',
'https://threejs.org/examples/textures/equirectangular/blouberg_sunrise_2_1k.hdr',
'https://threejs.org/examples/textures/equirectangular/royal_esplanade_1k.hdr',
'https://threejs.org/examples/textures/equirectangular/moonless_golf_1k.hdr',
'https://threejs.org/examples/textures/equirectangular/san_giuseppe_bridge_2k.hdr',
'https://hdrihaven.r2cache.com/hdr/1k/studio_small_06_1k.hdr',
'https://hdrihaven.r2cache.com/hdr/1k/studio_small_05_1k.hdr',
'https://hdrihaven.r2cache.com/hdr/1k/studio_small_02_1k.hdr',
'https://hdrihaven.r2cache.com/hdr/1k/studio_small_01_1k.hdr',
]

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
msaa: false,
rgbm: false,
plugins: [new ProgressivePlugin((window as any).TESTING ? 20 : 200)],
})

const directionalLight = createDirLight(viewer)

viewer.materialManager.registerMaterialExtension(extension)
viewer.renderManager.renderer.shadowMap.type = BasicShadowMap

// extra check to ignore the sampling of shadow if intensity is 0
ShaderChunk.lights_fragment_begin = shaderReplaceString(
ShaderChunk.lights_fragment_begin,
'directLight.color *= ( directLight.visible && receiveShadow )',
'directLight.color *= ( directLight.visible && receiveShadow && length(directLight.color) > 0.001)',
{replaceAll: true})

const ground = viewer.addPluginSync(BaseGroundPlugin)
ground.mesh!.castShadow = false
ground.material!.roughness = 1
ground.material!.metalness = 0

const ui = viewer.addPluginSync(new TweakpaneUiPlugin(false))

await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
})

viewer.scene.envMapIntensity = 1

await viewer.setEnvironmentMap(hdris[0], {
setBackground: true,
})

ui.appendChild({
type: 'dropdown',
label: 'Environment Map',
children: hdris.map((url)=>({
label: url.split('/').pop()!.split('.').shift()!,
value: url,
})),
value: hdris[0],
onChange: async(ev)=>{
console.log(ev.value)
await viewer.setEnvironmentMap(ev.value, {
setBackground: true,
})
refreshHist()
},
})

let histogram2 = createHistogramFromImage(viewer.scene.environment?.image)
function refreshHist() {
histogram2 = createHistogramFromImage(viewer.scene.environment?.image)
}

viewer.addEventListener('postFrame', ()=>updateLight(viewer, directionalLight, histogram2))

ui.setupPluginUi(BaseGroundPlugin)
// const targetPreview = viewer.addPluginSync(new RenderTargetPreviewPlugin())
// targetPreview.addTarget(()=>directionalLight.shadow.map, 'shadow')

}

const extension: MaterialExtension = {
isCompatible: ()=> true,
computeCacheKey: ()=> 'aomap1',
shaderExtender(shader) {
shader.fragmentShader = shaderReplaceString(shader.fragmentShader, '#include <aomap_fragment>', `
#ifdef USE_AOMAP
// reads channel R, compatible with a combined OcclusionRoughnessMetallic (RGB) texture
float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0;
#else
const int ii = 0;
DirectionalLightShadow edls = directionalLightShadows[ ii ];
float ambientOcclusion = getShadow( directionalShadowMap[ ii ], edls.shadowMapSize, edls.shadowBias, edls.shadowRadius, vDirectionalShadowCoord[ ii ] );
#endif

reflectedLight.indirectDiffuse *= ambientOcclusion;

#if defined( USE_ENVMAP ) && defined( STANDARD )

float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );

reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );

#endif
`)
// shader.defines.USE_UV = ''
},
}

function createDirLight(viewer: ThreeViewer) {
const directionalLight = new DirectionalLight(0xffffff, 4)
directionalLight.position.set(-2, -2, 2)
directionalLight.lookAt(0, 0, 0)
directionalLight.color.set(0xffffff)
directionalLight.intensity = 0
directionalLight.castShadow = true
directionalLight.shadow.mapSize.setScalar(1024)
directionalLight.shadow.camera.near = 0.1
directionalLight.shadow.camera.far = 10
directionalLight.shadow.camera.top = 2
directionalLight.shadow.camera.bottom = -2
directionalLight.shadow.camera.left = -2
directionalLight.shadow.camera.right = 2
viewer.scene.addObject(directionalLight, {addToRoot: true})
// move to index 0 in parent.children, so that directionalLight always has index 0 in shader. required for material extension
const parent = directionalLight.parent!
const index = parent.children.indexOf(directionalLight)
if (index > 0) {
parent.children.splice(index, 1)
parent.children.unshift(directionalLight)
}

return directionalLight
}
function updateLight(viewer: ThreeViewer, directionalLight: DirectionalLight, histogram: ReturnType<typeof createHistogramFromImage>) {
if (viewer.renderManager.frameCount < 1) return
// if (viewer.renderManager.frameCount > 2) return
const bounds = viewer.scene.getBounds(false)
const size = bounds.getSize(new Vector3()).length()
const center = bounds.getCenter(new Vector3())

const i = viewer.renderManager.frameCount <= 1 ? histogram.brightestI : histogram.sampleIndex()
histogram.indexToColor(i, directionalLight)
directionalLight.intensity = 0 // so it doesnt show in the scene
histogram.indexToPosition(i, directionalLight.position).multiplyScalar(0.5 + size).add(center)
directionalLight.lookAt(center)
directionalLight.shadow.camera.near = Math.max(size / 100, 0.1)
directionalLight.shadow.camera.far = size * 2.5
directionalLight.shadow.camera.updateProjectionMatrix()
viewer.renderManager.resetShadows()
}

function sampleRandom2(pow = 2) {
return Math.max(0, Math.pow(Math.random(), pow) - 0.001)
}
function sampleRandom() {
return Math.max(0, Math.random() - 0.001)
}

const maxIntensityClamp = 50
const ignoreBottomBins = 1 // should be at-least 1 to ignore black pixels.
const numBins = 100 // Number of bins in the histogram (configurable)
const sampleRandPower = 1.25 // increase this to give more focus to higher intensity pixels. between 1 and 2
const topHalf = true // todo if this is true, half the shadow in shader?

function createHistogramFromImage(image: {data: Uint16Array, width: number, height: number}) {
const histogram: number[][] = []

let maxIntensity = -1
let brightestI = 0
// const maxIntensity1 = 65504
for (let i = 0; i < image.data.length / 4; i++) {
const r = DataUtils.fromHalfFloat(image.data[i * 4])
const g = DataUtils.fromHalfFloat(image.data[i * 4 + 1])
const b = DataUtils.fromHalfFloat(image.data[i * 4 + 2])
const a = DataUtils.fromHalfFloat(image.data[i * 4 + 3])
const intensity = a * Math.max(r, g, b) // Calculate intensity
const binIndex = Math.floor(numBins * Math.max(0, Math.min(1 - 0.001, intensity / maxIntensityClamp))) // Calculate the bin index
histogram[binIndex] ||= []
histogram[binIndex].push(i)
if (maxIntensity < intensity) {
maxIntensity = intensity
brightestI = i
}
if (topHalf && i > image.data.length / 8) break
}
histogram.reverse()
const cdf = histogram.map((bin) => bin ? bin.length : 0)
const maxW = numBins - 1 - ignoreBottomBins + 1
cdf[0] = cdf[0] * maxW
for (let i = 1; i < numBins; i++) {
cdf[i] = cdf[i - 1] + (cdf[i] || 0) * (maxW - i) // *i for intensity of that bin
}
console.log(cdf)
return {
histogram, cdf,
brightestI,
maxIntensity,
sampleIndex: ()=>{
const max = cdf[cdf.length - 1]
const r = sampleRandom2(sampleRandPower) * max
const binIndex = cdf.findIndex((value) => value >= r)
const bin = histogram[binIndex]
const index = Math.floor(bin.length * sampleRandom())
return bin[index]
},
indexToPosition: (i: number, position: Vector3)=>{
// todo handle envMapRotation
const {width, height} = image
const x = i % width / width
const y = 1 - Math.floor(i / width) / height
const phi = Math.PI * (x * 2 - 1)
const theta = Math.PI * 0.5 * (y * 2 - 1)
return position.set(
Math.cos(theta) * Math.cos(phi),
Math.sin(theta),
Math.cos(theta) * Math.sin(phi),
)
},
indexToColor: (i: number, light: {color: Color, intensity: number})=>{
// todo handle envMapIntensity
const r = DataUtils.fromHalfFloat(image.data[i * 4])
const g = DataUtils.fromHalfFloat(image.data[i * 4 + 1])
const b = DataUtils.fromHalfFloat(image.data[i * 4 + 2])
const a = DataUtils.fromHalfFloat(image.data[i * 4 + 3])
light.color.setRGB(Math.min(1, r * a), Math.min(1, g * a), Math.min(1, b * a))
light.intensity = Math.min(a * Math.max(r, g, b), maxIntensityClamp)
},
}
}

init().finally(_testFinish)

Loading…
Откажи
Сачувај