Переглянути джерело

add smaa antialiasing option (new default)

Alexander Rose 4 роки тому
батько
коміт
9eba0b91a8

+ 26 - 5
src/mol-canvas3d/passes/postprocessing.ts

@@ -21,6 +21,7 @@ import quad_vert from '../../mol-gl/shader/quad.vert';
 import postprocessing_frag from '../../mol-gl/shader/postprocessing.frag';
 import { StereoCamera } from '../camera/stereo';
 import { FxaaParams, FxaaPass } from './fxaa';
+import { SmaaParams, SmaaPass } from './smaa';
 
 const PostprocessingSchema = {
     ...QuadSchema,
@@ -96,10 +97,11 @@ export const PostprocessingParams = {
         }),
         off: PD.Group({})
     }, { cycle: true, description: 'Draw outline around 3D objects' }),
-    antialiasing: PD.MappedStatic('on', {
-        on: PD.Group(FxaaParams),
+    antialiasing: PD.MappedStatic('smaa', {
+        fxaa: PD.Group(FxaaParams),
+        smaa: PD.Group(SmaaParams),
         off: PD.Group({})
-    }, { cycle: true, description: 'Smooth pixel edges' }),
+    }, { options: [['fxaa', 'FXAA'], ['smaa', 'SMAA'], ['off', 'Off']], description: 'Smooth pixel edges' }),
 };
 export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
 
