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

Support stencil and add examples.

master
Palash Bansal пре 11 месеци
родитељ
комит
29a191dc48
No account linked to committer's email address

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

@@ -383,7 +383,7 @@
<li><a href="./depthoffield-plugin/">DepthOfField Plugin </a></li>
<li><a href="./ssreflection-plugin/">Screen Space Reflection(SSR) Plugin </a></li>
<li><a href="./temporalaa-plugin/">Temporal Anti-aliasing(TAA) Plugin </a></li>
<li><a href="./outline-plugin/">Outline(Picking) Plugin </a></li>
<li><a href="./outline-plugin/">Outline (Picking) Plugin </a></li>
<li><a href="./ssgi-plugin/">Screen Space Global Illumination(SSGI) Plugin </a></li>
<li><a href="./ssgi-ssr-plugin/">SSGI + SSR Plugins </a></li>
<!--<li><a href="./ssrtao-plugin/">Screen Space Ray Traced AO Plugin </a></li>-->
@@ -498,7 +498,7 @@
<li><a href="./custom-bump-map-plugin/">Custom Bump Map Plugin </a></li>
<li><a href="./parallax-mapping-plugin/">Parallax(Relief) Mapping Plugin </a></li>
</ul>
<h2 class="category">Utils</h2>
<h2 class="category">Utils / Experiments</h2>
<ul>
<li><a href="./contact-shadow-ground-plugin/">Contact Shadow Ground Plugin</a></li>
<li><a href="./hdri-ground-plugin/">HDRi Ground Plugin <br/>(Projected Skybox)</a></li>
@@ -512,14 +512,13 @@
<li><a href="./obj-to-glb/">Convert OBJ to GLB </a></li>
<li><a href="./3dm-to-glb/">Convert 3DM to GLB </a></li>
<li><a href="./hdr-to-exr/">Convert HDR to EXR </a></li>
<li><a href="./fat-lines/">Fat Lines <br/>(Mesh Lines) </a></li>
<li><a href="./fat-line-spiral/">Line Spiral<br/>(Mesh Lines) </a></li>
</ul>
<h2 class="category">Experiments</h2>
<ul>
<li><a href="./fat-lines/">Fat Lines (Mesh Lines) </a></li>
<li><a href="./fat-line-spiral/">Line Spiral (Mesh Lines) </a></li>
<li><a href="./progressive-hdr-shadows-exp/">Progressive HDR Environment Shadows</a></li>
<li><a href="./multi-render-uv-clip/">Multi-render UV clipping <br/> (Material Extension) </a></li>
<li><a href="./svg-geometry-playground/">SVG Geometry Playground </a></li>
<li><a href="./stencil-clipping-portal/">Stencil Clipping Portal </a></li>
<li><a href="./stencil-picking-outline/">Stencil Picking Outline </a></li>
</ul>
<h2 class="category">Shaders</h2>
<ul>

+ 37
- 0
examples/stencil-clipping-portal/index.html Прегледај датотеку

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stencil Clipping WebGL</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/global-loading.mjs"></script>
<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>

+ 87
- 0
examples/stencil-clipping-portal/script.ts Прегледај датотеку

@@ -0,0 +1,87 @@
import {
_testFinish,
_testStart,
IObject3D,
LoadingScreenPlugin,
OrbitControls3,
PickingPlugin,
ThreeViewer,
TransformControlsPlugin,
} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'
import * as THREE from 'threepipe'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
msaa: false, // todo this is not working here, but its working in the other stencil example.
rgbm: true,
plugins: [LoadingScreenPlugin, PickingPlugin, TransformControlsPlugin],
stencil: true,
})

const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true))
ui.setupPlugins(PickingPlugin, TransformControlsPlugin)

await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr', {
setBackground: true,
})
const result = await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
})

const model = result?.getObjectByName('node_damagedHelmet_-6514')
const config = model?.uiConfig
if (config) ui.appendChild(config)

const maskMaterial = new THREE.MeshBasicMaterial({
color: 0x222222,
stencilRef: 1,
depthWrite: false,
stencilWrite: true,
depthTest: true,
stencilFunc: THREE.AlwaysStencilFunc,
stencilZPass: THREE.ReplaceStencilOp,
})
const maskPlane = new THREE.Mesh(new THREE.PlaneGeometry(4, 4), maskMaterial)
viewer.scene.addObject(maskPlane)

