123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744 |
- /**
- * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
- import { Viewport } from '../mol-canvas3d/camera/util';
- import { ICamera } from '../mol-canvas3d/camera';
- import { Scene } from './scene';
- import { WebGLContext } from './webgl/context';
- import { Mat4, Vec3, Vec4, Vec2, Quat } from '../mol-math/linear-algebra';
- import { GraphicsRenderable } from './renderable';
- import { Color } from '../mol-util/color';
- import { ValueCell, deepEqual } from '../mol-util';
- import { GlobalUniformValues } from './renderable/schema';
- import { GraphicsRenderVariant } from './webgl/render-item';
- import { ParamDefinition as PD } from '../mol-util/param-definition';
- import { Clipping } from '../mol-theme/clipping';
- import { stringToWords } from '../mol-util/string';
- import { degToRad } from '../mol-math/misc';
- import { createNullTexture, Texture, Textures } from './webgl/texture';
- import { arrayMapUpsert } from '../mol-util/array';
- import { clamp } from '../mol-math/interpolate';
- export interface RendererStats {
- programCount: number
- shaderCount: number
- attributeCount: number
- elementsCount: number
- framebufferCount: number
- renderbufferCount: number
- textureCount: number
- vertexArrayCount: number
- drawCount: number
- instanceCount: number
- instancedDrawCount: number
- }
- interface Renderer {
- readonly stats: RendererStats
- readonly props: Readonly<RendererProps>
- clear: (toBackgroundColor: boolean) => void
- clearDepth: () => void
- update: (camera: ICamera) => void
- renderPick: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null) => void
- renderDepth: (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
- renderWboitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
- renderWboitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
- setProps: (props: Partial<RendererProps>) => void
- setViewport: (x: number, y: number, width: number, height: number) => void
- setTransparentBackground: (value: boolean) => void
- setDrawingBufferSize: (width: number, height: number) => void
- setPixelRatio: (value: number) => void
- dispose: () => void
- }
- export const RendererParams = {
- backgroundColor: PD.Color(Color(0x000000), { description: 'Background color of the 3D canvas' }),
- pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
- interiorDarkening: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
- interiorColorFlag: PD.Boolean(true, { label: 'Use Interior Color' }),
- interiorColor: PD.Color(Color.fromNormalizedRgb(0.3, 0.3, 0.3)),
- highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
- selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
- highlightStrength: PD.Numeric(0.7, { min: 0.0, max: 1.0, step: 0.1 }),
- selectStrength: PD.Numeric(0.7, { min: 0.0, max: 1.0, step: 0.1 }),
- markerPriority: PD.Select(1, [[1, 'Highlight'], [2, 'Select']]),
- xrayEdgeFalloff: PD.Numeric(1, { min: 0.0, max: 3.0, step: 0.1 }),
- light: PD.ObjectList({
- inclination: PD.Numeric(180, { min: 0, max: 180, step: 1 }),
- azimuth: PD.Numeric(0, { min: 0, max: 360, step: 1 }),
- color: PD.Color(Color.fromNormalizedRgb(1.0, 1.0, 1.0)),
- intensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
- }, o => Color.toHexString(o.color), { defaultValue: [{
- inclination: 180,
- azimuth: 0,
- color: Color.fromNormalizedRgb(1.0, 1.0, 1.0),
- intensity: 0.6
- }] }),
- ambientColor: PD.Color(Color.fromNormalizedRgb(1.0, 1.0, 1.0)),
- ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
- clip: PD.Group({
- variant: PD.Select('instance', PD.arrayToOptions<Clipping.Variant>(['instance', 'pixel'])),
- objects: PD.ObjectList({
- type: PD.Select('plane', PD.objectToOptions(Clipping.Type, t => stringToWords(t))),
- invert: PD.Boolean(false),
- position: PD.Vec3(Vec3()),
- rotation: PD.Group({
- axis: PD.Vec3(Vec3.create(1, 0, 0)),
- angle: PD.Numeric(0, { min: -180, max: 180, step: 1 }, { description: 'Angle in Degrees' }),
- }, { isExpanded: true }),
- scale: PD.Vec3(Vec3.create(1, 1, 1)),
- }, o => stringToWords(o.type))
- })
- };
- export type RendererProps = PD.Values<typeof RendererParams>
- type Light = {
- count: number
- direction: number[]
- color: number[]
- }
- const tmpDir = Vec3();
- const tmpColor = Vec3();
- function getLight(props: RendererProps['light'], light?: Light): Light {
- const { direction, color } = light || {
- direction: (new Array(5 * 3)).fill(0),
- color: (new Array(5 * 3)).fill(0),
- };
- for (let i = 0, il = props.length; i < il; ++i) {
- const p = props[i];
- Vec3.directionFromSpherical(tmpDir, degToRad(p.inclination), degToRad(p.azimuth), 1);
- Vec3.toArray(tmpDir, direction, i * 3);
- Vec3.scale(tmpColor, Color.toVec3Normalized(tmpColor, p.color), p.intensity);
- Vec3.toArray(tmpColor, color, i * 3);
- }
- return { count: props.length, direction, color };
- }
- type Clip = {
- variant: Clipping.Variant
- objects: {
- count: number
- type: number[]
- invert: boolean[]
- position: number[]
- rotation: number[]
- scale: number[]
- }
- }
- const tmpQuat = Quat();
- function getClip(props: RendererProps['clip'], clip?: Clip): Clip {
- const { type, invert, position, rotation, scale } = clip?.objects || {
- type: (new Array(5)).fill(1),
- invert: (new Array(5)).fill(false),
- position: (new Array(5 * 3)).fill(0),
- rotation: (new Array(5 * 4)).fill(0),
- scale: (new Array(5 * 3)).fill(1),
- };
- for (let i = 0, il = props.objects.length; i < il; ++i) {
- const p = props.objects[i];
- type[i] = Clipping.Type[p.type];
- invert[i] = p.invert;
- Vec3.toArray(p.position, position, i * 3);
- Quat.toArray(Quat.setAxisAngle(tmpQuat, p.rotation.axis, degToRad(p.rotation.angle)), rotation, i * 4);
- Vec3.toArray(p.scale, scale, i * 3);
- }
- return {
- variant: props.variant,
- objects: { count: props.objects.length, type, invert, position, rotation, scale }
- };
- }
- namespace Renderer {
- export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
- const { gl, state, stats, extensions: { fragDepth } } = ctx;
- const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
- const light = getLight(p.light);
- const clip = getClip(p.clip);
- const viewport = Viewport();
- const drawingBufferSize = Vec2.create(gl.drawingBufferWidth, gl.drawingBufferHeight);
- const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor);
- let transparentBackground = false;
- const nullDepthTexture = createNullTexture(gl);
- const sharedTexturesList: Textures = [
- ['tDepth', nullDepthTexture]
- ];
- const view = Mat4();
- const invView = Mat4();
- const modelView = Mat4();
- const invModelView = Mat4();
- const invProjection = Mat4();
- const modelViewProjection = Mat4();
- const invModelViewProjection = Mat4();
- const cameraDir = Vec3();
- const viewOffset = Vec2();
- const ambientColor = Vec3();
- Vec3.scale(ambientColor, Color.toArrayNormalized(p.ambientColor, ambientColor, 0), p.ambientIntensity);
- const globalUniforms: GlobalUniformValues = {
- uModel: ValueCell.create(Mat4.identity()),
- uView: ValueCell.create(view),
- uInvView: ValueCell.create(invView),
- uModelView: ValueCell.create(modelView),
- uInvModelView: ValueCell.create(invModelView),
- uInvProjection: ValueCell.create(invProjection),
- uProjection: ValueCell.create(Mat4()),
- uModelViewProjection: ValueCell.create(modelViewProjection),
- uInvModelViewProjection: ValueCell.create(invModelViewProjection),
- uIsOrtho: ValueCell.create(1),
- uViewOffset: ValueCell.create(viewOffset),
- uPixelRatio: ValueCell.create(ctx.pixelRatio),
- uViewport: ValueCell.create(Viewport.toVec4(Vec4(), viewport)),
- uDrawingBufferSize: ValueCell.create(drawingBufferSize),
- uCameraPosition: ValueCell.create(Vec3()),
- uCameraDir: ValueCell.create(cameraDir),
- uNear: ValueCell.create(1),
- uFar: ValueCell.create(10000),
- uFogNear: ValueCell.create(1),
- uFogFar: ValueCell.create(10000),
- uFogColor: ValueCell.create(bgColor),
- uRenderWboit: ValueCell.create(false),
- uMarkingDepthTest: ValueCell.create(false),
- uTransparentBackground: ValueCell.create(false),
- uClipObjectType: ValueCell.create(clip.objects.type),
- uClipObjectInvert: ValueCell.create(clip.objects.invert),
- uClipObjectPosition: ValueCell.create(clip.objects.position),
- uClipObjectRotation: ValueCell.create(clip.objects.rotation),
- uClipObjectScale: ValueCell.create(clip.objects.scale),
- uLightDirection: ValueCell.create(light.direction),
- uLightColor: ValueCell.create(light.color),
- uAmbientColor: ValueCell.create(ambientColor),
- uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
- uInteriorDarkening: ValueCell.create(p.interiorDarkening),
- uInteriorColorFlag: ValueCell.create(p.interiorColorFlag),
- uInteriorColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.interiorColor)),
- uHighlightColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.highlightColor)),
- uSelectColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.selectColor)),
- uHighlightStrength: ValueCell.create(p.highlightStrength),
- uSelectStrength: ValueCell.create(p.selectStrength),
- uMarkerPriority: ValueCell.create(p.markerPriority),
- uXrayEdgeFalloff: ValueCell.create(p.xrayEdgeFalloff),
- };
- const globalUniformList = Object.entries(globalUniforms);
- let globalUniformsNeedUpdate = true;
- const renderObject = (r: GraphicsRenderable, variant: GraphicsRenderVariant) => {
- if (r.state.disposed || !r.state.visible || (!r.state.pickable && variant[0] === 'p')) {
- return;
- }
- let definesNeedUpdate = false;
- if (r.state.noClip) {
- if (r.values.dClipObjectCount.ref.value !== 0) {
- ValueCell.update(r.values.dClipObjectCount, 0);
- definesNeedUpdate = true;
- }
- } else {
- if (r.values.dClipObjectCount.ref.value !== clip.objects.count) {
- ValueCell.update(r.values.dClipObjectCount, clip.objects.count);
- definesNeedUpdate = true;
- }
- if (r.values.dClipVariant.ref.value !== clip.variant) {
- ValueCell.update(r.values.dClipVariant, clip.variant);
- definesNeedUpdate = true;
- }
- }
- if (r.values.dLightCount.ref.value !== light.count) {
- ValueCell.update(r.values.dLightCount, light.count);
- definesNeedUpdate = true;
- }
- if (definesNeedUpdate) r.update();
- const program = r.getProgram(variant);
- if (state.currentProgramId !== program.id) {
- // console.log('new program')
- globalUniformsNeedUpdate = true;
- program.use();
- }
- if (globalUniformsNeedUpdate) {
- // console.log('globalUniformsNeedUpdate')
- program.setUniforms(globalUniformList);
- globalUniformsNeedUpdate = false;
- }
- if (r.values.dRenderMode) { // indicates direct-volume
- if ((variant[0] === 'p' || variant === 'depth') && r.values.dRenderMode.ref.value === 'volume') {
- return; // no picking/depth in volume mode
- }
- // culling done in fragment shader
- state.disable(gl.CULL_FACE);
- state.frontFace(gl.CCW);
- if (variant === 'colorBlended') {
- // depth test done manually in shader against `depthTexture`
- // still need to enable when fragDepth can be used to write depth
- if (r.values.dRenderMode.ref.value === 'volume' || !fragDepth) {
- state.disable(gl.DEPTH_TEST);
- state.depthMask(false);
- } else {
- state.enable(gl.DEPTH_TEST);
- state.depthMask(r.values.uAlpha.ref.value === 1.0);
- }
- }
- } else {
- if (r.values.dDoubleSided) {
- if (r.values.dDoubleSided.ref.value || r.values.hasReflection.ref.value) {
- state.disable(gl.CULL_FACE);
- } else {
- state.enable(gl.CULL_FACE);
- }
- } else {
- // webgl default
- state.disable(gl.CULL_FACE);
- }
- if (r.values.dFlipSided) {
- if (r.values.dFlipSided.ref.value) {
- state.frontFace(gl.CW);
- state.cullFace(gl.FRONT);
- } else {
- state.frontFace(gl.CCW);
- state.cullFace(gl.BACK);
- }
- } else {
- // webgl default
- state.frontFace(gl.CCW);
- state.cullFace(gl.BACK);
- }
- }
- r.render(variant, sharedTexturesList);
- };
- const update = (camera: ICamera) => {
- ValueCell.update(globalUniforms.uView, camera.view);
- ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view));
- ValueCell.update(globalUniforms.uProjection, camera.projection);
- ValueCell.update(globalUniforms.uInvProjection, Mat4.invert(invProjection, camera.projection));
- ValueCell.updateIfChanged(globalUniforms.uIsOrtho, camera.state.mode === 'orthographic' ? 1 : 0);
- ValueCell.update(globalUniforms.uViewOffset, camera.viewOffset.enabled ? Vec2.set(viewOffset, camera.viewOffset.offsetX * 16, camera.viewOffset.offsetY * 16) : Vec2.set(viewOffset, 0, 0));
- ValueCell.update(globalUniforms.uCameraPosition, camera.state.position);
- ValueCell.update(globalUniforms.uCameraDir, Vec3.normalize(cameraDir, Vec3.sub(cameraDir, camera.state.target, camera.state.position)));
- ValueCell.updateIfChanged(globalUniforms.uFar, camera.far);
- ValueCell.updateIfChanged(globalUniforms.uNear, camera.near);
- ValueCell.updateIfChanged(globalUniforms.uFogFar, camera.fogFar);
- ValueCell.updateIfChanged(globalUniforms.uFogNear, camera.fogNear);
- ValueCell.updateIfChanged(globalUniforms.uTransparentBackground, transparentBackground);
- };
- const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderWboit: boolean, markingDepthTest: boolean) => {
- arrayMapUpsert(sharedTexturesList, 'tDepth', depthTexture || nullDepthTexture);
- ValueCell.update(globalUniforms.uModel, group.view);
- ValueCell.update(globalUniforms.uModelView, Mat4.mul(modelView, group.view, camera.view));
- ValueCell.update(globalUniforms.uInvModelView, Mat4.invert(invModelView, modelView));
- 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.uMarkingDepthTest, markingDepthTest);
- state.enable(gl.SCISSOR_TEST);
- state.colorMask(true, true, true, true);
- const { x, y, width, height } = viewport;
- gl.viewport(x, y, width, height);
- gl.scissor(x, y, width, height);
- globalUniformsNeedUpdate = true;
- state.currentRenderItemId = -1;
- };
- const renderPick = (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null) => {
- state.disable(gl.BLEND);
- state.enable(gl.DEPTH_TEST);
- state.depthMask(true);
- updateInternal(group, camera, depthTexture, false, false);
- const { renderables } = group;
- for (let i = 0, il = renderables.length; i < il; ++i) {
- if (!renderables[i].state.colorOnly) {
- renderObject(renderables[i], variant);
- }
- }
- };
- const renderDepth = (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);
- const { renderables } = group;
- for (let i = 0, il = renderables.length; i < il; ++i) {
- renderObject(renderables[i], 'depth');
- }
- };
- 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);
- const { renderables } = group;
- for (let i = 0, il = renderables.length; i < il; ++i) {
- const r = renderables[i];
- if (r.values.markerAverage.ref.value !== 1) {
- renderObject(renderables[i], 'markingDepth');
- }
- }
- };
- const renderMarkingMask = (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, !!depthTexture);
- const { renderables } = group;
- for (let i = 0, il = renderables.length; i < il; ++i) {
- const r = renderables[i];
- if (r.values.markerAverage.ref.value > 0) {
- renderObject(renderables[i], 'markingMask');
- }
- }
- };
- const renderBlended = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
- renderBlendedOpaque(group, camera, depthTexture);
- renderBlendedTransparent(group, camera, depthTexture);
- };
- const renderBlendedOpaque = (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);
- const { renderables } = group;
- for (let i = 0, il = renderables.length; i < il; ++i) {
- const r = renderables[i];
- if (r.state.opaque) {
- renderObject(r, 'colorBlended');
- }
- }
- };
- const renderBlendedTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
- state.enable(gl.DEPTH_TEST);
- updateInternal(group, camera, depthTexture, false, false);
- const { renderables } = group;
- if (transparentBackground) {
- state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
- } else {
- state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
- }
- state.enable(gl.BLEND);
- state.depthMask(true);
- for (let i = 0, il = renderables.length; i < il; ++i) {
- const r = renderables[i];
- if (!r.state.opaque && r.state.writeDepth) {
- renderObject(r, 'colorBlended');
- }
- }
- state.depthMask(false);
- for (let i = 0, il = renderables.length; i < il; ++i) {
- const r = renderables[i];
- if (!r.state.opaque && !r.state.writeDepth) {
- renderObject(r, 'colorBlended');
- }
- }
- };
- const renderBlendedVolumeOpaque = (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 !== 1 && !r.values.dXrayShaded?.ref.value) {
- renderObject(r, 'colorBlended');
- }
- }
- };
- 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) {
- renderObject(r, 'colorBlended');
- }
- }
- };
- const renderWboitOpaque = (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);
- 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.dRenderMode?.ref.value !== 'volume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) {
- renderObject(r, 'colorWboit');
- }
- }
- };
- const renderWboitTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
- updateInternal(group, camera, depthTexture, true, 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.dRenderMode?.ref.value === 'volume' || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
- renderObject(r, 'colorWboit');
- }
- }
- };
- return {
- clear: (toBackgroundColor: boolean) => {
- state.enable(gl.SCISSOR_TEST);
- state.enable(gl.DEPTH_TEST);
- state.colorMask(true, true, true, true);
- state.depthMask(true);
- if (transparentBackground) {
- state.clearColor(0, 0, 0, 0);
- } else if (toBackgroundColor) {
- state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1);
- } else {
- state.clearColor(1, 1, 1, 1);
- }
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
- },
- clearDepth: () => {
- state.enable(gl.SCISSOR_TEST);
- state.enable(gl.DEPTH_TEST);
- state.depthMask(true);
- gl.clear(gl.DEPTH_BUFFER_BIT);
- },
- update,
- renderPick,
- renderDepth,
- renderMarkingDepth,
- renderMarkingMask,
- renderBlended,
- renderBlendedOpaque,
- renderBlendedTransparent,
- renderBlendedVolumeOpaque,
- renderBlendedVolumeTransparent,
- renderWboitOpaque,
- renderWboitTransparent,
- setProps: (props: Partial<RendererProps>) => {
- if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) {
- p.backgroundColor = props.backgroundColor;
- Color.toVec3Normalized(bgColor, p.backgroundColor);
- ValueCell.update(globalUniforms.uFogColor, Vec3.copy(globalUniforms.uFogColor.ref.value, bgColor));
- }
- if (props.pickingAlphaThreshold !== undefined && props.pickingAlphaThreshold !== p.pickingAlphaThreshold) {
- p.pickingAlphaThreshold = props.pickingAlphaThreshold;
- ValueCell.update(globalUniforms.uPickingAlphaThreshold, p.pickingAlphaThreshold);
- }
- if (props.interiorDarkening !== undefined && props.interiorDarkening !== p.interiorDarkening) {
- p.interiorDarkening = props.interiorDarkening;
- ValueCell.update(globalUniforms.uInteriorDarkening, p.interiorDarkening);
- }
- if (props.interiorColorFlag !== undefined && props.interiorColorFlag !== p.interiorColorFlag) {
- p.interiorColorFlag = props.interiorColorFlag;
- ValueCell.update(globalUniforms.uInteriorColorFlag, p.interiorColorFlag);
- }
- if (props.interiorColor !== undefined && props.interiorColor !== p.interiorColor) {
- p.interiorColor = props.interiorColor;
- ValueCell.update(globalUniforms.uInteriorColor, Color.toVec3Normalized(globalUniforms.uInteriorColor.ref.value, p.interiorColor));
- }
- if (props.highlightColor !== undefined && props.highlightColor !== p.highlightColor) {
- p.highlightColor = props.highlightColor;
- ValueCell.update(globalUniforms.uHighlightColor, Color.toVec3Normalized(globalUniforms.uHighlightColor.ref.value, p.highlightColor));
- }
- if (props.selectColor !== undefined && props.selectColor !== p.selectColor) {
- p.selectColor = props.selectColor;
- ValueCell.update(globalUniforms.uSelectColor, Color.toVec3Normalized(globalUniforms.uSelectColor.ref.value, p.selectColor));
- }
- if (props.highlightStrength !== undefined && props.highlightStrength !== p.highlightStrength) {
- p.highlightStrength = props.highlightStrength;
- ValueCell.update(globalUniforms.uHighlightStrength, p.highlightStrength);
- }
- if (props.selectStrength !== undefined && props.selectStrength !== p.selectStrength) {
- p.selectStrength = props.selectStrength;
- ValueCell.update(globalUniforms.uSelectStrength, p.selectStrength);
- }
- if (props.markerPriority !== undefined && props.markerPriority !== p.markerPriority) {
- p.markerPriority = props.markerPriority;
- ValueCell.update(globalUniforms.uMarkerPriority, p.markerPriority);
- }
- if (props.xrayEdgeFalloff !== undefined && props.xrayEdgeFalloff !== p.xrayEdgeFalloff) {
- p.xrayEdgeFalloff = props.xrayEdgeFalloff;
- ValueCell.update(globalUniforms.uXrayEdgeFalloff, p.xrayEdgeFalloff);
- }
- if (props.light !== undefined && !deepEqual(props.light, p.light)) {
- p.light = props.light;
- Object.assign(light, getLight(props.light, light));
- ValueCell.update(globalUniforms.uLightDirection, light.direction);
- ValueCell.update(globalUniforms.uLightColor, light.color);
- }
- if (props.ambientColor !== undefined && props.ambientColor !== p.ambientColor) {
- p.ambientColor = props.ambientColor;
- Vec3.scale(ambientColor, Color.toArrayNormalized(p.ambientColor, ambientColor, 0), p.ambientIntensity);
- ValueCell.update(globalUniforms.uAmbientColor, ambientColor);
- }
- if (props.ambientIntensity !== undefined && props.ambientIntensity !== p.ambientIntensity) {
- p.ambientIntensity = props.ambientIntensity;
- Vec3.scale(ambientColor, Color.toArrayNormalized(p.ambientColor, ambientColor, 0), p.ambientIntensity);
- ValueCell.update(globalUniforms.uAmbientColor, ambientColor);
- }
- if (props.clip !== undefined && !deepEqual(props.clip, p.clip)) {
- p.clip = props.clip;
- Object.assign(clip, getClip(props.clip, clip));
- ValueCell.update(globalUniforms.uClipObjectPosition, clip.objects.position);
- ValueCell.update(globalUniforms.uClipObjectRotation, clip.objects.rotation);
- ValueCell.update(globalUniforms.uClipObjectScale, clip.objects.scale);
- ValueCell.update(globalUniforms.uClipObjectType, clip.objects.type);
- }
- },
- setViewport: (x: number, y: number, width: number, height: number) => {
- gl.viewport(x, y, width, height);
- gl.scissor(x, y, width, height);
- if (x !== viewport.x || y !== viewport.y || width !== viewport.width || height !== viewport.height) {
- Viewport.set(viewport, x, y, width, height);
- ValueCell.update(globalUniforms.uViewport, Vec4.set(globalUniforms.uViewport.ref.value, x, y, width, height));
- }
- },
- setTransparentBackground: (value: boolean) => {
- transparentBackground = value;
- },
- setDrawingBufferSize: (width: number, height: number) => {
- if (width !== drawingBufferSize[0] || height !== drawingBufferSize[1]) {
- ValueCell.update(globalUniforms.uDrawingBufferSize, Vec2.set(drawingBufferSize, width, height));
- }
- },
- setPixelRatio: (value: number) => {
- ValueCell.update(globalUniforms.uPixelRatio, value);
- },
- props: p,
- get stats(): RendererStats {
- return {
- programCount: ctx.stats.resourceCounts.program,
- shaderCount: ctx.stats.resourceCounts.shader,
- attributeCount: ctx.stats.resourceCounts.attribute,
- elementsCount: ctx.stats.resourceCounts.elements,
- framebufferCount: ctx.stats.resourceCounts.framebuffer,
- renderbufferCount: ctx.stats.resourceCounts.renderbuffer,
- textureCount: ctx.stats.resourceCounts.texture,
- vertexArrayCount: ctx.stats.resourceCounts.vertexArray,
- drawCount: stats.drawCount,
- instanceCount: stats.instanceCount,
- instancedDrawCount: stats.instancedDrawCount,
- };
- },
- dispose: () => {
- // TODO
- }
- };
- }
- }
- export { Renderer };
|