Ver Fonte

wip, ssao

Alexander Rose há 6 anos atrás
pai
commit
0b6dfa8a1f

+ 45 - 4
src/mol-canvas3d/canvas3d.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -7,7 +7,7 @@
 import { BehaviorSubject, Subscription } from 'rxjs';
 import { now } from 'mol-util/now';
 
-import { Vec3 } from 'mol-math/linear-algebra'
+import { Vec3, Vec2 } from 'mol-math/linear-algebra'
 import InputObserver, { ModifiersKeys, ButtonsType } from 'mol-util/input/input-observer'
 import Renderer, { RendererStats, RendererParams } from 'mol-gl/renderer'
 import { GraphicsRenderObject } from 'mol-gl/render-object'
@@ -29,6 +29,9 @@ import { BoundingSphereHelper, DebugHelperParams } from './helper/bounding-spher
 import { decodeFloatRGB } from 'mol-util/float-packing';
 import { SetUtils } from 'mol-util/set';
 import { Canvas3dInteractionHelper } from './helper/interaction-events';
+import { createTexture } from 'mol-gl/webgl/texture';
+import { ValueCell } from 'mol-util';
+import { getSSAOPassRenderable, SSAOPassParams } from './passes/ssao-pass';
 
 export const Canvas3DParams = {
     // TODO: FPS cap?
@@ -37,6 +40,8 @@ export const Canvas3DParams = {
     cameraClipDistance: PD.Numeric(0, { min: 0.0, max: 50.0, step: 0.1 }, { description: 'The distance between camera and scene at which to clip regardless of near clipping plane.' }),
     clip: PD.Interval([1, 100], { min: 1, max: 100, step: 1 }),
     fog: PD.Interval([50, 100], { min: 1, max: 100, step: 1 }),
+
+    ambientOcclusion: PD.Group(SSAOPassParams),
     renderer: PD.Group(RendererParams),
     trackball: PD.Group(TrackballControlsParams),
     debug: PD.Group(DebugHelperParams)
@@ -117,6 +122,12 @@ namespace Canvas3D {
         const controls = TrackballControls.create(input, camera, p.trackball)
         const renderer = Renderer.create(webgl, camera, p.renderer)
 
+        const drawTarget = createRenderTarget(webgl, canvas.width, canvas.height)
+        const depthTexture = createTexture(webgl, 'image-depth', 'depth', 'ushort', 'nearest')
+        depthTexture.define(canvas.width, canvas.height)
+        depthTexture.attachFramebuffer(drawTarget.framebuffer, 'depth')
+        const ssaoPass = getSSAOPassRenderable(webgl, drawTarget.texture, depthTexture, p.ambientOcclusion)
+
         let pickScale = 0.25 / webgl.pixelRatio
         let pickWidth = Math.round(canvas.width * pickScale)
         let pickHeight = Math.round(canvas.height * pickScale)
@@ -215,13 +226,18 @@ namespace Canvas3D {
                         renderer.render(scene, 'pickGroup');
                         break;
                     case 'draw':
-                        webgl.unbindFramebuffer();
                         renderer.setViewport(0, 0, canvas.width, canvas.height);
-                        renderer.render(scene, variant);
+                        drawTarget.bind()
+                        renderer.render(scene, 'draw');
                         if (debugHelper.isEnabled) {
                             debugHelper.syncVisibility()
                             renderer.render(debugHelper.scene, 'draw')
                         }
+                        webgl.unbindFramebuffer();
+                        webgl.state.disable(webgl.gl.SCISSOR_TEST)
+                        webgl.state.disable(webgl.gl.BLEND)
+                        webgl.state.disable(webgl.gl.DEPTH_TEST)
+                        ssaoPass.render()
                         pickDirty = true
                         break;
                 }
@@ -395,6 +411,25 @@ namespace Canvas3D {
                 if (props.clip !== undefined) p.clip = [props.clip[0], props.clip[1]]
                 if (props.fog !== undefined) p.fog = [props.fog[0], props.fog[1]]
 
+                if (props.ambientOcclusion) {
+                    if (props.ambientOcclusion.enable !== undefined) {
+                        p.ambientOcclusion.enable = props.ambientOcclusion.enable
+                        ValueCell.update(ssaoPass.values.uEnable, props.ambientOcclusion.enable ? 1 : 0)
+                    }
+                    if (props.ambientOcclusion.kernelSize !== undefined) {
+                        p.ambientOcclusion.kernelSize = props.ambientOcclusion.kernelSize
+                        ValueCell.update(ssaoPass.values.uKernelSize, props.ambientOcclusion.kernelSize)
+                    }
+                    if (props.ambientOcclusion.bias !== undefined) {
+                        p.ambientOcclusion.bias = props.ambientOcclusion.bias
+                        ValueCell.update(ssaoPass.values.uBias, props.ambientOcclusion.bias)
+                    }
+                    if (props.ambientOcclusion.radius !== undefined) {
+                        p.ambientOcclusion.radius = props.ambientOcclusion.radius
+                        ValueCell.update(ssaoPass.values.uRadius, props.ambientOcclusion.radius)
+                    }
+                }
+                
                 if (props.renderer) renderer.setProps(props.renderer)
                 if (props.trackball) controls.setProps(props.trackball)
                 if (props.debug) debugHelper.setProps(props.debug)
@@ -407,6 +442,8 @@ namespace Canvas3D {
                     cameraClipDistance: p.cameraClipDistance,
                     clip: p.clip,
                     fog: p.fog,
+
+                    ambientOcclusion: { ...p.ambientOcclusion },
                     renderer: { ...renderer.props },
                     trackball: { ...controls.props },
                     debug: { ...debugHelper.props }
@@ -438,6 +475,10 @@ namespace Canvas3D {
             Viewport.set(camera.viewport, 0, 0, canvas.width, canvas.height)
             Viewport.set(controls.viewport, 0, 0, canvas.width, canvas.height)
 
+            drawTarget.setSize(canvas.width, canvas.height)
+            depthTexture.define(canvas.width, canvas.height)
+            ValueCell.update(ssaoPass.values.uTexSize, Vec2.set(ssaoPass.values.uTexSize.ref.value, canvas.width, canvas.height))
+
             pickScale = 0.25 / webgl.pixelRatio
             pickWidth = Math.round(canvas.width * pickScale)
             pickHeight = Math.round(canvas.height * pickScale)

+ 60 - 0
src/mol-canvas3d/passes/ssao-pass.ts

@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { QuadSchema, QuadValues } from 'mol-gl/compute/util';
+import { TextureSpec, Values, UniformSpec } from 'mol-gl/renderable/schema';
+import { ShaderCode } from 'mol-gl/shader-code';
+import { WebGLContext } from 'mol-gl/webgl/context';
+import { Texture } from 'mol-gl/webgl/texture';
+import { ValueCell } from 'mol-util';
+import { createComputeRenderItem } from 'mol-gl/webgl/render-item';
+import { createComputeRenderable } from 'mol-gl/renderable';
+import { Vec2 } from 'mol-math/linear-algebra';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
+
+const SSAOPassSchema = {
+    ...QuadSchema,
+    tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
+    tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
+    uTexSize: UniformSpec('v2'),
+
+    uEnable: UniformSpec('i'),
+    uKernelSize: UniformSpec('i'),
+    uBias: UniformSpec('f'),
+    uRadius: UniformSpec('f'),
+}
+
+export const SSAOPassParams = {
+    enable: PD.Boolean(true),
+    kernelSize: PD.Numeric(4, { min: 1, max: 100, step: 1 }),
+    bias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
+    radius: PD.Numeric(128, { min: 0, max: 256, step: 1 }),
+}
+export type SSAOPassProps = PD.Values<typeof SSAOPassParams>
+
+export function getSSAOPassRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture, props: Partial<SSAOPassProps>) {
+    const p = { ...PD.getDefaultValues(SSAOPassParams), props }
+    const values: Values<typeof SSAOPassSchema> = {
+        ...QuadValues,
+        tColor: ValueCell.create(colorTexture),
+        tDepth: ValueCell.create(depthTexture),
+        uTexSize: ValueCell.create(Vec2.create(colorTexture.width, colorTexture.height)),
+
+        uEnable: ValueCell.create(p.enable ? 1 : 0),
+        uKernelSize: ValueCell.create(p.kernelSize),
+        uBias: ValueCell.create(p.bias),
+        uRadius: ValueCell.create(p.radius),
+    }
+
+    const schema = { ...SSAOPassSchema }
+    const shaderCode = ShaderCode(
+        require('mol-gl/shader/quad.vert').default,
+        require('mol-gl/shader/passes/ssao.frag').default
+    )
+    const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
+
+    return createComputeRenderable(renderItem, values)
+}

+ 53 - 0
src/mol-gl/shader/passes/ssao.frag

@@ -0,0 +1,53 @@
+precision highp float;
+precision highp int;
+precision highp sampler2D;
+
+uniform sampler2D tColor;
+uniform sampler2D tDepth;
+uniform vec2 uTexSize;
+
+uniform int uEnable;
+uniform int uKernelSize;
+uniform float uBias;
+uniform float uRadius;
+
+const float noiseAmount = 0.0002;
+
+float noise(vec2 coords) {
+	float a = 12.9898;
+	float b = 78.233;
+	float c = 43758.5453;
+	float dt = dot(coords, vec2(a,b));
+	float sn = mod(dt, 3.14159);
+	
+	return fract(sin(sn) * c);
+}
+
+float calcSSAO(in vec2 coords, in float depth) {
+	float occlusionFactor = 0.0;
+	
+	for (int i = -uKernelSize; i <= uKernelSize; i++) {
+		for (int j = -uKernelSize; j <= uKernelSize; j++) {
+			vec2 coordsDelta = coords + uRadius / float(uKernelSize) * vec2(float(i) / uTexSize.x, float(j) / uTexSize.y);
+            coordsDelta += noiseAmount * (noise(coordsDelta) - 0.5) / uTexSize;
+            coordsDelta = clamp(coordsDelta, 0.5 / uTexSize, 1.0 - 1.0 / uTexSize);
+			if (texture(tDepth, coordsDelta).r < depth) occlusionFactor += 1.0;
+		}
+	}
+
+	return occlusionFactor / float((2 * uKernelSize + 1) * (2 * uKernelSize + 1));
+}
+
+void main(void) {
+	vec2 coords = gl_FragCoord.xy / uTexSize;
+	vec4 color = texture(tColor, coords);
+    float depth = texture(tDepth, coords).r;
+	
+	// calculate screen-space ambient occlusion
+	if ((uEnable != 0) && (depth != 1.0)) {
+		float occlusionFactor = calcSSAO(coords, depth);
+		color = mix(color, vec4(0.0, 0.0, 0.0, 1.0), uBias * occlusionFactor);
+	}
+	
+	gl_FragColor = color;
+}