const maskCube = new THREE.Mesh(new THREE.BoxGeometry(4, 4, 20), new THREE.MeshBasicMaterial({
color: 0xffffff,
stencilRef: 1,
depthWrite: false,
stencilWrite: true,
depthTest: true,
stencilFunc: THREE.AlwaysStencilFunc,
stencilZPass: THREE.ReplaceStencilOp,
colorWrite:false,
}))
maskCube.userData.bboxVisible = false
maskCube.userData.userSelectable = false
maskCube.position.set(0, 0, 10)
viewer.scene.addObject(maskCube)
maskPlane.addEventListener('objectUpdate', ()=>{
maskCube.position.set(maskPlane.position.x, maskPlane.position.y, maskPlane.position.z + 10)
})

const mat = model?.materials?.[0]
if (mat && model) {
mat.stencilWrite = true
mat.stencilRef = 1
mat.stencilFunc = THREE.LessEqualStencilFunc
model.renderOrder = 2
model.position.z = -0.4
}

viewer.getPlugin(PickingPlugin)?.setSelectedObject(model)

const controls = viewer.scene.mainCamera.controls as OrbitControls3
controls.minAzimuthAngle = -1.5
controls.maxAzimuthAngle = 1.5

}

_testStart()
init().finally(_testFinish)

+ 37
- 0
examples/stencil-picking-outline/index.html Прегледај датотеку

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stencil Picking Outline</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/global-loading.mjs"></script>
<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>

+ 143
- 0
examples/stencil-picking-outline/script.ts Прегледај датотеку

@@ -0,0 +1,143 @@
import {
_testFinish,
_testStart,
AlwaysStencilFunc,
BufferGeometry2,
DecrementStencilOp,
EqualStencilFunc,
IObject3D,
LoadingScreenPlugin,
Mesh2,
PickingPlugin,
ReplaceStencilOp,
shaderReplaceString,
ThreeViewer,
TransformControlsPlugin,
UnlitMaterial,
} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

// Custom outline for picking plugin by duplicating the model and using stencil buffer
async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
msaa: true,
rgbm: true,
plugins: [LoadingScreenPlugin, PickingPlugin, new TransformControlsPlugin(false)],
stencil: true,
})

const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true))
ui.setupPlugins(TransformControlsPlugin, PickingPlugin)

await Promise.all([
viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr', {
setBackground: false,
}),
viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/kira.glb', {
autoCenter: true,
autoScale: true,
autoScaleRadius: 6,
}),
])

