threepipe
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

screen-pass.md 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. ---
  2. prev:
  3. text: 'Plugin System'
  4. link: './plugin-system'
  5. next:
  6. text: 'Screen Pass'
  7. link: './screen-pass'
  8. ---
  9. # Screen Pass - Extensions and Shaders
  10. 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.
  11. ## Overview
  12. The Screen Pass renders the final scene by processing the diffuse and transparent render targets. It supports:
  13. - Custom fragment shaders
  14. - Shader snippets for simple modifications
  15. - Material extensions for complex modifications
  16. - Built-in features like tonemapping, background clipping, and transparency handling
  17. 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.
  18. ## Basic Screen Shader
  19. The simplest way to customize the screen pass is by providing a shader snippet as a string:
  20. ```typescript
  21. const viewer = new ThreeViewer({
  22. canvas: document.getElementById('canvas'),
  23. screenShader: `
  24. // add a basic red tint
  25. diffuseColor *= vec4(1.0, 0.0, 0.0, 1.0);
  26. `
  27. })
  28. ```
  29. 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.
  30. **Live Example:** [Basic Screen Shader](https://threepipe.org/examples/#screen-shader/)
  31. ## Advanced Screen Shader with Parameters
  32. For more complex modifications, you can provide shader parameters and functions:
  33. ```typescript
  34. const viewer = new ThreeViewer({
  35. canvas: document.getElementById('canvas'),
  36. screenShader: {
  37. pars: ` // this is added before the main function
  38. uniform vec3 tintColor;
  39. vec4 applyTint(vec4 color) {
  40. return vec4(color.rgb * tintColor, color.a);
  41. }
  42. `,
  43. main: ` // this is added inside the main function
  44. diffuseColor = applyTint(diffuseColor);
  45. `
  46. }
  47. })
  48. // Add the uniform to the screen pass material
  49. viewer.renderManager.screenPass.material.uniforms.tintColor = {
  50. value: new Color(0, 0, 1) // blue tint
  51. }
  52. ```
  53. **Live Example:** [Advanced Screen Shader](https://threepipe.org/examples/#screen-shader-advanced/)
  54. ## Custom Screen Shader Material
  55. For complete control, you can provide a full shader material configuration:
  56. ```typescript
  57. const viewer = new ThreeViewer({
  58. canvas: document.getElementById('canvas'),
  59. tonemap: true,
  60. screenShader: new ExtendedShaderMaterial({
  61. ...CopyShader,
  62. // Custom fragment shader
  63. fragmentShader: `
  64. #include <packing>
  65. varying vec2 vUv;
  66. uniform vec3 tintColor;
  67. void main() {
  68. vec4 diffuseColor = tDiffuseTexelToLinear (texture2D(tDiffuse, vUv));
  69. #glMarker
  70. diffuseColor.rgb *= tintColor;
  71. gl_FragColor = diffuseColor;
  72. #include <colorspace_fragment>
  73. }
  74. `,
  75. uniforms: {
  76. tDiffuse: {value: null},
  77. tTransparent: {value: null},
  78. tintColor: {value: new Color(0, 1, 0)},
  79. },
  80. transparent: true,
  81. blending: NoBlending,
  82. side: FrontSide,
  83. }, ['tDiffuse', 'tTransparent'])
  84. })
  85. ```
  86. **Live Example:** [Custom Screen Shader Material](https://threepipe.org/examples/#screen-shader-material/)
  87. ## The #glMarker System
  88. The `#glMarker` is a special placeholder in the screen shader that allows plugins and extensions to inject their own code. This enables:
  89. 1. **Plugin Integration**: Plugins like tonemap, vignette, and film grain can modify the final image
  90. 2. **Extension Points**: Multiple extensions can modify the same shader without conflicts
  91. 3. **Shader Composition**: Complex effects can be built by combining multiple extensions
  92. When using custom screen shaders, include `#glMarker` to ensure compatibility with plugins:
  93. ```glsl
  94. void main() {
  95. vec4 diffuseColor = tDiffuseTexelToLinear (texture2D(tDiffuse, vUv));
  96. #glMarker // Plugin injection point
  97. // Your custom modifications
  98. diffuseColor.rgb *= tintColor;
  99. gl_FragColor = diffuseColor;
  100. }
  101. ```
  102. ## Screen Pass Material Extensions
  103. Material extensions provide the most flexible way to modify the screen pass. They allow you to:
  104. - Add custom uniforms
  105. - Inject shader code
  106. - Add defines
  107. - Hook into render events
  108. ```typescript
  109. const extension = {
  110. extraUniforms: {
  111. tintColor: {value: new Color(0, 1, 1)} // cyan tint
  112. },
  113. parsFragmentSnippet: ` // added before main function
  114. uniform vec3 tintColor;
  115. vec4 applyTint(vec4 color) {
  116. return vec4(color.rgb * tintColor, color.a);
  117. }
  118. `,
  119. shaderExtender: (shader, material, renderer) => {
  120. console.log('Patching shader')
  121. shader.fragmentShader = shaderReplaceString(
  122. shader.fragmentShader,
  123. '#glMarker',
  124. `diffuseColor = applyTint(diffuseColor);`,
  125. {prepend: true} // prepend to existing #glMarker content
  126. )
  127. },
  128. priority: 100, // execution order
  129. isCompatible: (material) => material.isShaderMaterial,
  130. computeCacheKey: (material) => 'tint-extension'
  131. }
  132. // Register the extension
  133. viewer.renderManager.screenPass.material.registerMaterialExtensions([extension])
  134. ```
  135. **Live Example:** [Screen Pass Extension](https://threepipe.org/examples/#screen-pass-extension/)
  136. ## Screen Pass Extension Plugins
  137. 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.
  138. ```typescript
  139. import {
  140. AScreenPassExtensionPlugin,
  141. Color,
  142. glsl,
  143. onChange,
  144. serialize,
  145. uiColor,
  146. uiFolderContainer,
  147. uiSlider,
  148. uiToggle,
  149. uniform,
  150. } from 'threepipe'
  151. @uiFolderContainer('Custom Tint Extension')
  152. export class CustomScreenPassExtensionPlugin extends AScreenPassExtensionPlugin {
  153. static readonly PluginType = 'CustomTint'
  154. // Define uniforms that will be available in the shader
  155. readonly extraUniforms = {
  156. tintIntensity: {value: 1},
  157. tintColor: {value: new Color(0xff0000)},
  158. } as const
  159. // Plugin properties with UI decorators
  160. @onChange(CustomScreenPassExtensionPlugin.prototype.setDirty)
  161. @uiToggle('Enable')
  162. @serialize() enabled: boolean = true
  163. @uiSlider('Intensity', [0.1, 4], 0.01)
  164. @uniform({propKey: 'tintIntensity'}) // Links to extraUniforms
  165. @serialize() intensity = 1
  166. @uiColor('Color')
  167. @uniform({propKey: 'tintColor'})
  168. @serialize('tintColor') color = new Color(0xff0000)
  169. /**
  170. * Priority determines the order of extension application
  171. * Lower values = applied later (after other extensions)
  172. */
  173. priority = -50
  174. /**
  175. * Add shader code before the main function
  176. * Use glsl`` template literal for syntax highlighting
  177. */
  178. parsFragmentSnippet = () => {
  179. if (this.isDisabled()) return ''
  180. return glsl`
  181. uniform float tintIntensity;
  182. uniform vec3 tintColor;
  183. vec4 ApplyTint(vec4 color) {
  184. return vec4(color.rgb * tintColor * tintIntensity, color.a);
  185. }
  186. `
  187. }
  188. /**
  189. * Shader code to inject at the #glMarker position
  190. */
  191. protected _shaderPatch = 'diffuseColor = ApplyTint(diffuseColor);'
  192. constructor(enabled = true) {
  193. super()
  194. this.enabled = enabled
  195. }
  196. }
  197. // Register the plugin
  198. const viewer = new ThreeViewer({
  199. canvas: document.getElementById('canvas'),
  200. plugins: [CustomScreenPassExtensionPlugin],
  201. })
  202. ```
  203. ### Key Features of Extension Plugins:
  204. 1. **Automatic UI Generation**: UI decorators create controls automatically
  205. 2. **Serialization**: Properties are saved/loaded with `@serialize()`
  206. 3. **Uniform Binding**: `@uniform()` decorator links properties to shader uniforms
  207. 4. **Change Detection**: `@onChange()` triggers updates when properties change
  208. 5. **Priority System**: Control the order of extension application
  209. 6. **Conditional Logic**: Use `isDisabled()` to conditionally apply effects
  210. ### Extension Plugin Methods:
  211. - `parsFragmentSnippet()`: Add code before the main function
  212. - `_shaderPatch`: Code to inject at #glMarker (can also be a function)
  213. - `isDisabled()`: Check if the extension should be applied
  214. - `setDirty()`: Mark the material for recompilation
  215. **Live Example:** [Screen Pass Extension Plugin](https://threepipe.org/examples/#screen-pass-extension-plugin/)
  216. ## Built-in Features
  217. ### Background Clipping
  218. Control background rendering with the `clipBackground` option:
  219. ```typescript
  220. // Enable background clipping
  221. viewer.renderManager.screenPass.clipBackground = true
  222. // Force background clipping (overrides the above which is also in the UI)
  223. viewer.renderManager.screenPass.clipBackgroundForce = true
  224. ```
  225. ### Output Color Space
  226. Configure the output color space for the final render:
  227. ```typescript
  228. import { SRGBColorSpace, LinearSRGBColorSpace } from 'threepipe'
  229. viewer.renderManager.screenPass.outputColorSpace = SRGBColorSpace
  230. ```
  231. ## Available Variables
  232. When writing custom screen shaders, these variables are available:
  233. - `diffuseColor`: The final pixel color (vec4)
  234. - `tDiffuse`: Main render target texture (sampler2D)
  235. - `vUv`: UV coordinates (vec2)
  236. - `transparentColor`: Transparent objects color (vec4)
  237. - `tTransparent`: Transparent render target texture (sampler2D)
  238. ### Working with G-Buffer
  239. When using the GBufferPlugin, additional variables become available:
  240. ::: details GBuffer Snippet
  241. ```glsl
  242. #ifdef HAS_GBUFFER
  243. float depth = getDepth(vUv);
  244. bool isBackground = depth > 0.99 && transparentColor.a < 0.001;
  245. #endif
  246. ```
  247. :::
  248. ```glsl
  249. // Use depth information for effects
  250. diffuseColor.rgb = mix(diffuseColor.rgb, fogColor.rgb, depth);
  251. ```
  252. ## Best Practices
  253. 1. **Always include #glMarker** in custom shaders to maintain plugin compatibility
  254. 2. **Use material extensions** for complex modifications that need to interact with other plugins
  255. 3. **Test with different plugins** to ensure compatibility
  256. 4. **Consider performance** when adding complex shader operations
  257. 5. **Use appropriate uniforms** instead of hardcoded values for dynamic effects
  258. ## Integration with Plugins
  259. Many built-in plugins extend the screen pass:
  260. - **TonemapPlugin**: Adds tone mapping to the final image
  261. - **VignettePlugin**: Adds vignette effect
  262. - **FilmGrainPlugin**: Adds film grain texture
  263. - **ChromaticAberrationPlugin**: Adds chromatic aberration
  264. These plugins use the material extension system to inject their effects at the `#glMarker` position, allowing them to work together seamlessly.