threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
96 lines (79 loc) • 4.28 kB
text/typescript
import {Color, Material, Texture, WebGLMultipleRenderTargets, WebGLRenderTarget} from 'three'
import {RenderPass} from 'three/examples/jsm/postprocessing/RenderPass.js'
import {IPassID, IPipelinePass} from './Pass'
import {ICamera, IMaterial, IRenderManager, IScene, IWebGLRenderer, PhysicalMaterial} from '../core'
import {uiFolderContainer, UiObjectConfig, uiToggle} from 'uiconfig.js'
import {getOrCall, ValOrFunc} from 'ts-browser-helpers'
<GBufferRenderPass>((c)=>c.passId + ' Render Pass')
export class GBufferRenderPass<TP extends IPassID=IPassID, T extends WebGLMultipleRenderTargets | WebGLRenderTarget|undefined=WebGLMultipleRenderTargets | WebGLRenderTarget> extends RenderPass implements IPipelinePass<TP> { // todo: extend from jittered?
readonly isGBufferRenderPass = true
uiConfig: UiObjectConfig
('Enabled') enabled = true
declare scene?: IScene
before?: IPassID[]
after?: IPassID[]
required?: IPassID[]
constructor(public readonly passId: TP, public target: ValOrFunc<T>, material: Material, clearColor: Color = new Color(1, 1, 1), clearAlpha = 1) {
super(undefined, undefined, material, clearColor, clearAlpha)
}
private _transparentMats = new Set<IMaterial>()
private _transmissiveMats = new Set<[IMaterial, number]>()
// todo make a global parameter in the viewer to be able to render all transparent and transmissive materials to gbuffer by default
preprocessMaterial = (material: IMaterial, renderToGBuffer?: boolean) => {
renderToGBuffer = renderToGBuffer ?? material.userData.renderToGBuffer
if (material.userData.pluginsDisabled) renderToGBuffer = false
if (
material.transparent && (renderToGBuffer || material.opacity > 0.99 && !material.map && !material.alphaMap) || // transparent and render to gbuffer
!material.transparent && !material.transmission && renderToGBuffer === false // opaque and dont render to gbuffer
) {
this._transparentMats.add(material)
material.transparent = !material.transparent
// material.needsUpdate = true
}
if (
material.transmission &&
Math.abs(material.transmission || 0) > 0 && renderToGBuffer // transmission and render to gbuffer
) {
this._transmissiveMats.add([material, material.transmission])
material.transmission = 0
// material.needsUpdate = true
}
}
/**
* Renders to {@link target}
* @param renderer
* @param _ - this is ignored
* @param _1 - this is ignored
* @param deltaTime
* @param maskActive
*/
render(renderer: IWebGLRenderer, _?: WebGLRenderTarget<Texture|Texture[]>|null, _1?: WebGLRenderTarget<Texture|Texture[]>, deltaTime?: number, maskActive?: boolean) {
if (!this.scene || !this.camera) return
const t = renderer.getRenderTarget()
const activeCubeFace = renderer.getActiveCubeFace()
const activeMipLevel = renderer.getActiveMipmapLevel()
this.scene.traverse(({material}) => {
if (!material) return
if (Array.isArray(material)) material.forEach((m)=>this.preprocessMaterial(m))
else this.preprocessMaterial(material)
})
// todo; copy double sided, check with post processing
renderer.renderWithModes({
shadowMapRender: false,
backgroundRender: false,
opaqueRender: true,
transparentRender: false,
transmissionRender: false,
mainRenderPass: false,
}, ()=> super.render(renderer, null, getOrCall(this.target), deltaTime as any, maskActive as any)) // here this.target is the write-buffer, variable writeBuffer is ignored
this._transparentMats.forEach(m => m.transparent = !m.transparent)
this._transparentMats.clear()
this._transmissiveMats.forEach(([m, tr]: [PhysicalMaterial, number]) => m.transmission = tr)
this._transmissiveMats.clear()
renderer.setRenderTarget(t, activeCubeFace, activeMipLevel)
}
beforeRender(scene: IScene, camera: ICamera, _: IRenderManager): void {
this.scene = scene
this.camera = camera
}
}