const selectionMaterial = new UnlitMaterial({
color: 0xe98a65,
stencilRef: 1,
depthWrite: false,
stencilWrite: true,
depthTest: true,
stencilFunc: AlwaysStencilFunc,
stencilZPass: ReplaceStencilOp,
colorWrite: true,
})
selectionMaterial.registerMaterialExtensions([{
shaderExtender:(shader, _material, _renderer) => {
shader.vertexShader = shaderReplaceString(
shader.vertexShader,
'#include <begin_vertex>',
'\ntransformed += normal * 0.02;',
{append: true}
)
},
isCompatible: ()=>true,
computeCacheKey: ()=> 'selectionMaterial',
}])
const selectionMesh = new Mesh2(new BufferGeometry2(), selectionMaterial)
const selectedObjectUpdateListener = (ev: {object: IObject3D})=>{
if (ev.object !== selected) return
selected.updateMatrixWorld()
selected.matrixWorld.decompose(
selectionMesh.position,
selectionMesh.quaternion,
selectionMesh.scale,
)
selectionMesh.updateMatrixWorld()
// const selectionScale = 1.1
// const center = new Box3B().expandByObject(selectionMesh).getCenter(new Vector3()).sub(selectionMesh.position)
// const m = new Matrix4().makeTranslation(new Vector3().copy(center).negate())
// .multiply(new Matrix4().makeScale(selectionScale, selectionScale, selectionScale))
// .multiply(new Matrix4().makeTranslation(new Vector3().copy(center).multiplyScalar(1 / selectionScale)))
// selectionMesh.matrix.premultiply(m).decompose(selectionMesh.position, selectionMesh.quaternion, selectionMesh.scale)
}
let selected = undefined as IObject3D | undefined
let lastState = null as any
viewer.getPlugin(PickingPlugin)!.addEventListener('selectedObjectChanged', ()=>{
const model = viewer.getPlugin(PickingPlugin)?.getSelectedObject<IObject3D>()
const geometry = model?.geometry
if (selected === model) return
if (selected) {
// remove selection mesh from previous selected object
selected.removeEventListener('objectUpdate', selectedObjectUpdateListener)
const lastMaterial = selected.materials?.[0]
if (lastMaterial && lastState) {
lastMaterial.stencilWrite = lastState.stencilWrite
lastMaterial.stencilRef = lastState.stencilRef
lastMaterial.stencilFunc = lastState.stencilFunc
lastMaterial.stencilZPass = lastState.stencilZPass
lastMaterial.needsUpdate = true
selected.renderOrder = lastState.renderOrder
}
lastState = null
}
const material = model?.materials?.[0]
if (!model?.isObject3D || !geometry || !material) { // it can also be a selected material
selectionMesh.geometry = undefined as any
selectionMesh.removeFromParent()
return
}
if (!geometry) return

// Set selected object's material to use stencil buffer
lastState = {
stencilWrite: material.stencilWrite,
stencilRef: material.stencilRef,
stencilFunc: material.stencilFunc,
stencilZPass: material.stencilZPass,
renderOrder: model.renderOrder,
}
material.stencilWrite = true
material.stencilRef = 1
material.stencilFunc = EqualStencilFunc
material.stencilZPass = DecrementStencilOp
material.needsUpdate = true
model.renderOrder = 2

// Set selection mesh to match selected object
selected = model
selectionMesh.geometry = geometry
// add listeners to update selection mesh position when its moved
selected.addEventListener('objectUpdate', selectedObjectUpdateListener)
selectedObjectUpdateListener({object: selected})

if (!selectionMesh.parent) viewer.scene.addObject(selectionMesh, {addToRoot: true}) // add to root so it is not saved
})
viewer.getPlugin(PickingPlugin)!.widgetEnabled = false

const chair = viewer.scene.getObjectByName('Node-Mesh003_1')
viewer.getPlugin(PickingPlugin)!.setSelectedObject(chair, true)
}

_testStart()
init().finally(_testFinish)

+ 3
- 2
src/rendering/RenderManager.ts Прегледај датотеку

@@ -139,7 +139,7 @@ export class RenderManager<TE extends IRenderManagerEventMap = IRenderManagerEve
// this._xrPreAnimationLoop = this._xrPreAnimationLoop.bind(this)
this._renderSize = new Vector2(canvas.clientWidth, canvas.clientHeight)
this._renderScale = renderScale
this._renderer = this._initWebGLRenderer(canvas, alpha)
this._renderer = this._initWebGLRenderer(canvas, alpha, targetOptions?.stencilBuffer ?? false)
this._context = this._renderer.getContext()
this._isWebGL2 = this._renderer.capabilities.isWebGL2
if (!this._isWebGL2) console.error('RenderManager: WebGL 1 is not officially supported anymore. Some features may not work.')
@@ -152,7 +152,7 @@ export class RenderManager<TE extends IRenderManagerEventMap = IRenderManagerEve
// if (animationLoop) this.addEventListener('animationLoop', animationLoop) // todo: from viewer
}

