|
@@ -54,18 +54,19 @@ interface Renderer {
|
|
|
readonly props: Readonly<RendererProps>
|
|
|
|
|
|
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void
|
|
|
- clearDepth: () => void
|
|
|
+ clearDepth: (packed?: boolean) => void
|
|
|
update: (camera: ICamera) => void
|
|
|
|
|
|
- renderPick: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null, pickType: PickType) => void
|
|
|
+ renderPick: (group: Scene.Group, camera: ICamera, variant: 'pick' | 'depth', depthTexture: Texture | null, pickType: PickType) => void
|
|
|
renderDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
+ renderDepthOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
+ renderDepthTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
renderMarkingDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
renderMarkingMask: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
renderBlended: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
renderBlendedOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
renderBlendedTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
- renderBlendedVolumeOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
- renderBlendedVolumeTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
+ renderBlendedVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
renderWboitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
renderWboitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
|
|
|
|
@@ -141,6 +142,12 @@ namespace Renderer {
|
|
|
BlendedBack = 2
|
|
|
}
|
|
|
|
|
|
+ const enum Mask {
|
|
|
+ All = 0,
|
|
|
+ Opaque = 1,
|
|
|
+ Transparent = 2,
|
|
|
+ }
|
|
|
+
|
|
|
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
|
|
|
const { gl, state, stats } = ctx;
|
|
|
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
|
|
@@ -152,7 +159,9 @@ namespace Renderer {
|
|
|
|
|
|
let transparentBackground = false;
|
|
|
|
|
|
- const emptyDepthTexture = ctx.resources.texture('image-depth', 'depth', 'ushort', 'nearest');
|
|
|
+ const emptyDepthTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
|
|
+ emptyDepthTexture.define(1, 1);
|
|
|
+ emptyDepthTexture.load({ array: new Uint8Array([255, 255, 255, 255]), width: 1, height: 1 });
|
|
|
const sharedTexturesList: Textures = [
|
|
|
['tDepth', emptyDepthTexture]
|
|
|
];
|
|
@@ -197,7 +206,7 @@ namespace Renderer {
|
|
|
uFogFar: ValueCell.create(10000),
|
|
|
uFogColor: ValueCell.create(bgColor),
|
|
|
|
|
|
- uRenderWboit: ValueCell.create(false),
|
|
|
+ uRenderMask: ValueCell.create(0),
|
|
|
uMarkingDepthTest: ValueCell.create(false),
|
|
|
uPickType: ValueCell.create(PickType.None),
|
|
|
uMarkingType: ValueCell.create(MarkingType.None),
|
|
@@ -327,7 +336,7 @@ namespace Renderer {
|
|
|
ValueCell.updateIfChanged(globalUniforms.uTransparentBackground, transparentBackground);
|
|
|
};
|
|
|
|
|
|
- const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderWboit: boolean, markingDepthTest: boolean) => {
|
|
|
+ const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderMask: Mask, markingDepthTest: boolean) => {
|
|
|
arrayMapUpsert(sharedTexturesList, 'tDepth', depthTexture || emptyDepthTexture);
|
|
|
|
|
|
ValueCell.update(globalUniforms.uModel, group.view);
|
|
@@ -336,7 +345,7 @@ namespace Renderer {
|
|
|
ValueCell.update(globalUniforms.uModelViewProjection, Mat4.mul(modelViewProjection, modelView, camera.projection));
|
|
|
ValueCell.update(globalUniforms.uInvModelViewProjection, Mat4.invert(invModelViewProjection, modelViewProjection));
|
|
|
|
|
|
- ValueCell.updateIfChanged(globalUniforms.uRenderWboit, renderWboit);
|
|
|
+ ValueCell.updateIfChanged(globalUniforms.uRenderMask, renderMask);
|
|
|
ValueCell.updateIfChanged(globalUniforms.uMarkingDepthTest, markingDepthTest);
|
|
|
|
|
|
state.enable(gl.SCISSOR_TEST);
|
|
@@ -355,7 +364,7 @@ namespace Renderer {
|
|
|
state.enable(gl.DEPTH_TEST);
|
|
|
state.depthMask(true);
|
|
|
|
|
|
- updateInternal(group, camera, depthTexture, false, false);
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.All, false);
|
|
|
ValueCell.updateIfChanged(globalUniforms.uPickType, pickType);
|
|
|
|
|
|
const { renderables } = group;
|
|
@@ -371,7 +380,7 @@ namespace Renderer {
|
|
|
state.enable(gl.DEPTH_TEST);
|
|
|
state.depthMask(true);
|
|
|
|
|
|
- updateInternal(group, camera, depthTexture, false, false);
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.All, false);
|
|
|
|
|
|
const { renderables } = group;
|
|
|
for (let i = 0, il = renderables.length; i < il; ++i) {
|
|
@@ -379,12 +388,44 @@ namespace Renderer {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ const renderDepthOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
|
|
+ state.disable(gl.BLEND);
|
|
|
+ state.enable(gl.DEPTH_TEST);
|
|
|
+ state.depthMask(true);
|
|
|
+
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.Opaque, false);
|
|
|
+
|
|
|
+ const { renderables } = group;
|
|
|
+ for (let i = 0, il = renderables.length; i < il; ++i) {
|
|
|
+ const r = renderables[i];
|
|
|
+ if (r.state.opaque && r.values.transparencyAverage.ref.value !== 1 && !r.values.dXrayShaded?.ref.value) {
|
|
|
+ renderObject(r, 'depth', Flag.None);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const renderDepthTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
|
|
+ state.disable(gl.BLEND);
|
|
|
+ state.enable(gl.DEPTH_TEST);
|
|
|
+ state.depthMask(true);
|
|
|
+
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.Transparent, false);
|
|
|
+
|
|
|
+ const { renderables } = group;
|
|
|
+ for (let i = 0, il = renderables.length; i < il; ++i) {
|
|
|
+ const r = renderables[i];
|
|
|
+ if (!r.state.opaque || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) {
|
|
|
+ renderObject(r, 'depth', Flag.None);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
const renderMarkingDepth = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
|
|
state.disable(gl.BLEND);
|
|
|
state.enable(gl.DEPTH_TEST);
|
|
|
state.depthMask(true);
|
|
|
|
|
|
- updateInternal(group, camera, depthTexture, false, false);
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.All, false);
|
|
|
ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Depth);
|
|
|
|
|
|
const { renderables } = group;
|
|
@@ -402,7 +443,7 @@ namespace Renderer {
|
|
|
state.enable(gl.DEPTH_TEST);
|
|
|
state.depthMask(true);
|
|
|
|
|
|
- updateInternal(group, camera, depthTexture, false, !!depthTexture);
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.All, !!depthTexture);
|
|
|
ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Mask);
|
|
|
|
|
|
const { renderables } = group;
|
|
@@ -425,14 +466,14 @@ namespace Renderer {
|
|
|
state.enable(gl.DEPTH_TEST);
|
|
|
state.depthMask(true);
|
|
|
|
|
|
- updateInternal(group, camera, depthTexture, false, false);
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.Opaque, false);
|
|
|
|
|
|
const { renderables } = group;
|
|
|
for (let i = 0, il = renderables.length; i < il; ++i) {
|
|
|
const r = renderables[i];
|
|
|
if (r.state.opaque) {
|
|
|
renderObject(r, 'colorBlended', Flag.None);
|
|
|
- } else if (r.values.uDoubleSided?.ref.value && r.values.dOpaqueBackfaces?.ref.value) {
|
|
|
+ } else if (r.values.uDoubleSided?.ref.value && r.values.dTransparentBackfaces?.ref.value === 'opaque') {
|
|
|
renderObject(r, 'colorBlended', Flag.BlendedBack);
|
|
|
}
|
|
|
}
|
|
@@ -441,7 +482,7 @@ namespace Renderer {
|
|
|
const renderBlendedTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
|
|
state.enable(gl.DEPTH_TEST);
|
|
|
|
|
|
- updateInternal(group, camera, depthTexture, false, false);
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.Transparent, false);
|
|
|
|
|
|
const { renderables } = group;
|
|
|
|
|
@@ -463,10 +504,10 @@ namespace Renderer {
|
|
|
state.depthMask(false);
|
|
|
for (let i = 0, il = renderables.length; i < il; ++i) {
|
|
|
const r = renderables[i];
|
|
|
- if (!r.state.opaque && !r.state.writeDepth) {
|
|
|
+ if ((!r.state.opaque && !r.state.writeDepth) || r.values.transparencyAverage.ref.value > 0) {
|
|
|
if (r.values.uDoubleSided?.ref.value) {
|
|
|
// render frontfaces and backfaces separately to avoid artefacts
|
|
|
- if (!r.values.dOpaqueBackfaces?.ref.value) {
|
|
|
+ if (r.values.dTransparentBackfaces?.ref.value !== 'opaque') {
|
|
|
renderObject(r, 'colorBlended', Flag.BlendedBack);
|
|
|
}
|
|
|
renderObject(r, 'colorBlended', Flag.BlendedFront);
|
|
@@ -477,39 +518,16 @@ namespace Renderer {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- const renderBlendedVolumeOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
|
|
+ const renderBlendedVolume = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
|
|
state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
|
state.enable(gl.BLEND);
|
|
|
|
|
|
- updateInternal(group, camera, depthTexture, false, false);
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.Transparent, false);
|
|
|
|
|
|
const { renderables } = group;
|
|
|
for (let i = 0, il = renderables.length; i < il; ++i) {
|
|
|
const r = renderables[i];
|
|
|
-
|
|
|
- // TODO: simplify, handle in renderable.state???
|
|
|
- // uAlpha is updated in "render" so we need to recompute it here
|
|
|
- const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
|
|
|
- if (alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && !r.values.dXrayShaded?.ref.value) {
|
|
|
- renderObject(r, 'colorBlended', Flag.None);
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const renderBlendedVolumeTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
|
|
- state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
|
- state.enable(gl.BLEND);
|
|
|
-
|
|
|
- updateInternal(group, camera, depthTexture, false, false);
|
|
|
-
|
|
|
- const { renderables } = group;
|
|
|
- for (let i = 0, il = renderables.length; i < il; ++i) {
|
|
|
- const r = renderables[i];
|
|
|
-
|
|
|
- // TODO: simplify, handle in renderable.state???
|
|
|
- // uAlpha is updated in "render" so we need to recompute it here
|
|
|
- const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
|
|
|
- if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) {
|
|
|
+ if (r.values.dGeometryType.ref.value === 'directVolume') {
|
|
|
renderObject(r, 'colorBlended', Flag.None);
|
|
|
}
|
|
|
}
|
|
@@ -520,7 +538,7 @@ namespace Renderer {
|
|
|
state.enable(gl.DEPTH_TEST);
|
|
|
state.depthMask(true);
|
|
|
|
|
|
- updateInternal(group, camera, depthTexture, false, false);
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.Opaque, false);
|
|
|
|
|
|
const { renderables } = group;
|
|
|
for (let i = 0, il = renderables.length; i < il; ++i) {
|
|
@@ -529,14 +547,14 @@ namespace Renderer {
|
|
|
// TODO: simplify, handle in renderable.state???
|
|
|
// uAlpha is updated in "render" so we need to recompute it here
|
|
|
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
|
|
|
- if ((alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) || r.values.dOpaqueBackfaces?.ref.value) {
|
|
|
+ if ((alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) || r.values.dTransparentBackfaces?.ref.value === 'opaque') {
|
|
|
renderObject(r, 'colorWboit', Flag.None);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const renderWboitTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
|
|
- updateInternal(group, camera, depthTexture, true, false);
|
|
|
+ updateInternal(group, camera, depthTexture, Mask.Transparent, false);
|
|
|
|
|
|
const { renderables } = group;
|
|
|
for (let i = 0, il = renderables.length; i < il; ++i) {
|
|
@@ -567,23 +585,31 @@ namespace Renderer {
|
|
|
}
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
|
},
|
|
|
- clearDepth: () => {
|
|
|
+ clearDepth: (packed = false) => {
|
|
|
state.enable(gl.SCISSOR_TEST);
|
|
|
- state.enable(gl.DEPTH_TEST);
|
|
|
- state.depthMask(true);
|
|
|
- gl.clear(gl.DEPTH_BUFFER_BIT);
|
|
|
+
|
|
|
+ if (packed) {
|
|
|
+ state.colorMask(true, true, true, true);
|
|
|
+ state.clearColor(1, 1, 1, 1);
|
|
|
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
|
+ } else {
|
|
|
+ state.enable(gl.DEPTH_TEST);
|
|
|
+ state.depthMask(true);
|
|
|
+ gl.clear(gl.DEPTH_BUFFER_BIT);
|
|
|
+ }
|
|
|
},
|
|
|
update,
|
|
|
|
|
|
renderPick,
|
|
|
renderDepth,
|
|
|
+ renderDepthOpaque,
|
|
|
+ renderDepthTransparent,
|
|
|
renderMarkingDepth,
|
|
|
renderMarkingMask,
|
|
|
renderBlended,
|
|
|
renderBlendedOpaque,
|
|
|
renderBlendedTransparent,
|
|
|
- renderBlendedVolumeOpaque,
|
|
|
- renderBlendedVolumeTransparent,
|
|
|
+ renderBlendedVolume,
|
|
|
renderWboitOpaque,
|
|
|
renderWboitTransparent,
|
|
|
|