|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- ---
- prev:
- text: 'Plugin System'
- link: './plugin-system'
-
- next:
- text: 'Screen Pass'
- link: './screen-pass'
- ---
-
- # Screen Pass - Extensions and Shaders
-
- The Screen Pass is the final rendering stage in Threepipe that outputs the rendered scene to the screen or a render target. It provides multiple ways to customize the final image through custom shaders, material extensions, and shader snippets.
-
- ## Overview
-
- The Screen Pass renders the final scene by processing the diffuse and transparent render targets. It supports:
- - Custom fragment shaders
- - Shader snippets for simple modifications
- - Material extensions for complex modifications
- - Built-in features like tonemapping, background clipping, and transparency handling
-
- Check out the [ScreenPass.glsl](https://github.com/repalash/threepipe/blob/master/src/postprocessing/ScreenPass.glsl) for the default fragment shader code used in the screen pass.
-
- ## Basic Screen Shader
-
- The simplest way to customize the screen pass is by providing a shader snippet as a string:
-
- ```typescript
- const viewer = new ThreeViewer({
- canvas: document.getElementById('canvas'),
- screenShader: `
- // add a basic red tint
- diffuseColor *= vec4(1.0, 0.0, 0.0, 1.0);
- `
- })
- ```
-
- This snippet is inserted at the `#glMarker` position in the default screen shader and can modify the `diffuseColor` variable which contains the final pixel color.
-
- **Live Example:** [Basic Screen Shader](https://threepipe.org/examples/#screen-shader/)
-
- ## Advanced Screen Shader with Parameters
-
- For more complex modifications, you can provide shader parameters and functions:
-
- ```typescript
- const viewer = new ThreeViewer({
- canvas: document.getElementById('canvas'),
- screenShader: {
- pars: ` // this is added before the main function
- uniform vec3 tintColor;
- vec4 applyTint(vec4 color) {
- return vec4(color.rgb * tintColor, color.a);
- }
- `,
- main: ` // this is added inside the main function
- diffuseColor = applyTint(diffuseColor);
- `
- }
- })
-
- // Add the uniform to the screen pass material
- viewer.renderManager.screenPass.material.uniforms.tintColor = {
- value: new Color(0, 0, 1) // blue tint
- }
- ```
-
- **Live Example:** [Advanced Screen Shader](https://threepipe.org/examples/#screen-shader-advanced/)
-
- ## Custom Screen Shader Material
-
- For complete control, you can provide a full shader material configuration:
-
- ```typescript
- const viewer = new ThreeViewer({
- canvas: document.getElementById('canvas'),
- tonemap: true,
- screenShader: new ExtendedShaderMaterial({
- ...CopyShader,
- // Custom fragment shader
- fragmentShader: `
- #include <packing>
-
- varying vec2 vUv;
- uniform vec3 tintColor;
-
- void main() {
- vec4 diffuseColor = tDiffuseTexelToLinear (texture2D(tDiffuse, vUv));
-
- #glMarker
-
- diffuseColor.rgb *= tintColor;
-
- gl_FragColor = diffuseColor;
- #include <colorspace_fragment>
- }
- `,
- uniforms: {
- tDiffuse: {value: null},
- tTransparent: {value: null},
- tintColor: {value: new Color(0, 1, 0)},
- },
- transparent: true,
- blending: NoBlending,
- side: FrontSide,
- }, ['tDiffuse', 'tTransparent'])
- })
- ```
-
- **Live Example:** [Custom Screen Shader Material](https://threepipe.org/examples/#screen-shader-material/)
-
- ## The #glMarker System
-
- The `#glMarker` is a special placeholder in the screen shader that allows plugins and extensions to inject their own code. This enables:
-
- 1. **Plugin Integration**: Plugins like tonemap, vignette, and film grain can modify the final image
- 2. **Extension Points**: Multiple extensions can modify the same shader without conflicts
- 3. **Shader Composition**: Complex effects can be built by combining multiple extensions
-
- When using custom screen shaders, include `#glMarker` to ensure compatibility with plugins:
-
- ```glsl
- void main() {
- vec4 diffuseColor = tDiffuseTexelToLinear (texture2D(tDiffuse, vUv));
-
- #glMarker // Plugin injection point
-
- // Your custom modifications
- diffuseColor.rgb *= tintColor;
-
- gl_FragColor = diffuseColor;
- }
- ```
-
- ## Screen Pass Material Extensions
-
- Material extensions provide the most flexible way to modify the screen pass. They allow you to:
- - Add custom uniforms
- - Inject shader code
- - Add defines
- - Hook into render events
-
- ```typescript
- const extension = {
- extraUniforms: {
- tintColor: {value: new Color(0, 1, 1)} // cyan tint
- },
- parsFragmentSnippet: ` // added before main function
- uniform vec3 tintColor;
- vec4 applyTint(vec4 color) {
- return vec4(color.rgb * tintColor, color.a);
- }
- `,
- shaderExtender: (shader, material, renderer) => {
- console.log('Patching shader')
- shader.fragmentShader = shaderReplaceString(
- shader.fragmentShader,
- '#glMarker',
- `diffuseColor = applyTint(diffuseColor);`,
- {prepend: true} // prepend to existing #glMarker content
- )
- },
- priority: 100, // execution order
- isCompatible: (material) => material.isShaderMaterial,
- computeCacheKey: (material) => 'tint-extension'
- }
-
- // Register the extension
- viewer.renderManager.screenPass.material.registerMaterialExtensions([extension])
- ```
-
- **Live Example:** [Screen Pass Extension](https://threepipe.org/examples/#screen-pass-extension/)
-
- ## Screen Pass Extension Plugins
-
- For more complex effects that need UI configuration and serialization, you can create a custom screen pass extension plugin using `AScreenPassExtensionPlugin`. This base class provides automatic UI generation, serialization, and integration with the plugin system.
-
- ```typescript
- import {
- AScreenPassExtensionPlugin,
- Color,
- glsl,
- onChange,
- serialize,
- uiColor,
- uiFolderContainer,
- uiSlider,
- uiToggle,
- uniform,
- } from 'threepipe'
-
- @uiFolderContainer('Custom Tint Extension')
- export class CustomScreenPassExtensionPlugin extends AScreenPassExtensionPlugin {
- static readonly PluginType = 'CustomTint'
-
- // Define uniforms that will be available in the shader
- readonly extraUniforms = {
- tintIntensity: {value: 1},
- tintColor: {value: new Color(0xff0000)},
- } as const
-
- // Plugin properties with UI decorators
- @onChange(CustomScreenPassExtensionPlugin.prototype.setDirty)
- @uiToggle('Enable')
- @serialize() enabled: boolean = true
-
- @uiSlider('Intensity', [0.1, 4], 0.01)
- @uniform({propKey: 'tintIntensity'}) // Links to extraUniforms
- @serialize() intensity = 1
-
- @uiColor('Color')
- @uniform({propKey: 'tintColor'})
- @serialize('tintColor') color = new Color(0xff0000)
-
- /**
- * Priority determines the order of extension application
- * Lower values = applied later (after other extensions)
- */
- priority = -50
-
- /**
- * Add shader code before the main function
- * Use glsl`` template literal for syntax highlighting
- */
- parsFragmentSnippet = () => {
- if (this.isDisabled()) return ''
-
- return glsl`
- uniform float tintIntensity;
- uniform vec3 tintColor;
- vec4 ApplyTint(vec4 color) {
- return vec4(color.rgb * tintColor * tintIntensity, color.a);
- }
- `
- }
-
- /**
- * Shader code to inject at the #glMarker position
- */
- protected _shaderPatch = 'diffuseColor = ApplyTint(diffuseColor);'
-
- constructor(enabled = true) {
- super()
- this.enabled = enabled
- }
- }
-
- // Register the plugin
- const viewer = new ThreeViewer({
- canvas: document.getElementById('canvas'),
- plugins: [CustomScreenPassExtensionPlugin],
- })
- ```
-
- ### Key Features of Extension Plugins:
-
- 1. **Automatic UI Generation**: UI decorators create controls automatically
- 2. **Serialization**: Properties are saved/loaded with `@serialize()`
- 3. **Uniform Binding**: `@uniform()` decorator links properties to shader uniforms
- 4. **Change Detection**: `@onChange()` triggers updates when properties change
- 5. **Priority System**: Control the order of extension application
- 6. **Conditional Logic**: Use `isDisabled()` to conditionally apply effects
-
- ### Extension Plugin Methods:
-
- - `parsFragmentSnippet()`: Add code before the main function
- - `_shaderPatch`: Code to inject at #glMarker (can also be a function)
- - `isDisabled()`: Check if the extension should be applied
- - `setDirty()`: Mark the material for recompilation
-
- **Live Example:** [Screen Pass Extension Plugin](https://threepipe.org/examples/#screen-pass-extension-plugin/)
-
- ## Built-in Features
-
- ### Background Clipping
-
- Control background rendering with the `clipBackground` option:
-
- ```typescript
- // Enable background clipping
- viewer.renderManager.screenPass.clipBackground = true
-
- // Force background clipping (overrides the above which is also in the UI)
- viewer.renderManager.screenPass.clipBackgroundForce = true
- ```
-
- ### Output Color Space
-
- Configure the output color space for the final render:
-
- ```typescript
- import { SRGBColorSpace, LinearSRGBColorSpace } from 'threepipe'
-
- viewer.renderManager.screenPass.outputColorSpace = SRGBColorSpace
- ```
-
- ## Available Variables
-
- When writing custom screen shaders, these variables are available:
-
- - `diffuseColor`: The final pixel color (vec4)
- - `tDiffuse`: Main render target texture (sampler2D)
- - `vUv`: UV coordinates (vec2)
- - `transparentColor`: Transparent objects color (vec4)
- - `tTransparent`: Transparent render target texture (sampler2D)
-
- ### Working with G-Buffer
-
- When using the GBufferPlugin, additional variables become available:
-
- ::: details GBuffer Snippet
- ```glsl
- #ifdef HAS_GBUFFER
- float depth = getDepth(vUv);
- bool isBackground = depth > 0.99 && transparentColor.a < 0.001;
- #endif
- ```
- :::
-
- ```glsl
- // Use depth information for effects
- diffuseColor.rgb = mix(diffuseColor.rgb, fogColor.rgb, depth);
- ```
-
- ## Best Practices
-
- 1. **Always include #glMarker** in custom shaders to maintain plugin compatibility
- 2. **Use material extensions** for complex modifications that need to interact with other plugins
- 3. **Test with different plugins** to ensure compatibility
- 4. **Consider performance** when adding complex shader operations
- 5. **Use appropriate uniforms** instead of hardcoded values for dynamic effects
-
- ## Integration with Plugins
-
- Many built-in plugins extend the screen pass:
-
- - **TonemapPlugin**: Adds tone mapping to the final image
- - **VignettePlugin**: Adds vignette effect
- - **FilmGrainPlugin**: Adds film grain texture
- - **ChromaticAberrationPlugin**: Adds chromatic aberration
-
- These plugins use the material extension system to inject their effects at the `#glMarker` position, allowing them to work together seamlessly.
|