Przeglądaj źródła

Screen Space Curvature (#8562)

* Proposed PrePass changes

* Screen Space Curvature

* fix lint

* nit
sebavan 5 lat temu
rodzic
commit
57bf524970

+ 1 - 0
dist/preview release/what's new.md

@@ -199,6 +199,7 @@
 ### Post Processes
 
 - SSAO 2 is now using the brand new `PrePassRenderer` to avoid rendering the scene twice ([CraigFeldpsar](https://github.com/craigfeldspar)
+- Added Screen Space Curvature post process ([Popov72](https://github.com/Popov72) and [Sebavan](https://github.com/sebavan/))
 
 ## Bugs
 

+ 2 - 1
src/PostProcesses/index.ts

@@ -29,4 +29,5 @@ export * from "./tonemapPostProcess";
 export * from "./volumetricLightScatteringPostProcess";
 export * from "./vrDistortionCorrectionPostProcess";
 export * from "./vrMultiviewToSingleviewPostProcess";
-export * from "./screenSpaceReflectionPostProcess";
+export * from "./screenSpaceReflectionPostProcess";
+export * from "./screenSpaceCurvaturePostProcess";

+ 75 - 0
src/PostProcesses/screenSpaceCurvaturePostProcess.ts

@@ -0,0 +1,75 @@
+import { Nullable } from "../types";
+import { Logger } from "../Misc/logger";
+import { Camera } from "../Cameras/camera";
+import { Effect } from "../Materials/effect";
+import { PostProcess, PostProcessOptions } from "./postProcess";
+import { Constants } from "../Engines/constants";
+import { GeometryBufferRenderer } from "../Rendering/geometryBufferRenderer";
+import { Scene } from "../scene";
+
+import '../Rendering/geometryBufferRendererSceneComponent';
+import "../Shaders/screenSpaceCurvature.fragment";
+import { EngineStore } from '../Engines/engineStore';
+
+declare type Engine = import("../Engines/engine").Engine;
+
+/**
+ * The Screen Space curvature effect can help highlighting ridge and valley of a model.
+ */
+export class ScreenSpaceCurvaturePostProcess extends PostProcess {
+    /**
+     * Defines how much ridge the curvature effect displays.
+     */
+    public ridge: number = 1;
+
+    /**
+     * Defines how much valley the curvature effect displays.
+     */
+    public valley: number = 1;
+
+    private _geometryBufferRenderer: Nullable<GeometryBufferRenderer>;
+
+    /**
+     * Creates a new instance ScreenSpaceCurvaturePostProcess
+     * @param name The name of the effect.
+     * @param scene The scene containing the objects to blur according to their velocity.
+     * @param options The required width/height ratio to downsize to before computing the render pass.
+     * @param camera The camera to apply the render pass to.
+     * @param samplingMode The sampling mode to be used when computing the pass. (default: 0)
+     * @param engine The engine which the post process will be applied. (default: current engine)
+     * @param reusable If the post process can be reused on the same frame. (default: false)
+     * @param textureType Type of textures used when performing the post process. (default: 0)
+     * @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: false)
+     */
+    constructor(name: string, scene: Scene, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false) {
+        super(name, "screenSpaceCurvature", ["curvature_ridge", "curvature_valley"], ["textureSampler", "normalSampler"], options, camera, samplingMode, engine, reusable, undefined, textureType, undefined, null, blockCompilation);
+
+        this._geometryBufferRenderer = scene.enableGeometryBufferRenderer();
+
+        if (!this._geometryBufferRenderer) {
+            // Geometry buffer renderer is not supported. So, work as a passthrough.
+            Logger.Error("Multiple Render Target support needed for screen space curvature post process. Please use IsSupported test first.");
+        } else {
+            // Geometry buffer renderer is supported.
+            this.onApply = (effect: Effect) => {
+                effect.setFloat("curvature_ridge", 0.5 / Math.max(this.ridge * this.ridge, 1e-4));
+                effect.setFloat("curvature_valley", 0.7 / Math.max(this.valley * this.valley, 1e-4));
+
+                const normalTexture = this._geometryBufferRenderer!.getGBuffer().textures[1];
+                effect.setTexture("normalSampler", normalTexture);
+            };
+        }
+    }
+
+    /**
+     * Support test.
+     */
+    public static get IsSupported(): boolean {
+        var engine = EngineStore.LastCreatedEngine;
+        if (!engine) {
+            return false;
+        }
+
+        return engine.webGLVersion > 1 || engine.getCaps().drawBuffersExtension;
+    }
+}

+ 16 - 1
src/Rendering/geometryBufferRenderer.ts

@@ -13,10 +13,13 @@ import { AbstractMesh } from "../Meshes/abstractMesh";
 import { Color4 } from '../Maths/math.color';
 import { StandardMaterial } from '../Materials/standardMaterial';
 import { PBRMaterial } from '../Materials/PBR/pbrMaterial';
+import { _DevTools } from '../Misc/devTools';
+import { Observer } from '../Misc/observable';
+import { Engine } from '../Engines/engine';
+import { Nullable } from '../types';
 
 import "../Shaders/geometry.fragment";
 import "../Shaders/geometry.vertex";
-import { _DevTools } from '../Misc/devTools';
 
 /** @hidden */
 interface ISavedTransformationMatrix {
@@ -66,6 +69,7 @@ export class GeometryBufferRenderer {
     public renderTransparentMeshes = true;
 
     private _scene: Scene;
+    private _resizeObserver: Nullable<Observer<Engine>> = null;
     private _multiRenderTarget: MultiRenderTarget;
     private _ratio: number;
     private _enablePosition: boolean = false;
@@ -353,6 +357,11 @@ export class GeometryBufferRenderer {
      * Disposes the renderer and frees up associated resources.
      */
     public dispose(): void {
+        if (this._resizeObserver) {
+            const engine = this._scene.getEngine();
+            engine.onResizeObservable.remove(this._resizeObserver);
+            this._resizeObserver = null;
+        }
         this.getGBuffer().dispose();
     }
 
@@ -392,6 +401,12 @@ export class GeometryBufferRenderer {
             engine.clear(new Color4(0.0, 0.0, 0.0, 1.0), true, true, true);
         });
 
+        this._resizeObserver = engine.onResizeObservable.add(() => {
+            if (this._multiRenderTarget) {
+                this._multiRenderTarget.resize({ width: engine.getRenderWidth() * this._ratio, height: engine.getRenderHeight() * this._ratio });
+            }
+        });
+
         // Custom render function
         var renderSubMesh = (subMesh: SubMesh): void => {
             var renderingMesh = subMesh.getRenderingMesh();

+ 49 - 0
src/Shaders/screenSpaceCurvature.fragment.fx

@@ -0,0 +1,49 @@
+// From Blender sources: https://developer.blender.org/D3617#change-jG1fqR1RpokL
+// Forum request: https://forum.babylonjs.com/t/cavity-shader-effect-like-blender-2-8-viewport-for-babylon-js/11789
+precision highp float;
+
+// Samplers
+varying vec2 vUV;
+uniform sampler2D textureSampler;
+uniform sampler2D normalSampler;
+
+uniform float curvature_ridge;
+uniform float curvature_valley;
+
+#ifndef CURVATURE_OFFSET
+    #define CURVATURE_OFFSET 1
+#endif
+
+float curvature_soft_clamp(float curvature, float control)
+{
+    if (curvature < 0.5 / control)
+        return curvature * (1.0 - curvature * control);
+    return 0.25 / control;
+}
+
+float calculate_curvature(ivec2 texel, float ridge, float valley)
+{
+    vec2 normal_up    = texelFetchOffset(normalSampler, texel, 0, ivec2(0,  CURVATURE_OFFSET)).rb;
+    vec2 normal_down  = texelFetchOffset(normalSampler, texel, 0, ivec2(0, -CURVATURE_OFFSET)).rb;
+    vec2 normal_left  = texelFetchOffset(normalSampler, texel, 0, ivec2(-CURVATURE_OFFSET, 0)).rb;
+    vec2 normal_right = texelFetchOffset(normalSampler, texel, 0, ivec2( CURVATURE_OFFSET, 0)).rb;
+
+    float normal_diff = ((normal_up.g - normal_down.g) + (normal_right.r - normal_left.r));
+
+    if (normal_diff < 0.0)
+        return -2.0 * curvature_soft_clamp(-normal_diff, valley);
+
+    return 2.0 * curvature_soft_clamp(normal_diff, ridge);
+}
+
+void main(void) 
+{
+    ivec2 texel = ivec2(gl_FragCoord.xy);
+
+    vec4 baseColor = texture2D(textureSampler, vUV);
+
+    float curvature = calculate_curvature(texel, curvature_ridge, curvature_valley);
+    baseColor.rgb *= curvature + 1.0;
+
+    gl_FragColor = baseColor;
+}