@@ -113,6 +115,7 @@ export class PostprocessingPass {
     private readonly tmpTarget: RenderTarget
     private readonly renderable: PostprocessingRenderable
     private readonly fxaa: FxaaPass
+    private readonly smaa: SmaaPass
 
     constructor(private webgl: WebGLContext, private drawPass: DrawPass) {
         const { colorTarget, depthTexture } = drawPass;
@@ -123,6 +126,7 @@ export class PostprocessingPass {
         this.tmpTarget = webgl.createRenderTarget(width, height, false, 'uint8', 'linear');
         this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture);
         this.fxaa = new FxaaPass(webgl, this.tmpTarget.texture);
+        this.smaa = new SmaaPass(webgl, this.tmpTarget.texture);
     }
 
     syncSize() {
@@ -135,6 +139,8 @@ export class PostprocessingPass {
             this.tmpTarget.setSize(width, height);
             ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
             this.fxaa.setSize(width, height);
+
+            if (this.smaa.supported) this.smaa.setSize(width, height);
         }
     }
 
@@ -204,7 +210,7 @@ export class PostprocessingPass {
     }
 
     private _renderFxaa(camera: ICamera, toDrawingBuffer: boolean, props: PostprocessingProps) {
-        if (props.antialiasing.name !== 'on') return;
+        if (props.antialiasing.name !== 'fxaa') return;
 
         const input = (props.occlusion.name === 'on' || props.outline.name === 'on')
             ? this.tmpTarget.texture : this.drawPass.colorTarget.texture;
@@ -220,13 +226,28 @@ export class PostprocessingPass {
         this.fxaa.render();
     }
 
+    private _renderSmaa(camera: ICamera, toDrawingBuffer: boolean, props: PostprocessingProps) {
+        if (props.antialiasing.name !== 'smaa') return;
+
+        const input = (props.occlusion.name === 'on' || props.outline.name === 'on')
+            ? this.tmpTarget.texture : this.drawPass.colorTarget.texture;
+        this.smaa.update(input, props.antialiasing.params);
+
+        this.smaa.render(camera.viewport, toDrawingBuffer ? undefined : this.target);
+    }
+
     private _render(camera: ICamera, toDrawingBuffer: boolean, props: PostprocessingProps) {
         if (props.occlusion.name === 'on' || props.outline.name === 'on' || props.antialiasing.name === 'off') {
             this._renderPostprocessing(camera, toDrawingBuffer, props);
         }
 
-        if (props.antialiasing.name === 'on') {
+        if (props.antialiasing.name === 'fxaa') {
             this._renderFxaa(camera, toDrawingBuffer, props);
+        } else if (props.antialiasing.name === 'smaa') {
+            if (!this.smaa.supported) {
+                throw new Error('SMAA not supported, missing "HTMLImageElement"');
+            }
+            this._renderSmaa(camera, toDrawingBuffer, props);
         }
     }
 

Різницю між файлами не показано, бо вона завелика
+ 239 - 0
src/mol-canvas3d/passes/smaa.ts


+ 66 - 0
src/mol-gl/shader/smaa/blend.frag.ts

@@ -0,0 +1,66 @@
+/**
+ * Slightly adapted from https://github.com/mrdoob/three.js
+ * MIT License Copyright (c) 2010-2020 three.js authors
+ *
+ * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
+ * Preset: SMAA 1x Medium (with color edge detection)
+ * https://github.com/iryoku/smaa/releases/tag/v2.8
+ */
+
+export const blend_frag = `
+precision highp float;
+precision highp int;
+precision highp sampler2D;
+
+uniform sampler2D tWeights;
+uniform sampler2D tColor;
+uniform vec2 uTexSizeInv;
+
+varying vec2 vUv;
+varying vec4 vOffset[2];
+
+vec4 SMAANeighborhoodBlendingPS(vec2 texCoord, vec4 offset[2], sampler2D colorTex, sampler2D blendTex) {
+    // Fetch the blending weights for current pixel:
+    vec4 a;
+    a.xz = texture2D(blendTex, texCoord).xz;
+    a.y = texture2D(blendTex, offset[1].zw).g;
+    a.w = texture2D(blendTex, offset[1].xy).a;
+
+    // Is there any blending weight with a value greater than 0.0?
+    if (dot(a, vec4(1.0, 1.0, 1.0, 1.0)) < 1e-5) {
+        return texture2D(colorTex, texCoord, 0.0);
+    } else {
+        // Up to 4 lines can be crossing a pixel (one through each edge). We
+        // favor blending by choosing the line with the maximum weight for each
+        // direction:
+        vec2 offset;
+        offset.x = a.a > a.b ? a.a : -a.b; // left vs. right
+        offset.y = a.g > a.r ? -a.g : a.r; // top vs. bottom // WebGL port note: Changed signs
+
+        // Then we go in the direction that has the maximum weight:
+        if (abs(offset.x) > abs(offset.y)) { // horizontal vs. vertical
+            offset.y = 0.0;
+        } else {
+            offset.x = 0.0;
+        }
+
+        // Fetch the opposite color and lerp by hand:
+        vec4 C = texture2D(colorTex, texCoord, 0.0);
+        texCoord += sign(offset) * uTexSizeInv;
+        vec4 Cop = texture2D(colorTex, texCoord, 0.0);
+        float s = abs(offset.x) > abs(offset.y) ? abs(offset.x) : abs(offset.y);
+
+        // WebGL port note: Added gamma correction
+        C.xyz = pow(C.xyz, vec3(2.2));
+        Cop.xyz = pow(Cop.xyz, vec3(2.2));
+        vec4 mixed = mix(C, Cop, s);
+        mixed.xyz = pow(mixed.xyz, vec3(1.0 / 2.2));
+
+        return mixed;
+    }
+}
+
+void main() {
+    gl_FragColor = SMAANeighborhoodBlendingPS(vUv, vOffset, tColor, tWeights);
+}
+`;

+ 32 - 0
src/mol-gl/shader/smaa/blend.vert.ts

@@ -0,0 +1,32 @@
+/**
+ * Slightly adapted from https://github.com/mrdoob/three.js
+ * MIT License Copyright (c) 2010-2020 three.js authors
+ *
+ * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
+ * Preset: SMAA 1x Medium (with color edge detection)
+ * https://github.com/iryoku/smaa/releases/tag/v2.8
+ */
+
+export const blend_vert = `
+precision highp float;
+
+attribute vec2 aPosition;
+uniform vec2 uQuadScale;
+
+uniform vec2 uTexSizeInv;
+
+varying vec2 vUv;
+varying vec4 vOffset[2];
+
+void SMAANeighborhoodBlendingVS(vec2 texCoord) {
+    vOffset[0] = texCoord.xyxy + uTexSizeInv.xyxy * vec4(-1.0, 0.0, 0.0, 1.0); // WebGL port note: Changed sign in W component
+    vOffset[1] = texCoord.xyxy + uTexSizeInv.xyxy * vec4(1.0, 0.0, 0.0, -1.0); // WebGL port note: Changed sign in W component
+}
+
+void main() {
+    vUv = (aPosition + 1.0) * 0.5;
+    SMAANeighborhoodBlendingVS(vUv);
+    vec2 position = aPosition * uQuadScale - vec2(1.0, 1.0) + uQuadScale;
+    gl_Position = vec4(position, 0.0, 1.0);
+}
+`;

+ 76 - 0
src/mol-gl/shader/smaa/edges.frag.ts

@@ -0,0 +1,76 @@
+/**
+ * Slightly adapted from https://github.com/mrdoob/three.js
+ * MIT License Copyright (c) 2010-2020 three.js authors
+ *
+ * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
+ * Preset: SMAA 1x Medium (with color edge detection)
+ * https://github.com/iryoku/smaa/releases/tag/v2.8
+ */
+
+export const edges_frag = `
+precision highp float;
+precision highp int;
+precision highp sampler2D;
+
+uniform sampler2D tColor;
+uniform vec2 uTexSizeInv;
+
+varying vec2 vUv;
+varying vec4 vOffset[3];
+
+vec4 SMAAColorEdgeDetectionPS(vec2 texcoord, vec4 offset[3], sampler2D colorTex) {
+    vec2 threshold = vec2(dEdgeThreshold, dEdgeThreshold);
+
+    // Calculate color deltas:
+    vec4 delta;
+    vec3 C = texture2D(colorTex, texcoord).rgb;
+
+    vec3 Cleft = texture2D(colorTex, offset[0].xy).rgb;
+    vec3 t = abs(C - Cleft);
+    delta.x = max(max(t.r, t.g), t.b);
+
+    vec3 Ctop = texture2D(colorTex, offset[0].zw).rgb;
+    t = abs(C - Ctop);
+    delta.y = max(max(t.r, t.g), t.b);
+
+    // We do the usual threshold:
+    vec2 edges = step(threshold, delta.xy);
+
+    // Then discard if there is no edge:
+    if (dot(edges, vec2(1.0, 1.0 )) == 0.0)
+        discard;
+
+    // Calculate right and bottom deltas:
+    vec3 Cright = texture2D(colorTex, offset[1].xy).rgb;
+    t = abs( C - Cright );
+    delta.z = max(max(t.r, t.g), t.b);
+
+    vec3 Cbottom  = texture2D(colorTex, offset[1].zw).rgb;
+    t = abs(C - Cbottom);
+    delta.w = max(max(t.r, t.g), t.b);
+
+    // Calculate the maximum delta in the direct neighborhood:
+    float maxDelta = max(max(max(delta.x, delta.y), delta.z), delta.w );
+
+    // Calculate left-left and top-top deltas:
+    vec3 Cleftleft  = texture2D(colorTex, offset[2].xy).rgb;
+    t = abs( C - Cleftleft );
+    delta.z = max(max(t.r, t.g), t.b);
+
+    vec3 Ctoptop = texture2D(colorTex, offset[2].zw).rgb;
+    t = abs(C - Ctoptop);
+    delta.w = max(max(t.r, t.g), t.b);
+
+    // Calculate the final maximum delta:
+    maxDelta = max(max(maxDelta, delta.z), delta.w);
+
+    // Local contrast adaptation in action:
+    edges.xy *= step(0.5 * maxDelta, delta.xy);
+
+    return vec4(edges, 0.0, 0.0);
+}
+
+void main() {
+    gl_FragColor = SMAAColorEdgeDetectionPS(vUv, vOffset, tColor);
+}
+`;

+ 33 - 0
src/mol-gl/shader/smaa/edges.vert.ts

@@ -0,0 +1,33 @@
+/**
+ * Slightly adapted from https://github.com/mrdoob/three.js
+ * MIT License Copyright (c) 2010-2020 three.js authors
+ *
+ * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
+ * Preset: SMAA 1x Medium (with color edge detection)
+ * https://github.com/iryoku/smaa/releases/tag/v2.8
+ */
+
+export const edges_vert = `
+precision highp float;
+
+attribute vec2 aPosition;
+uniform vec2 uQuadScale;
+
+uniform vec2 uTexSizeInv;
+
+varying vec2 vUv;
+varying vec4 vOffset[ 3 ];
+
+void SMAAEdgeDetectionVS(vec2 texCoord) {
+    vOffset[0] = texCoord.xyxy + uTexSizeInv.xyxy * vec4(-1.0, 0.0, 0.0, 1.0); // WebGL port note: Changed sign in W component
+    vOffset[1] = texCoord.xyxy + uTexSizeInv.xyxy * vec4(1.0, 0.0, 0.0, -1.0); // WebGL port note: Changed sign in W component
+    vOffset[2] = texCoord.xyxy + uTexSizeInv.xyxy * vec4(-2.0, 0.0, 0.0, 2.0); // WebGL port note: Changed sign in W component
+}
+
+void main() {
+    vUv = (aPosition + 1.0) * 0.5;
+    SMAAEdgeDetectionVS(vUv);
+    vec2 position = aPosition * uQuadScale - vec2(1.0, 1.0) + uQuadScale;
+    gl_Position = vec4(position, 0.0, 1.0);
+}
+`;

+ 216 - 0
src/mol-gl/shader/smaa/weights.frag.ts

@@ -0,0 +1,216 @@
+/**
+ * Slightly adapted from https://github.com/mrdoob/three.js
+ * MIT License Copyright (c) 2010-2020 three.js authors
+ *
+ * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
+ * Preset: SMAA 1x Medium (with color edge detection)
+ * https://github.com/iryoku/smaa/releases/tag/v2.8
+ */
+
+export const weights_frag = `
+precision highp float;
+precision highp int;
+precision highp sampler2D;
+
+#define SMAASampleLevelZeroOffset(tex, coord, offset) texture2D(tex, coord + float(offset) * uTexSizeInv, 0.0)
+
+#define SMAA_AREATEX_MAX_DISTANCE 16
+#define SMAA_AREATEX_PIXEL_SIZE (1.0 / vec2(160.0, 560.0))
+#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0)
+
+uniform sampler2D tEdges;
+uniform sampler2D tArea;
+uniform sampler2D tSearch;
+uniform vec2 uTexSizeInv;
+
+varying vec2 vUv;
+varying vec4 vOffset[3];
+varying vec2 vPixCoord;
+
+#if __VERSION__ == 100
+    vec2 round(vec2 x) {
+        return sign(x) * floor(abs(x) + 0.5);
+    }
+#endif
+
+float SMAASearchLength(sampler2D searchTex, vec2 e, float bias, float scale) {
+    // Not required if searchTex accesses are set to point:
+    // float2 SEARCH_TEX_PIXEL_SIZE = 1.0 / float2(66.0, 33.0);
+    // e = float2(bias, 0.0) + 0.5 * SEARCH_TEX_PIXEL_SIZE +
+    //     e * float2(scale, 1.0) * float2(64.0, 32.0) * SEARCH_TEX_PIXEL_SIZE;
+    e.r = bias + e.r * scale;
+    return 255.0 * texture2D(searchTex, e, 0.0).r;
+}
+
+float SMAASearchXLeft(sampler2D edgesTex, sampler2D searchTex, vec2 texCoord, float end) {
+    /**
+     * @PSEUDO_GATHER4
+     * This texCoord has been offset by (-0.25, -0.125) in the vertex shader to
+     * sample between edge, thus fetching four edges in a row.
+     * Sampling with different offsets in each direction allows to disambiguate
+     * which edges are active from the four fetched ones.
+     */
+    vec2 e = vec2(0.0, 1.0);
+
+    for (int i = 0; i < dMaxSearchSteps; i++) { // WebGL port note: Changed while to for
+        e = texture2D( edgesTex, texCoord, 0.0).rg;
+        texCoord -= vec2(2.0, 0.0) * uTexSizeInv;
+        if (!(texCoord.x > end && e.g > 0.8281 && e.r == 0.0)) break;
+    }
+
+    // We correct the previous (-0.25, -0.125) offset we applied:
+    texCoord.x += 0.25 * uTexSizeInv.x;
+
+    // The searches are bias by 1, so adjust the coords accordingly:
+    texCoord.x += uTexSizeInv.x;
+
+    // Disambiguate the length added by the last step:
+    texCoord.x += 2.0 * uTexSizeInv.x; // Undo last step
+    texCoord.x -= uTexSizeInv.x * SMAASearchLength(searchTex, e, 0.0, 0.5);
+
+    return texCoord.x;
+}
+
+float SMAASearchXRight(sampler2D edgesTex, sampler2D searchTex, vec2 texCoord, float end) {
+    vec2 e = vec2( 0.0, 1.0 );
+
+    for (int i = 0; i < dMaxSearchSteps; i++) { // WebGL port note: Changed while to for
+        e = texture2D(edgesTex, texCoord, 0.0).rg;
+        texCoord += vec2(2.0, 0.0) * uTexSizeInv;
+        if (!(texCoord.x < end && e.g > 0.8281 && e.r == 0.0)) break;
+    }
+
+    texCoord.x -= 0.25 * uTexSizeInv.x;
+    texCoord.x -= uTexSizeInv.x;
+    texCoord.x -= 2.0 * uTexSizeInv.x;
+    texCoord.x += uTexSizeInv.x * SMAASearchLength( searchTex, e, 0.5, 0.5 );
+
+    return texCoord.x;
+}
+
+float SMAASearchYUp(sampler2D edgesTex, sampler2D searchTex, vec2 texCoord, float end) {
+    vec2 e = vec2( 1.0, 0.0 );
+
+    for (int i = 0; i < dMaxSearchSteps; i++) { // WebGL port note: Changed while to for
+        e = texture2D(edgesTex, texCoord, 0.0).rg;
+        texCoord += vec2(0.0, 2.0) * uTexSizeInv; // WebGL port note: Changed sign
+        if (!(texCoord.y > end && e.r > 0.8281 && e.g == 0.0)) break;
+    }
+
+    texCoord.y -= 0.25 * uTexSizeInv.y; // WebGL port note: Changed sign
+    texCoord.y -= uTexSizeInv.y; // WebGL port note: Changed sign
+    texCoord.y -= 2.0 * uTexSizeInv.y; // WebGL port note: Changed sign
+    texCoord.y += uTexSizeInv.y * SMAASearchLength(searchTex, e.gr, 0.0, 0.5); // WebGL port note: Changed sign
+
+    return texCoord.y;
+}
+
+float SMAASearchYDown(sampler2D edgesTex, sampler2D searchTex, vec2 texCoord, float end) {
+    vec2 e = vec2( 1.0, 0.0 );
+
+    for (int i = 0; i < dMaxSearchSteps; i++) { // WebGL port note: Changed while to for
+        e = texture2D(edgesTex, texCoord, 0.0).rg;
+        texCoord -= vec2( 0.0, 2.0 ) * uTexSizeInv; // WebGL port note: Changed sign
+        if (!(texCoord.y < end && e.r > 0.8281 && e.g == 0.0)) break;
+    }
+
+    texCoord.y += 0.25 * uTexSizeInv.y; // WebGL port note: Changed sign
+    texCoord.y += uTexSizeInv.y; // WebGL port note: Changed sign
+    texCoord.y += 2.0 * uTexSizeInv.y; // WebGL port note: Changed sign
+    texCoord.y -= uTexSizeInv.y * SMAASearchLength(searchTex, e.gr, 0.5, 0.5); // WebGL port note: Changed sign
+
+    return texCoord.y;
+}
+
+vec2 SMAAArea(sampler2D areaTex, vec2 dist, float e1, float e2, float offset) {
+    // Rounding prevents precision errors of bilinear filtering:
+    vec2 texCoord = float(SMAA_AREATEX_MAX_DISTANCE) * round(4.0 * vec2(e1, e2)) + dist;
+
+    // We do a scale and bias for mapping to texel space:
+    texCoord = SMAA_AREATEX_PIXEL_SIZE * texCoord + (0.5 * SMAA_AREATEX_PIXEL_SIZE);
+
+    // Move to proper place, according to the subpixel offset:
+    texCoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset;
+
+    return texture2D(areaTex, texCoord, 0.0).rg;
+}
+
+vec4 SMAABlendingWeightCalculationPS(vec2 texCoord, vec2 pixCoord, vec4 offset[3], sampler2D edgesTex, sampler2D areaTex, sampler2D searchTex, ivec4 subsampleIndices) {
+    vec4 weights = vec4(0.0, 0.0, 0.0, 0.0);
+
+    vec2 e = texture2D(edgesTex, texCoord).rg;
+
+    if (e.g > 0.0) { // Edge at north
+        vec2 d;
+
+        // Find the distance to the left:
+        vec2 coords;
+        coords.x = SMAASearchXLeft(edgesTex, searchTex, offset[0].xy, offset[2].x );
+        coords.y = offset[1].y; // offset[1].y = texCoord.y - 0.25 * uTexSizeInv.y (@CROSSING_OFFSET)
+        d.x = coords.x;
+
+        // Now fetch the left crossing edges, two at a time using bilinear
+        // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to
+        // discern what value each edge has:
+        float e1 = texture2D(edgesTex, coords, 0.0).r;
+
+        // Find the distance to the right:
+        coords.x = SMAASearchXRight(edgesTex, searchTex, offset[0].zw, offset[2].y);
+        d.y = coords.x;
+
+        // We want the distances to be in pixel units (doing this here allow to
+        // better interleave arithmetic and memory accesses):
+        d = d / uTexSizeInv.x - pixCoord.x;
+
+        // SMAAArea below needs a sqrt, as the areas texture is compressed
+        // quadratically:
+        vec2 sqrt_d = sqrt(abs(d));
+
+        // Fetch the right crossing edges:
+        coords.y -= 1.0 * uTexSizeInv.y; // WebGL port note: Added
+        float e2 = SMAASampleLevelZeroOffset(edgesTex, coords, ivec2(1, 0)).r;
+
+        // Ok, we know how this pattern looks like, now it is time for getting
+        // the actual area:
+        weights.rg = SMAAArea(areaTex, sqrt_d, e1, e2, float(subsampleIndices.y));
+    }
+
+    if (e.r > 0.0) { // Edge at west
+        vec2 d;
+
+        // Find the distance to the top:
+        vec2 coords;
+
+        coords.y = SMAASearchYUp(edgesTex, searchTex, offset[1].xy, offset[2].z );
+        coords.x = offset[0].x; // offset[1].x = texCoord.x - 0.25 * uTexSizeInv.x;
+        d.x = coords.y;
+
+        // Fetch the top crossing edges:
+        float e1 = texture2D(edgesTex, coords, 0.0).g;
+
+        // Find the distance to the bottom:
+        coords.y = SMAASearchYDown(edgesTex, searchTex, offset[1].zw, offset[2].w);
+        d.y = coords.y;
+
+        // We want the distances to be in pixel units:
+        d = d / uTexSizeInv.y - pixCoord.y;
+
+        // SMAAArea below needs a sqrt, as the areas texture is compressed
+        // quadratically:
+        vec2 sqrt_d = sqrt(abs(d));
+
+        // Fetch the bottom crossing edges:
+        coords.y -= 1.0 * uTexSizeInv.y; // WebGL port note: Added
+        float e2 = SMAASampleLevelZeroOffset(edgesTex, coords, ivec2(0, 1)).g;
+
+        // Get the area for this direction:
+        weights.ba = SMAAArea(areaTex, sqrt_d, e1, e2, float(subsampleIndices.x));
+    }
+
+    return weights;
+}
+
+void main() {
+    gl_FragColor = SMAABlendingWeightCalculationPS(vUv, vPixCoord, vOffset, tEdges, tArea, tSearch, ivec4(0.0));
+}
+`;

+ 39 - 0
src/mol-gl/shader/smaa/weights.vert.ts

@@ -0,0 +1,39 @@
+/**
+ * Slightly adapted from https://github.com/mrdoob/three.js
+ * MIT License Copyright (c) 2010-2020 three.js authors
+ *
+ * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
+ * Preset: SMAA 1x Medium (with color edge detection)
+ * https://github.com/iryoku/smaa/releases/tag/v2.8
+ */
+
+export const weights_vert = `
+precision highp float;
+
+attribute vec2 aPosition;
+uniform vec2 uQuadScale;
+
+uniform vec2 uTexSizeInv;
+
+varying vec2 vUv;
+varying vec4 vOffset[3];
+varying vec2 vPixCoord;
+
+void SMAABlendingWeightCalculationVS(vec2 texCoord) {
+    vPixCoord = texCoord / uTexSizeInv;
+
+    // We will use these offsets for the searches later on (see @PSEUDO_GATHER4):
+    vOffset[0] = texCoord.xyxy + uTexSizeInv.xyxy * vec4(-0.25, 0.125, 1.25, 0.125); // WebGL port note: Changed sign in Y and W components
+    vOffset[1] = texCoord.xyxy + uTexSizeInv.xyxy * vec4(-0.125, 0.25, -0.125, -1.25); // WebGL port note: Changed sign in Y and W components
+
+    // And these for the searches, they indicate the ends of the loops:
+    vOffset[2] = vec4(vOffset[0].xz, vOffset[1].yw) + vec4(-2.0, 2.0, -2.0, 2.0) * uTexSizeInv.xxyy * float(dMaxSearchSteps);
+}
+
+void main() {
+    vUv = (aPosition + 1.0) * 0.5;
+    SMAABlendingWeightCalculationVS(vUv);
+    vec2 position = aPosition * uQuadScale - vec2(1.0, 1.0) + uQuadScale;
+    gl_Position = vec4(position, 0.0, 1.0);
+}
+`;

+ 26 - 4
src/mol-gl/webgl/texture.ts

@@ -118,6 +118,10 @@ export function getAttachment(gl: GLRenderingContext, extensions: WebGLExtension
     throw new Error('unknown texture attachment');
 }
 
+function isImage(x: TextureImage<any> | TextureVolume<any> | HTMLImageElement): x is HTMLImageElement {
+    return typeof HTMLImageElement !== undefined && (x instanceof HTMLImageElement);
+}
+
 function isTexture2d(x: TextureImage<any> | TextureVolume<any>, target: number, gl: GLRenderingContext): x is TextureImage<any> {
     return target === gl.TEXTURE_2D;
 }
@@ -138,7 +142,7 @@ export interface Texture {
     getDepth: () => number
 
     define: (width: number, height: number, depth?: number) => void
-    load: (image: TextureImage<any> | TextureVolume<any>) => void
+    load: (image: TextureImage<any> | TextureVolume<any> | HTMLImageElement) => void
     bind: (id: TextureId) => void
     unbind: (id: TextureId) => void
     /** Use `layer` to attach a z-slice of a 3D texture */
@@ -193,7 +197,7 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
     init();
 
     let width = 0, height = 0, depth = 0;
-    let loadedData: undefined | TextureImage<any> | TextureVolume<any>;
+    let loadedData: undefined | TextureImage<any> | TextureVolume<any> | HTMLImageElement;
     let destroyed = false;
 
     function define(_width: number, _height: number, _depth?: number) {
@@ -210,13 +214,17 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
         }
     }
 
-    function load(data: TextureImage<any> | TextureVolume<any>) {
+    function load(data: TextureImage<any> | TextureVolume<any> | HTMLImageElement) {
         gl.bindTexture(target, texture);
         // unpack alignment of 1 since we use textures only for data
         gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
         gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
         gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
-        if (isTexture2d(data, target, gl)) {
+        if (isImage(data)) {
+            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
+            gl.bindTexture(gl.TEXTURE_2D, texture);
+            gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, type, data);
+        } else if (isTexture2d(data, target, gl)) {
             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY);
             width = data.width, height = data.height;
             gl.texImage2D(target, 0, internalFormat, width, height, 0, format, type, data.array);
@@ -314,6 +322,20 @@ export function createTextures(ctx: WebGLContext, schema: RenderableSchema, valu
     return textures;
 }
 
+/**
+ * Loads an image from a url to a textures and triggers update asynchronously.
+ * This will not work on node.js with a polyfill for HTMLImageElement.
+ */
+export function loadImageTexture(src: string, cell: ValueCell<Texture>, texture: Texture) {
+    const img = new Image();
+    img.onload = function() {
+        texture.load(img);
+        ValueCell.update(cell, texture);
+    };
+    img.src = src;
+    document.body.appendChild(img);
+}
+
 //
 
 export function createNullTexture(gl: GLRenderingContext, kind: TextureKind): Texture {

Деякі файли не було показано, через те що забагато файлів було змінено