protected _initWebGLRenderer(canvas: HTMLCanvasElement, alpha: boolean): IWebGLRenderer<this> {
protected _initWebGLRenderer(canvas: HTMLCanvasElement, alpha: boolean, stencil: boolean): IWebGLRenderer<this> {
const renderer = new WebGLRenderer({
canvas,
antialias: false,
@@ -160,6 +160,7 @@ export class RenderManager<TE extends IRenderManagerEventMap = IRenderManagerEve
premultipliedAlpha: false, // todo: see this, maybe use this with rgbm mode.
preserveDrawingBuffer: true,
powerPreference: RenderManager.POWER_PREFERENCE,
stencil,
})
// renderer.info.autoReset = false // Not supported by ExtendedRenderPass


+ 7
- 0
src/rendering/RenderTarget.ts Прегледај датотеку

@@ -80,7 +80,14 @@ export interface CreateRenderTargetOptions {
colorSpace?: ColorSpace
type?: TextureDataType
format?: number
/**
* @default true
*/
depthBuffer?: boolean
/**
* @default false
*/
stencilBuffer?: boolean
depthTexture?: boolean
depthTextureType?: typeof UnsignedShortType | typeof UnsignedInt248Type | typeof UnsignedIntType | typeof FloatType
depthTextureFormat?: typeof DepthFormat | typeof DepthStencilFormat

+ 2
- 1
src/rendering/RenderTargetManager.ts Прегледај датотеку

@@ -57,6 +57,7 @@ export abstract class RenderTargetManager<TE extends object = object> extends Ev
colorSpace = NoColorSpace,
type = UnsignedByteType,
format = RGBAFormat,
stencilBuffer = false,
depthBuffer = true,
depthTexture = false,
depthTextureType = UnsignedIntType,
@@ -78,7 +79,7 @@ export abstract class RenderTargetManager<TE extends object = object> extends Ev
height: size.height,
count: textureCount,
} : size,
{samples, colorSpace, type, format, depthBuffer, depthTexture: depthTex},
{samples, colorSpace, type, format, depthBuffer, depthTexture: depthTex, stencilBuffer},
textureCount > 1 ? WebGLMultipleRenderTargets as any : WebGLRenderTarget)
this._processNewTarget(target, sizeMultiplier, trackTarget)
this._setTargetOptions(target, op)

+ 6
- 0
src/viewer/ThreeViewer.ts Прегледај датотеку

@@ -165,6 +165,11 @@ export interface ThreeViewerOptions {
* @default 1
*/
modelRootScale?: number
/**
* Enable stencil in renderer and stencilBuffer in composer render targets.
* @default false
*/
stencil?: boolean

debug?: boolean

@@ -455,6 +460,7 @@ export class ThreeViewer extends EventDispatcher<Record<IViewerEventTypes, IView
rgbm: options.rgbm ?? options.useRgbm ?? true,
zPrepass: options.zPrepass ?? options.useGBufferDepth ?? false,
depthBuffer: !(options.zPrepass ?? options.useGBufferDepth ?? false),
stencilBuffer: options.stencil,
screenShader: options.screenShader,
renderScale: typeof options.renderScale === 'string' ? options.renderScale === 'auto' ?
Math.min(options.maxRenderScale || 2, window.devicePixelRatio) : parseFloat(options.renderScale) :

+ 3
- 1
src/viewer/ViewerRenderManager.ts Прегледај датотеку

@@ -11,6 +11,7 @@ export interface ViewerRenderManagerOptions extends IRenderManagerOptions {
rgbm?: boolean,
msaa?: boolean | number,
depthBuffer?: boolean,
stencilBuffer?: boolean,
zPrepass?: boolean,
screenShader?: TViewerScreenShader
maxHDRIntensity?: number
@@ -38,7 +39,7 @@ export class ViewerRenderManager extends RenderManager<ViewerRenderManagerEventM

static DEFAULT_MSAA_SAMPLES = 4

constructor({rgbm = true, msaa = false, depthBuffer = false, ...options}: ViewerRenderManagerOptions) {
constructor({rgbm = true, msaa = false, depthBuffer = true, stencilBuffer = false, ...options}: ViewerRenderManagerOptions) {
super({
...options,
targetOptions: {
@@ -47,6 +48,7 @@ export class ViewerRenderManager extends RenderManager<ViewerRenderManagerEventM
colorSpace: rgbm ? RGBM16ColorSpace : NoColorSpace,
type: rgbm ? UnsignedByteType : HalfFloatType,
depthBuffer: depthBuffer,
stencilBuffer: stencilBuffer,
generateMipmaps: /* msaa ? true : */false, // todo: hack for now, fix blurTransmissionTarget in ExtendedRenderPass
minFilter: /* msaa ? LinearMipMapLinearFilter : */LinearFilter, // todo: hack for now, fix blurTransmissionTarget in ExtendedRenderPass
},

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