UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

310 lines (309 loc) 11.9 kB
"use strict"; import { NodeContext } from "./../../../poly/NodeContext"; import { TypeAssert } from "./../../../poly/Assert"; import { RAYMARCHING_UNIFORMS } from "./../../gl/gl/raymarching/uniforms"; import { NodeParamsConfig, ParamConfig } from "../../utils/params/ParamsConfig"; import { PrimitiveMatNode } from "../_Base"; import { Material } from "three"; import { isBooleanTrue } from "../../../../core/Type"; import { CustomMaterialRayMarchingParamConfig } from "./customMaterials/CustomMaterialRayMarching"; import { ThreeToGl } from "../../../../core/ThreeToGl"; var RayMarchingDebugMode = /* @__PURE__ */ ((RayMarchingDebugMode2) => { RayMarchingDebugMode2["STEPS_COUNT"] = "Steps Count"; RayMarchingDebugMode2["DEPTH"] = "Depth"; return RayMarchingDebugMode2; })(RayMarchingDebugMode || {}); const RAYMARCHING_DEBUG_MODES = ["Steps Count" /* STEPS_COUNT */, "Depth" /* DEPTH */]; const DEBUG_STEPS_COUNT = RAYMARCHING_DEBUG_MODES.indexOf("Steps Count" /* STEPS_COUNT */); function generateCubeUVSize(parameters) { const imageHeight = parameters.envMapCubeUVHeight; if (imageHeight === null) return null; const maxMip = Math.log2(imageHeight) - 2; const texelHeight = 1 / imageHeight; const texelWidth = 1 / (3 * Math.max(Math.pow(2, maxMip), 7 * 16)); return { texelWidth, texelHeight, maxMip }; } function setDefines(shaderMaterial, props) { shaderMaterial.defines["ENVMAP_TYPE_CUBE_UV"] = props ? 1 : 0; shaderMaterial.defines["CUBEUV_TEXEL_WIDTH"] = props ? props.texelWidth : ThreeToGl.float(0.1); shaderMaterial.defines["CUBEUV_TEXEL_HEIGHT"] = props ? props.texelHeight : ThreeToGl.float(0.1); shaderMaterial.defines["CUBEUV_MAX_MIP"] = props ? ThreeToGl.float(props.maxMip) : ThreeToGl.float(1); shaderMaterial.defines["ROTATE_ENV_MAP_Y"] = props ? props.tEnvMapRotate : 0; } export function RayMarchingMainParamConfig(Base) { return class Mixin extends Base { constructor() { super(...arguments); /** @param maximum number of steps the raymarcher will run */ this.maxSteps = ParamConfig.INTEGER(RAYMARCHING_UNIFORMS.MAX_STEPS.value, { range: [1, 128], rangeLocked: [true, false] }); /** @param maximum distance the raymarcher will step through */ this.maxDist = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.MAX_DIST.value, { range: [1, 100], rangeLocked: [true, false] }); /** @param when the ray reaches this distance from a surface it will stop marching. You can lower this value to increase the precision of the raymarcher */ this.surfDist = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.SURF_DIST.value, { range: [0, 0.1], rangeLocked: [true, false], step: 1e-7 }); /** @param precision for normals computation */ this.normalsBias = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.NORMALS_BIAS.value, { range: [0, 0.1], rangeLocked: [true, false], step: 1e-7 }); /** @param precision for shadows computation */ this.shadowBias = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.SHADOW_BIAS.value, { range: [-0.1, 0.1], rangeLocked: [false, false], step: 1e-7 }); } }; } export function RayMarchingEnvMapParamConfig(Base) { return class Mixin extends Base { constructor() { super(...arguments); /** @param toggle if you want to use an environment map */ this.useEnvMap = ParamConfig.BOOLEAN(0, { separatorBefore: true // ...BooleanParamOptions(TextureEnvMapController), }); /** @param specify the environment map COP node */ this.envMap = ParamConfig.NODE_PATH("", { visibleIf: { useEnvMap: 1 }, nodeSelection: { context: NodeContext.COP } }); /** @param environment intensity */ this.envMapIntensity = ParamConfig.FLOAT(1, { visibleIf: { useEnvMap: 1 }, cook: false, callback: (node) => RayMarchingController.updateUniformEnvMapIntensity(node) }); /** @param environment roughness */ this.envMapRoughness = ParamConfig.FLOAT(1, { visibleIf: { useEnvMap: 1 }, cook: false, callback: (node) => RayMarchingController.updateUniformEnvMapRoughness(node) }); /** @param allow env map rotation */ this.tEnvMapRotate = ParamConfig.BOOLEAN(0, { visibleIf: { useEnvMap: 1 } }); /** @param env map rotation */ this.envMapRotation = ParamConfig.FLOAT(0, { range: [-Math.PI, Math.PI], rangeLocked: [false, false], step: 1e-4, visibleIf: { useEnvMap: 1, tEnvMapRotate: 1 }, cook: false, callback: (node) => RayMarchingController.updateUniformEnvMapRotate(node) }); } }; } export function RayMarchingDebugParamConfig(Base) { return class Mixin extends Base { constructor() { super(...arguments); /** @param debug mode */ this.debug = ParamConfig.BOOLEAN(0, { separatorBefore: true }); /** @param outputs color showing the number of steps required to solve the raymarching */ this.debugMode = ParamConfig.INTEGER(DEBUG_STEPS_COUNT, { menu: { entries: RAYMARCHING_DEBUG_MODES.map((name, value) => ({ name, value })) }, visibleIf: { debug: true } }); /** @param min steps count */ this.debugMinSteps = ParamConfig.INTEGER(RAYMARCHING_UNIFORMS.debugMinSteps.value, { range: [0, 128], rangeLocked: [true, false], step: 1 // visibleIf: {debug: true, debugMode: DEBUG_STEPS_COUNT}, }); /** @param max steps count */ this.debugMaxSteps = ParamConfig.INTEGER(RAYMARCHING_UNIFORMS.debugMaxSteps.value, { range: [0, 128], rangeLocked: [true, false], step: 1 // visibleIf: {debug: true, debugMode: DEBUG_STEPS_COUNT}, }); /** @param min depth */ this.debugMinDepth = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.debugMinDepth.value, { range: [0, 128], rangeLocked: [true, false], step: 1 // visibleIf: {debug: true, debugMode: DEBUG_DEPTH}, }); /** @param max depth */ this.debugMaxDepth = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.debugMaxDepth.value, { range: [0, 128], rangeLocked: [true, false], step: 1 // visibleIf: {debug: true, debugMode: DEBUG_DEPTH}, }); } }; } class RayMarchingMaterial extends Material { } class RayMarchingParamsConfig extends CustomMaterialRayMarchingParamConfig( RayMarchingDebugParamConfig(RayMarchingEnvMapParamConfig(RayMarchingMainParamConfig(NodeParamsConfig))) ) { } class RayMarchingMatNode extends PrimitiveMatNode { } export class RayMarchingController { constructor(node) { this.node = node; } async updateUniformsFromParams(shaderMaterial) { const uniforms = shaderMaterial.uniforms; if (!uniforms) { return; } const pv = this.node.pv; uniforms.MAX_STEPS.value = pv.maxSteps; uniforms.MAX_DIST.value = pv.maxDist; uniforms.SURF_DIST.value = pv.surfDist; uniforms.NORMALS_BIAS.value = pv.normalsBias; uniforms.SHADOW_BIAS.value = pv.shadowBias; uniforms.shadowDepthMin.value = pv.shadowDepthMin; uniforms.shadowDepthMax.value = pv.shadowDepthMax; uniforms.shadowDistanceMin.value = pv.shadowDistanceMin; uniforms.shadowDistanceMax.value = pv.shadowDistanceMax; this._updateUniforms(shaderMaterial); this._updateDebug(shaderMaterial, uniforms); await this._updateEnvMap(shaderMaterial, uniforms); } _updateDebug(shaderMaterial, uniforms) { const pv = this.node.pv; if (isBooleanTrue(pv.debug)) { let updateDebugMode2 = function(uniforms2) { const debugMode = RAYMARCHING_DEBUG_MODES[pv.debugMode]; switch (debugMode) { case "Steps Count" /* STEPS_COUNT */: { uniforms2.debugMinSteps.value = pv.debugMinSteps; uniforms2.debugMaxSteps.value = pv.debugMaxSteps; shaderMaterial.defines["DEBUG_STEPS_COUNT"] = 1; delete shaderMaterial.defines["DEBUG_DEPTH"]; shaderMaterial.needsUpdate = true; return; } case "Depth" /* DEPTH */: { uniforms2.debugMinDepth.value = pv.debugMinDepth; uniforms2.debugMaxDepth.value = pv.debugMaxDepth; shaderMaterial.defines["DEBUG_DEPTH"] = 1; delete shaderMaterial.defines["DEBUG_STEPS_COUNT"]; shaderMaterial.needsUpdate = true; return; } } TypeAssert.unreachable(debugMode); }; var updateDebugMode = updateDebugMode2; updateDebugMode2(uniforms); } else { if (shaderMaterial.defines["DEBUG_STEPS_COUNT"] != null) { delete shaderMaterial.defines["DEBUG_STEPS_COUNT"]; shaderMaterial.needsUpdate = true; } if (shaderMaterial.defines["DEBUG_DEPTH"] != null) { delete shaderMaterial.defines["DEBUG_DEPTH"]; shaderMaterial.needsUpdate = true; } } } async _updateEnvMap(shaderMaterial, uniforms) { const pv = this.node.pv; setDefines(shaderMaterial, null); const currentDefine = shaderMaterial.defines["ENVMAP_TYPE_CUBE_UV"]; const _fetchTexture = async () => { const pathParam = this.node.p.envMap; if (pathParam.isDirty()) { await pathParam.compute(); } const textureNode = pathParam.value.nodeWithContext(NodeContext.COP); if (textureNode) { const container = await textureNode.compute(); const texture = container.texture(); return texture; } }; const _applyTexture = (texture) => { uniforms["envMap"].value = texture; const props = generateCubeUVSize({ envMapCubeUVHeight: texture.image.height }); setDefines(shaderMaterial, props ? { ...props, tEnvMapRotate: pv.tEnvMapRotate } : null); }; const _removeTexture = () => { uniforms["envMap"].value = null; setDefines(shaderMaterial, null); }; const _updateNeedsUpdateIfRequired = () => { if (currentDefine != shaderMaterial.defines["ENVMAP_TYPE_CUBE_UV"]) { shaderMaterial.needsUpdate = true; } }; if (isBooleanTrue(pv.useEnvMap)) { const texture = await _fetchTexture(); if (texture) { _applyTexture(texture); } else { _removeTexture(); } } else { _removeTexture(); } _updateNeedsUpdateIfRequired(); } /** * * uniforms * */ _updateUniforms(shaderMaterial) { RayMarchingController._updateUniforms(this.node, shaderMaterial); } static _updateUniforms(node, shaderMaterial) { this._updateUniformEnvMapIntensity(node, shaderMaterial); this._updateUniformEnvMapRoughness(node, shaderMaterial); this._updateUniformEnvMapRotate(node, shaderMaterial); } static async updateUniformEnvMapIntensity(node) { this._updateUniformEnvMapIntensity(node, await node.material()); } static _updateUniformEnvMapIntensity(node, shaderMaterial) { const uniforms = shaderMaterial.uniforms; if (!uniforms) { return; } uniforms["envMapIntensity"].value = node.pv.envMapIntensity; } static async updateUniformEnvMapRoughness(node) { this._updateUniformEnvMapRoughness(node, await node.material()); } static _updateUniformEnvMapRoughness(node, shaderMaterial) { const uniforms = shaderMaterial.uniforms; if (!uniforms) { return; } uniforms["roughness"].value = node.pv.envMapRoughness; } static async updateUniformEnvMapRotate(node) { this._updateUniformEnvMapRotate(node, await node.material()); } static _updateUniformEnvMapRotate(node, shaderMaterial) { const uniforms = shaderMaterial.uniforms; if (!uniforms) { return; } uniforms["envMapRotationY"].value = node.pv.envMapRotation; } }