@three3d/volume
Version:
@three3d/volume 提供了 ThreeJS 的云效果,包括:点状云、线状云、热力云
951 lines (725 loc) • 54.5 kB
JavaScript
(function(m,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("three"),require("type-tls"),require("@three3d/tools"),require("image-tls")):typeof define=="function"&&define.amd?define(["exports","three","type-tls","@three3d/tools","image-tls"],n):(m=typeof globalThis<"u"?globalThis:m||self,n(m.Volume={},m.three,m.typeTls,m.tools,m.imageTls))})(this,function(m,n,b,O,M){"use strict";var Ut=Object.defineProperty;var At=(m,n,b)=>n in m?Ut(m,n,{enumerable:!0,configurable:!0,writable:!0,value:b}):m[n]=b;var h=(m,n,b)=>(At(m,typeof n!="symbol"?n+"":n,b),b);const qe=`precision highp sampler3D;
uniform sampler3D map;
uniform vec3 containerMin;
uniform vec3 containerMax;
// 0: 充满;1: 对齐;2: 原始
uniform int fit;
out vec3 cameraPos;
out vec3 lookDir;
out vec3 displayMin;
out vec3 displayMax;
void main() {
cameraPos = vec3( inverse( modelMatrix ) * vec4( cameraPosition, 1.0 ) ).xyz;
lookDir = position - cameraPos;
vec3 containerSize = containerMax - containerMin;
vec3 origin = containerMin;
vec3 textSize = vec3(textureSize(map,0));
displayMin = containerMin;
displayMax = containerMax;
switch (fit) {
case 1:
displayMax = displayMin + textSize;
break;
case 2:
origin = vec3(0.0);
break;
default:
textSize = containerSize;
}
vec3 scale = 1.0/textSize;
cameraPos = (cameraPos - origin)*scale;
lookDir *= scale;
displayMin = max((displayMin - origin)*scale,vec3(0.0));
displayMax = min((displayMax - origin)*scale,vec3(1.0));
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`;var ye=(r=>(r[r.Fill=0]="Fill",r[r.Align=1]="Align",r[r.Raw=2]="Raw",r))(ye||{});class de extends n.ShaderMaterial{constructor(e){const{map:t,opacity:i,accFactor:a,steps:o,alphaRange:s,fit:c,atomize:d,side:u,containerMin:p,containerMax:f,uniforms:v,...g}=e??{},y=i??1,S=a??1,P=o??100,C=new n.Vector2().copy(s??{x:0,y:.95}),D=c??0,_=d??!0,z=u??n.FrontSide,x=new n.Vector3().copy(p??{x:0,y:0,z:0}),w=new n.Vector3;if(f)w.copy(f);else if(t){const{width:F,height:B,depth:N}=t.image,E=new n.Vector3(F-1,B-1,N-1);w.addVectors(x,E)}const A={map:{value:t},containerMin:{value:x},containerMax:{value:w},fit:{value:D},opacity:{value:y},alphaRange:{value:C},accFactor:{value:S},steps:{value:P},isDoubleSide:{value:!1},atomize:{value:_},...v};super({glslVersion:n.GLSL3,uniforms:A,transparent:!0,vertexShader:qe,...g});h(this,"isVolumeMaterial",!0);h(this,"_side",n.FrontSide);this.opacity=y,this.side=z}get map(){return this.uniforms.map.value}set map(e){this.uniforms.map.value=e,this.uniformsNeedUpdate=!0}get containerMin(){return this.uniforms.containerMin.value}set containerMin(e){this.uniforms.containerMin.value.copy(e),this.uniformsNeedUpdate=!0}get containerMax(){return this.uniforms.containerMax.value}set containerMax(e){this.uniforms.containerMax.value.copy(e),this.uniformsNeedUpdate=!0}get containerSize(){return this.containerMax.clone().sub(this.containerMin)}get fit(){return this.uniforms.fit.value}set fit(e){this.uniforms.fit.value=e,this.uniformsNeedUpdate=!0}getFitTranslate(){return this.fit===2?new n.Vector3:this.containerMin.clone()}getFitScale(){var o;const e=new n.Vector3(1,1,1),t=(o=this.map)==null?void 0:o.image,i=this.fit;if(!t||i===2||i===1)return e;const a=new n.Vector3(t.width,t.height,t.depth);return e.copy(this.containerSize).divide(a),e}getFitMatrix(){var a;const e=new n.Matrix4,t=(a=this.map)==null?void 0:a.image,i=this.fit;if(!t||i===2)return e;if(i===0){const o=new n.Vector3(t.width,t.height,t.depth),c=this.containerSize.divide(o);e.makeScale(c.x,c.y,c.z)}return e.setPosition(this.containerMin),e}getFitMatrixInvert(){return this.getFitMatrix().invert()}get side(){return this._side}set side(e){this._side=e,this.uniforms&&(this.uniforms.isDoubleSide.value=e===n.DoubleSide,this.uniformsNeedUpdate=!0)}get opacity(){return this.uniforms.opacity.value}set opacity(e){this.uniforms&&(this.uniforms.opacity.value=e),this.uniformsNeedUpdate=!0}get alphaRange(){return this.uniforms.alphaRange.value}set alphaRange(e){this.uniforms.alphaRange.value.copy(e),this.uniformsNeedUpdate=!0}get atomize(){return this.uniforms.atomize.value}set atomize(e){this.uniforms.atomize.value=e,this.uniformsNeedUpdate=!0}get steps(){return this.uniforms.steps.value}set steps(e){this.uniforms.steps.value=e,this.uniformsNeedUpdate=!0}get accFactor(){return this.uniforms.accFactor.value}set accFactor(e){this.uniforms&&(this.uniforms.accFactor.value=e),this.uniformsNeedUpdate=!0}toMapPosition(e){const t=new n.Vector3(e.x,e.y,e.z).applyMatrix4(this.getFitMatrixInvert());return t.set(Math.trunc(t.x),Math.trunc(t.y),Math.trunc(t.z)),t}toMapDepth(e,t){const i=b.Axis.toKey(e),a=new n.Vector3;a[i]=1;const o=new n.Matrix3().setFromMatrix4(this.getFitMatrix());return a.applyMatrix3(o),t/=a.length(),Math.trunc(t)}getData3DSlice(e,t){const i=this.map;return i!=null&&i.image?(t=this.toMapDepth(e,t),O.get3DTextureSlice(i,e,t)):null}getItem(e){const t=this.map;return t!=null&&t.image?O.get3DTextureItem(t,this.toMapPosition(e)).value:null}}function Me(r){r.boundingBox||r.computeBoundingBox();const{x:l,y:e,z:t}=r.boundingBox.min;return r.translate(-l,-e,-t),r}class Xe extends n.Mesh{constructor(e){var s;const{width:t,height:i,depth:a}=((s=e.map)==null?void 0:s.image)||{},o=new n.BoxGeometry(t,i,a);Me(o);super(o,e);h(this,"isVolumeMesh",!0);h(this,"_geometry");h(this,"autoUpdateMaterial",!0);h(this,"_material");h(this,"autoUpdateGeometry",!0);h(this,"autoNormalize",!1);Object.defineProperties(this,{geometry:{get:()=>this._geometry,set:c=>{this._geometry=c,this.autoNormalize?this.normalize():this.autoUpdateMaterial&&this.updateMaterial()}},material:{get:()=>this._material,set:c=>{this._material=c,this.autoUpdateGeometry&&this.updateGeometry()}}}),this.geometry=o,this.material=e}updateMaterial(){const{geometry:e,material:t}=this;if(!(t&&e))return!1;e.boundingBox||e.computeBoundingBox();const{min:i,max:a}=e.boundingBox;t.containerMin=i,t.containerMax=a}updateGeometry(){var u;const{geometry:e,material:t,autoUpdateMaterial:i}=this,a=(u=t.map)==null?void 0:u.image;if(!a)return!1;e.boundingBox||e.computeBoundingBox();const{width:o,height:s,depth:c}=a,d=e.boundingBox.getSize(new n.Vector3);e.scale(o/d.x,s/d.y,c/d.z),i&&this.updateMaterial()}normalize(){const{geometry:e,autoUpdateMaterial:t}=this;e.boundingBox||e.computeBoundingBox();const{x:i,y:a,z:o}=e.boundingBox.min;e.translate(-i,-a,-o),t&&this.updateMaterial()}toMapPosition(e){const t=this.worldToLocal(new n.Vector3(e.x,e.y,e.z));return this.material.toMapPosition(t)}toMaterialDepth(e,t){const i=b.Axis.toKey(e),a=new n.Vector3;a[i]=1;const o=new n.Matrix3().setFromMatrix4(this.matrixWorld);return a.applyMatrix3(o),t/=a.length(),t}toMapDepth(e,t){return t=this.toMaterialDepth(e,t),this.material.toMapDepth(e,t)}getData3DSlice(e,t){return t=this.toMaterialDepth(e,t),this.material.getData3DSlice(e,t)}getItem(e){const t=this.worldToLocal(new n.Vector3(e.x,e.y,e.z));return this.material.getItem(t)}}const $e=`precision highp float;
precision highp sampler3D;
uniform sampler3D map;
uniform sampler2D gradient;
uniform bool atomize;
uniform float steps;
// 颜色累积系数
uniform float accFactor;
uniform vec2 range;
// 是否丢弃超出范围的像素
uniform bool discardOut;
// 空值范围
uniform vec2 voidRange;
uniform float opacity;
uniform vec2 alphaRange;
uniform bool isDoubleSide;
in vec3 cameraPos;
in vec3 lookDir;
in vec3 displayMin;
in vec3 displayMax;
out vec4 fragColor;
vec4 colorBlend(vec4 near,vec4 far){
float nA = near.a;
if (nA >= 1.0){
return near;
}
if (nA <= 0.0){
return far;
}
float fA = far.a;
float a = fA + nA - fA*nA;
vec3 color = (far.rgb * fA * (1.0 - nA) + near.rgb*nA)/a;
return vec4(color,a);
}
vec2 intersectBox( vec3 orig, vec3 dir ) {
vec3 inv_dir = 1.0 / dir;
vec3 tmin_tmp = (displayMin - orig ) * inv_dir;
vec3 tmax_tmp = (displayMax - orig ) * inv_dir;
vec3 tmin = min( tmin_tmp, tmax_tmp );
vec3 tmax = max( tmin_tmp, tmax_tmp );
float t0 = max( tmin.x, max( tmin.y, tmin.z ) );
float t1 = min( tmax.x, min( tmax.y, tmax.z ) );
return vec2( t0, t1 );
}
float getValue( vec3 point ) {
return texture( map, point ).r;
}
// 获取梯度颜色
vec4 getGradientColor(float val) {
val = clamp( val, 0.0,1.0);
return texture2D(gradient, vec2(val, 0.5));
}
void main(){
vec3 rayDir = normalize( lookDir );
vec2 times = intersectBox( cameraPos, rayDir );
float tMin = times.x;
float tMax = times.y;
if ( tMin > tMax || tMax < 0.0 ) discard;
tMin = max( tMin, 0.0 );
vec3 point = cameraPos + tMin * rayDir;
float step = 1.0/steps;
float opacityFactor = atomize ? step * accFactor : 1.0;
float invRangeLen = 1.0/(range.y - range.x);
vec3 stepDir = rayDir * step;
vec4 finalColor = vec4( 0,0,0,0);
float alphaMax = alphaRange.y;
for ( float t = tMin; t <= tMax; t += step,point += stepDir ) {
float val = getValue( point);
if (voidRange.x <= val && val <= voidRange.y ) continue;
val = (val - range.x) * invRangeLen;
if (discardOut && (val < 0.0 || val > 1.0)) continue;
vec4 gradientColor = getGradientColor(val);
gradientColor.a *= opacityFactor;
finalColor = colorBlend(finalColor,gradientColor);
if ( finalColor.a >= alphaMax ) break;
}
finalColor.a *= opacity;
if ( finalColor.a <= alphaRange.x ) discard;
if (isDoubleSide){
finalColor.a = 1.0 - sqrt(1.0 - finalColor.a);
}
fragColor = finalColor;
}`;class Ye extends de{constructor(e){const{gradient:t,range:i,discardOut:a,voidRange:o,...s}=e??{},c=new n.Vector2().copy(i??{x:0,y:100}),d=a??!0,u=new n.Vector2().copy(o??{x:-100,y:-1}),f={gradient:{value:t==null?t:O.createLinearGradientTexture(t)},range:{value:c},discardOut:{value:d},voidRange:{value:u}};super({...s,fragmentShader:$e,uniforms:f});h(this,"isGradientVolumeMaterial",!0)}get gradient(){return this.uniforms.gradient.value}set gradient(e){this.uniforms.gradient.value=e,this.uniformsNeedUpdate=!0}get range(){return this.uniforms.range.value}set range(e){this.uniforms.range.value.copy(e),this.uniformsNeedUpdate=!0}get discardOut(){return this.uniforms.discardOut.value}set discardOut(e){this.uniforms.discardOut.value=e,this.uniformsNeedUpdate=!0}get voidRange(){return this.uniforms.voidRange.value}set voidRange(e){this.uniforms.voidRange.value.copy(e),this.uniformsNeedUpdate=!0}}class ze extends n.Data3DTexture{constructor(e,t,i,a,o){super(e,t,i,a);h(this,"isGradientData3DTexture",!0);this.format=n.RedFormat,this.type=o??n.FloatType,this.minFilter=this.magFilter=n.LinearFilter,this.unpackAlignment=1,this.needsUpdate=!0}getData3DSlice(e,t){const{data:i,width:a,height:o,depth:s}=this.image;return M.getData3DSlice({data:i,size:{x:a,y:o,z:s}},e,t,1)}getItem(e){const{data:t,width:i,height:a,depth:o}=this.image;return M.getData3DItemSafe({data:t,size:{x:i,y:a,z:o}},e).value}getValue(e){const{data:t,width:i,height:a,depth:o}=this.image;return M.getData3DValueSafe({data:t,size:{x:i,y:a,z:o}},e).value}}function Ze(r,l){const{data:e,size:t}=r,{x:i,y:a,z:o}=t,s=i*a*o,{voidValue:c=0,uint8:d}=l??{};let u;if(d){u=new Uint8Array(s);for(let f=0;f<s;f++){const v=e[f]??c;u[f]=Math.trunc(v)}}else{u=new Float32Array(s);for(let f=0;f<s;f++){const v=e[f]??c;u[f]=v}}const p=d?n.UnsignedByteType:n.FloatType;return new ze(u,i,a,o,p)}const We=`precision highp float;
precision highp sampler3D;
uniform sampler3D map;
// 颜色累积系数
uniform float accFactor;
uniform bool isDoubleSide;
uniform bool atomize;
uniform float opacity;
uniform vec2 alphaRange;
uniform float steps;
in vec3 cameraPos;
in vec3 lookDir;
in vec3 displayMin;
in vec3 displayMax;
out vec4 fragColor;
// 混合颜色
vec4 colorBlend(vec4 near,vec4 far){
float nA = near.a;
if (nA >= 1.0){
return near;
}
if (nA <= 0.0){
return far;
}
float fA = far.a;
float a = fA + nA - fA*nA;
vec3 color = (far.rgb * fA * (1.0 - nA) + near.rgb*nA)/a;
return vec4(color,a);
}
// 包围盒求交
vec2 intersectBox( vec3 orig, vec3 dir ) {
vec3 inv_dir = 1.0 / dir;
vec3 tmin_tmp = (displayMin - orig ) * inv_dir;
vec3 tmax_tmp = (displayMax - orig ) * inv_dir;
vec3 tmin = min( tmin_tmp, tmax_tmp );
vec3 tmax = max( tmin_tmp, tmax_tmp );
float t0 = max( tmin.x, max( tmin.y, tmin.z ) );
float t1 = min( tmax.x, min( tmax.y, tmax.z ) );
return vec2( t0, t1 );
}
void main(){
vec3 rayDir = normalize( lookDir );
vec2 times = intersectBox( cameraPos, rayDir );
float tMin = times.x;
float tMax = times.y;
if ( tMin > tMax || tMax < 0.0 ) discard;
tMin = max( tMin, 0.0 );
vec3 point = cameraPos + tMin * rayDir;
float step = 1.0/steps;
float opacityFactor = atomize ? step * accFactor : 1.0;
vec3 stepDir = rayDir * step;
vec4 finalColor = vec4( 0,0,0,0);
float alphaMax = alphaRange.y;
for ( float t = tMin; t <= tMax; t += step,point += stepDir ) {
vec4 textureColor = texture( map, point );
textureColor.a *= opacityFactor;
finalColor = colorBlend(finalColor,textureColor);
if ( finalColor.a >= alphaMax ) break;
}
finalColor.a *= opacity;
if ( finalColor.a <= alphaRange.x ) discard;
if (isDoubleSide){
finalColor.a = 1.0 - sqrt(1.0 - finalColor.a);
}
fragColor = finalColor;
}`;class Ke extends de{constructor(e){super({...e,fragmentShader:We});h(this,"isImageVolumeMaterial",!0)}}function Se(r,l,e){const t=e??{},{x:i,y:a}=t.range??{x:0,y:100},o=t.discardOut??!0,{x:s,y:c}=t.voidRange??{x:-100,y:-1},d=t.voidColor??[0,0,0,0],{width:u,height:p}=l,f=Math.trunc(p/2),v=u-1;function g(_){const z=Math.trunc(v*_);return M.getImageDateColor(l,f,z)}const y=1/(a-i),{data:S,size:P}=r,C=S.length,D=[];for(let _=0;_<C;_++){let z=d,x=S[_];(x<=s||c<=x)&&(x=(x-i)*y,o&&(x<0||x>1)||(x=Math.min(Math.max(x,0),1),z=g(x))),D.push(...z)}return{data:D,size:P}}class fe extends n.Data3DTexture{constructor(e,t,i,a){super(e,t,i,a);h(this,"isImageData3DTexture",!0);this.format=n.RGBAFormat,this.type=n.UnsignedByteType,this.minFilter=this.magFilter=n.LinearFilter,this.unpackAlignment=1,this.needsUpdate=!0}getData3DSlice(e,t){const{data:i,width:a,height:o,depth:s}=this.image;return M.getData3DSlice({data:i,size:{x:a,y:o,z:s}},e,t,4)}getItem(e){return this.getColor(e)}getColor(e){const{data:t,width:i,height:a,depth:o}=this.image;return M.getData3DItemSafe({data:t,size:{x:i,y:a,z:o}},e,4).value}}function Qe(r,l,e){const t=(e==null?void 0:e.reverseY)??!0,{data:i,width:a,height:o,depth:s,colorSpace:c}=M.extrudeImage(r,l,{...e,reverseY:t}),d=new fe(i,a,o,s);return c==="srgb"&&(d.colorSpace="srgb"),d}function Je(r,l){const e=l.gradient,t=Array.isArray(e)?M.createLinearGradientImageData(e,256,1):M.isIImageData2(e)?e:M.getImageDateOfImage(e);let i=r;M.isIImageData3(r)&&(i={data:r.data,size:{x:r.width,y:r.height,z:r.depth}});const{data:a,size:o}=Se(i,t,l),s=Uint8ClampedArray.from(a);return new fe(s,o.x,o.y,o.z)}const et=`out vec3 lookDir;
out vec3 normalDir;
#if defined( USE_COLOR_ALPHA ) || defined( USE_COLOR )
out vec4 verColor;
#endif
void main() {
vec3 cameraPos = vec3( inverse( modelMatrix ) * vec4( cameraPosition, 1.0 ) ).xyz;
lookDir = position - cameraPos;
normalDir = normal;
#if defined( USE_COLOR_ALPHA )
verColor = color;
#elif defined( USE_INSTANCING_COLOR )
overColor = vec3(instanceColor,1.0);
#elif defined( USE_COLOR )
overColor = vec3(color,1.0);
#endif
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`,tt=`uniform vec3 color;
uniform float solid;
uniform float exp;
uniform float opacity;
uniform bool isDoubleSide;
in vec3 lookDir;
in vec3 normalDir;
#if defined( USE_COLOR_ALPHA ) || defined( USE_COLOR )
in vec4 verColor;
#endif
out vec4 fragColor;
void main(){
#if defined( USE_COLOR_ALPHA ) || defined( USE_COLOR )
fragColor = verColor;
#else
fragColor = vec4(color,1.0);
#endif
vec3 dir = normalize(lookDir);
vec3 nor = normalize(normalDir);
float depth = abs(dot(nor,dir));
float dist = sqrt(1.0 - pow(depth,2.0));
float finalAlpha = fragColor.a * depth;
if (dist > solid) {
float distAlpha = 1.0 - (dist - solid)/(1.0 - solid);
finalAlpha *= pow(distAlpha,exp);
}
finalAlpha *= opacity;
if ( finalAlpha == 0.0 ) discard;
if (isDoubleSide){
finalAlpha = 1.0 - sqrt(1.0 - finalAlpha);
}
fragColor.a = finalAlpha;
}`;class nt extends n.ShaderMaterial{constructor(e){const t=e??{},i=t.color??new n.Color(1,1,1),a=t.solid??0,o=t.exp??1,s=t.opacity??1,c=t.side??n.FrontSide,d={color:{value:i},solid:{value:a},exp:{value:o},opacity:{value:s},isDoubleSide:{value:!1}};super({glslVersion:n.GLSL3,uniforms:d,vertexShader:et,fragmentShader:tt,side:n.FrontSide,transparent:!0});h(this,"isSphereFogMaterial",!0);h(this,"_side",n.FrontSide);this.opacity=s,this.side=c}get side(){return this._side}set side(e){this._side=e,this.uniforms&&(this.uniforms.isDoubleSide.value=e===n.DoubleSide,this.uniformsNeedUpdate=!0)}get color(){return this.uniforms.color.value}set color(e){this.uniforms.color.value.copy(e),this.uniformsNeedUpdate=!0}get opacity(){return this.uniforms.opacity.value}set opacity(e){this.uniforms&&(this.uniforms.opacity.value=e),this.uniformsNeedUpdate=!0}get solid(){return this.uniforms.solid.value}set solid(e){this.uniforms&&(this.uniforms.solid.value=e),this.uniformsNeedUpdate=!0}get exp(){return this.uniforms.exp.value}set exp(e){this.uniforms&&(this.uniforms.exp.value=e),this.uniformsNeedUpdate=!0}}class pe extends n.ShaderMaterial{constructor(e,t){const{solid:i=0,exp:a=1,useRadius:o=!0,sizeAttenuation:s=!0,star:c=!1,...d}=e??{},{uniforms:u,...p}=t||{},f={star:{value:c},solid:{value:i},exp:{value:a}};super({uniforms:n.UniformsUtils.merge([n.ShaderLib.points.uniforms,f,u]),transparent:!0,depthTest:!1,defines:{useRadius:o},...p});h(this,"isFogPointsMaterial",!0);h(this,"sizeAttenuation",!0);Object.assign(this,{...d,solid:i,exp:a,sizeAttenuation:s})}get map(){return this.uniforms.map.value}set map(e){this.uniforms.map.value=e,this.uniformsNeedUpdate=!0}get star(){return this.uniforms.star.value}set star(e){this.uniforms.star.value=e,this.uniformsNeedUpdate=!0}get size(){return this.uniforms.size.value}set size(e){this.uniforms.size.value=e,this.uniformsNeedUpdate=!0}get opacity(){return this.uniforms.opacity.value}set opacity(e){this.uniforms&&(this.uniforms.opacity.value=e),this.uniformsNeedUpdate=!0}get solid(){return this.uniforms.solid.value}set solid(e){this.uniforms&&(this.uniforms.solid.value=e),this.uniformsNeedUpdate=!0}get exp(){return this.uniforms.exp.value}set exp(e){this.uniforms&&(this.uniforms.exp.value=e),this.uniformsNeedUpdate=!0}get scale(){return this.uniforms.scale.value}set scale(e){this.uniforms&&(this.uniforms.scale.value=e),this.uniformsNeedUpdate=!0}get fogDensity(){return this.uniforms.fogDensity.value}set fogDensity(e){this.uniforms&&(this.uniforms.fogDensity.value=e),this.uniformsNeedUpdate=!0}get fogNear(){return this.uniforms.fogNear.value}set fogNear(e){this.uniforms&&(this.uniforms.fogNear.value=e),this.uniformsNeedUpdate=!0}get fogFar(){return this.uniforms.fogFar.value}set fogFar(e){this.uniforms&&(this.uniforms.fogFar.value=e),this.uniformsNeedUpdate=!0}get fogColor(){return this.uniforms.fogColor.value}set fogColor(e){this.uniforms.fogColor.value.copy(e),this.uniformsNeedUpdate=!0}}const it=`#ifdef useRadius
attribute float radius;
#endif
uniform float size;
uniform float scale;
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <morphtarget_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
#ifdef USE_POINTS_UV
varying vec2 vUv;
uniform mat3 uvTransform;
#endif
void main() {
#ifdef USE_POINTS_UV
vUv = ( uvTransform * vec3( uv, 1 ) ).xy;
#endif
#include <color_vertex>
#include <morphcolor_vertex>
#include <begin_vertex>
#include <morphtarget_vertex>
#include <project_vertex>
#ifdef useRadius
gl_PointSize = radius;
#else
gl_PointSize = size;
#endif
#ifdef USE_SIZEATTENUATION
bool isPerspective = isPerspectiveMatrix( projectionMatrix );
if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );
#endif
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
#include <worldpos_vertex>
#include <fog_vertex>
}`,at=`uniform float solid;
uniform float exp;
uniform bool star;
uniform vec3 diffuse;
uniform float opacity;
#include <common>
#include <color_pars_fragment>
#include <map_particle_pars_fragment>
#include <alphatest_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
vec4 getColor(vec4 originColor){
float finalAlpha = originColor.a;
float dist = 0.0;
if (star){
vec2 coord = abs(gl_PointCoord - vec2(0.5));
dist = max(coord.x,coord.y);
}else{
dist = distance(gl_PointCoord,vec2(0.5));
if (dist>0.5) discard;
}
dist /= 0.5;
float depth = sqrt(1.0 - pow(dist,2.0));
finalAlpha *= depth;
if (dist > solid) {
float distAlpha = 1.0 - (dist - solid)/(1.0 - solid);
finalAlpha *= pow(distAlpha,exp);
}
originColor.a = finalAlpha;
return originColor;
}
void main() {
#include <clipping_planes_fragment>
vec3 outgoingLight = vec3( 0.0 );
vec4 diffuseColor = vec4( diffuse, opacity );
#include <logdepthbuf_fragment>
#include <map_particle_fragment>
#include <color_fragment>
#include <alphatest_fragment>
diffuseColor = getColor(diffuseColor);
outgoingLight = diffuseColor.rgb;
#include <output_fragment>
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
}`;class we extends pe{constructor(e){super(e,{vertexShader:it,fragmentShader:at});h(this,"isColorFogPointsMaterial",!0)}get color(){return this.uniforms.diffuse.value}set color(e){this.uniforms.diffuse.value.copy(e),this.uniformsNeedUpdate=!0}get alphaMap(){return this.uniforms.alphaMap.value}set alphaMap(e){this.uniforms&&(this.uniforms.alphaMap.value=e),this.uniformsNeedUpdate=!0}get uvTransform(){return this.uniforms.uvTransform.value}set uvTransform(e){this.uniforms&&this.uniforms.uvTransform.value.copy(e),this.uniformsNeedUpdate=!0}}function ot(r){const{points:l,radius:e=10,color:t=[0,0,0,0]}=r,i=[],a=[],o=[];for(const{x:c,y:d,z:u,radius:p,color:f}of l){i.push(c,d,u),a.push(p??e);const v=f??t,g=v[3];v[3]=Math.trunc(g*255),o.push(...v)}const s=new n.BufferGeometry;return s.setAttribute("position",new n.Float32BufferAttribute(i,3)),s.setAttribute("radius",new n.Float32BufferAttribute(a,1)),s.setAttribute("color",new n.Uint8ClampedBufferAttribute(o,4,!0)),s}function rt(r){const l=new we(r),e=ot(r);return new n.Points(e,l)}const st=`attribute float value;
uniform vec2 range;
varying float vValue;
#ifdef useRadius
attribute float radius;
#endif
uniform float size;
uniform float scale;
#include <common>
// #include <color_pars_vertex>
#include <fog_pars_vertex>
#include <morphtarget_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
#ifdef USE_POINTS_UV
varying vec2 vUv;
uniform mat3 uvTransform;
#endif
void main() {
vValue = (value - range.x)/(range.y - range.x);
#ifdef USE_POINTS_UV
vUv = ( uvTransform * vec3( uv, 1 ) ).xy;
#endif
// #include <color_vertex>
// #include <morphcolor_vertex>
#include <begin_vertex>
#include <morphtarget_vertex>
#include <project_vertex>
#ifdef useRadius
gl_PointSize = radius;
#else
gl_PointSize = size;
#endif
#ifdef USE_SIZEATTENUATION
bool isPerspective = isPerspectiveMatrix( projectionMatrix );
if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );
#endif
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
#include <worldpos_vertex>
#include <fog_vertex>
}`,lt=`varying float vValue;
uniform sampler2D map;
uniform float solid;
uniform float exp;
uniform bool star;
// uniform vec3 diffuse;
uniform float opacity;
#include <common>
// #include <color_pars_fragment>
// #include <map_particle_pars_fragment>
#include <alphatest_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
// 获取梯度颜色
vec4 getGradientColor(float val) {
val = clamp( val, 0.0,1.0);
return texture2D(map, vec2(val, 0.5));
}
vec4 getColor(){
float val = vValue;
float dist = 0.0;
if (star){
vec2 coord = abs(gl_PointCoord - vec2(0.5));
dist = max(coord.x,coord.y);
}else{
dist = distance(gl_PointCoord,vec2(0.5));
if (dist>0.5) discard;
}
dist /= 0.5;
float depth = sqrt(1.0 - pow(dist,2.0));
if (dist > solid) {
float solidDist = 1.0 - (dist - solid)/(1.0 - solid);
val *= pow(solidDist,exp);
}
vec4 valColor = getGradientColor(val);
valColor.a *= depth * opacity;
return valColor;
}
void main() {
#include <clipping_planes_fragment>
vec3 outgoingLight = vec3( 0.0 );
vec4 diffuseColor = getColor();
#include <logdepthbuf_fragment>
// #include <map_particle_fragment>
// #include <color_fragment>
#include <alphatest_fragment>
outgoingLight = diffuseColor.rgb;
#include <output_fragment>
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
}`;class De extends pe{constructor(e){const{range:t,...i}=e||{},o={range:{value:new n.Vector2().copy(t??{x:0,y:100})}};super(i,{vertexShader:st,fragmentShader:lt,uniforms:o});h(this,"isColorFogPointsMaterial",!0)}get range(){return this.uniforms.range.value}set range(e){this.uniforms.range.value.copy(e),this.uniformsNeedUpdate=!0}}function ct(r){const{points:l,radius:e=10,value:t=100}=r,i=[],a=[],o=[];for(const{x:c,y:d,z:u,radius:p,value:f}of l)i.push(c,d,u),a.push(p??e),o.push(f??t);const s=new n.BufferGeometry;return s.setAttribute("position",new n.Float32BufferAttribute(i,3)),s.setAttribute("radius",new n.Float32BufferAttribute(a,1)),s.setAttribute("value",new n.Float32BufferAttribute(o,1)),s}function ut(r){const l=r.gradient,e=l==null?l:O.createLinearGradientTexture(l),t=new De({...r,map:e}),i=ct(r);return new n.Points(i,t)}const mt=` out vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`,dt=`precision highp float;
precision highp sampler3D;
uniform sampler3D map;
uniform float opacity;
uniform int axis;
uniform float depth;
in vec2 vUv;
out vec4 fragColor;
void main(){
vec3 uv3 = vec3(0.0);
int depthSize = textureSize(map,0)[axis];
uv3[axis] = depth/float(depthSize);
int xIndex = (axis + 1)%3;
int yIndex = (axis + 2)%3;
uv3[xIndex] = vUv.x;
uv3[yIndex] = vUv.y;
fragColor = texture(map, uv3);
fragColor.a *= opacity;
}`;class be extends n.ShaderMaterial{constructor(e){const t=e??{};let{map:i,opacity:a,axis:o,depth:s,side:c,...d}=t;a=a??1,o=o??b.Axis.z,s=s??0,c=c??n.DoubleSide;const u={map:{value:i},opacity:{value:a},axis:{value:o},depth:{value:s}};super({...d,glslVersion:n.GLSL3,uniforms:u,transparent:!0,vertexShader:mt,fragmentShader:dt,side:c});h(this,"isSliceMaterial",!0);h(this,"_sliceSize",null);this.opacity=a}get map(){return this.uniforms.map.value}set map(e){this.uniforms.map.value=e,this._sliceSize=null,this.uniformsNeedUpdate=!0}get opacity(){return this.uniforms.opacity.value}set opacity(e){this.uniforms&&(this.uniforms.opacity.value=e),this.uniformsNeedUpdate=!0}get axis(){return this.uniforms.axis.value}set axis(e){this.uniforms.axis.value=e,this._sliceSize=null,this.uniformsNeedUpdate=!0}get depth(){return this.uniforms.depth.value}set depth(e){this.uniforms.depth.value=e,this.uniformsNeedUpdate=!0}get sliceSize(){let e=this._sliceSize;if(!e){this._sliceSize=e=new n.Vector2(1,1);const{axis:t,map:i}=this;if(i){const{width:a,height:o,depth:s}=i.image,[c,d]=b.Axis.getCrossAxiss(t),u=[a,o,s];e.set(u[c],u[d])}}return e}}class ft extends n.Mesh{constructor(e){const t=new be(e),i=new n.PlaneGeometry;super(i,t);h(this,"isSliceMesh",!0);h(this,"_geometry");h(this,"_material");h(this,"autoUpdateGeometry",!0);Object.defineProperties(this,{geometry:{get:()=>this._geometry,set:a=>{this._geometry=a,this.autoUpdateGeometry&&this.updateGeometry()}},material:{get:()=>this._material,set:a=>{this._material=a,this.autoUpdateGeometry&&this.updateGeometry()}}}),this.geometry=i,this.material=t}updateGeometry(){var s;const{geometry:e,material:t}=this;if(!t||!((s=t.map)==null?void 0:s.image))return!1;e.boundingBox||e.computeBoundingBox();const a=t.sliceSize,o=e.boundingBox.getSize(new n.Vector3);e.scale(a.x/o.x,a.y/o.y,1)}get map(){return this.material.map}set map(e){this.material.map=e,this.material=this.material}get axis(){return this.material.axis}set axis(e){this.material.axis=e,this.material=this.material}get depth(){return this.material.depth}set depth(e){this.material.depth=e}get sliceSize(){return this.material.sliceSize}}class pt extends M.ImageData3DSlice{constructor(e){const t=M.isIImageData3(e)?e:e.image;super(t);h(this,"isImageData3DTextureSlice",!0)}set texture(e){this.image3D=M.isIImageData3(e)?e:e.image}}function ve({ratio:r,value:l}){return l-l*r}function Pe(r,l){const{x:e,y:t}=l;let i=0;for(const{value:a,clim:{x:o,y:s}}of r)i+=(a-o)/(s-o);return i*(t-e)+e}function Ce(r,l,e){const{size:t,data3D:i,tags:a}=l,{clim:o,radius:s,value:c,hollow:d,valueGradient:u,valuesAccumulate:p}=r,f=new n.Vector3().copy(r),v=[],g=[],y={particles:v,values:g},S=s*d,P=f.clone().subScalar(s),C=f.clone().addScalar(s);P.max(new n.Vector3(0,0,0));const D=new n.Vector3().copy(t).subScalar(1);C.min(D);let{x:_,y:z,z:x}=P;_=Math.trunc(_),z=Math.trunc(z),x=Math.trunc(x);let{x:w,y:A,z:F}=C;if(w=Math.trunc(w),A=Math.trunc(A),F=Math.trunc(F),_>w||z>A||x>F)return y;const{x:B,y:N}=t,E=B*N;for(let I=x;I<=F;I++){const he=I*E;for(let R=z;R<=A;R++){const Q=he+R*B;e:for(let U=_;U<=w;U++){const H=Q+U;if(a[H])continue;const G=new n.Vector3(U,R,I),V=G.distanceTo(f);if(V>s||V<S)continue;a[H]=!0;const q={radius:s,hollow:d,hollowRadius:S,distance:V,clim:o,value:c},L=[],X={...q,ratio:V/s,distance:V,point:G},$=u(X);L.push({...r,...X,value:$});for(const k of e){const j=G.distanceTo(k),{hollow:ge,radius:Z}=k;if(j<=Z){const W=Z*ge;if(j<W)continue e;const T={...k,ratio:j/Z,distance:j,hollowRadius:W,point:G},xe=u(T);L.push({...T,value:xe})}}const Y=p(L,o);i[H]=Y,v.push(G),g.push(Y)}}}return y}function vt(r){const{points:l,clim:e={x:0,y:100},radius:t=10,value:i=100,hollow:a=0,valueGradient:o=ve,valuesAccumulate:s=Pe,size:c}=r,d={clim:e,radius:t,value:i,hollow:a,valueGradient:o,valuesAccumulate:s},u=l.map(_=>({...d,..._}));let p=c;c||(p=new n.Box3().setFromPoints(u).max.addScalar(1));const f=O.computeIntersectSpheres(u);let{x:v,y:g,z:y}=p;v=p.x=Math.trunc(v),g=p.y=Math.trunc(g),y=p.z=Math.trunc(y);const S=new Array(v*g*y),P=new Array(v*g*y),C={size:p,data3D:S,tags:P},D=u.length;for(let _=0;_<D;_++){const z=u[_],x=f[_].map(w=>u[w]);Ce(z,C,x)}return{data:S,size:p}}function Ve({startRadius:r,addedRadius:l,ratio:e}){return l*e+r}function Ne({startHollow:r,addedHollow:l,ratio:e}){return l*e+r}function Ue({startValue:r,addedValue:l,ratio:e}){return l*e+r}function Ae(r,l){const{x:e,y:t}=l;return r.reduce((i,a)=>i+a-e,0)}function ht(r){const{points:l,size:e,clim:t={x:0,y:100},radius:i=10,value:a=100,hollow:o=0,valueGradient:s=ve,radiusGradient:c=Ve,hollowGradient:d=Ne,lineValueGradient:u=Ue,valuesAccumulate:p=Ae}=r,f={radius:i,value:a,hollow:o},v=l.map(function(N){return Object.assign(new n.Vector3,f,N)}),g=e?new n.Vector3().copy(e).subScalar(1):new n.Box3().setFromPoints(v).max;g.x=Math.trunc(g.x),g.y=Math.trunc(g.y),g.z=Math.trunc(g.z);const y=g.clone().addScalar(1),{x:S,y:P,z:C}=y,D=S*P,_=new Array(D*C),z=[],x=[],w={particles:z,values:x,size:y,data:_},A=new n.Vector3(0,0,0),F=l.length,B=Math.trunc(F/2);for(let N=0;N<B;N++){const E=N*2,I=v[E],he=E+1,R=v[he],Q=new n.Line3(I,R),U=Q.distance(),H=Q.delta(new n.Vector3).normalize(),G=new n.Quaternion;G.setFromUnitVectors(new n.Vector3(0,1,0),H);const V=new n.Matrix4().makeRotationFromQuaternion(G);V.setPosition(I);const{radius:q,value:L,hollow:X}=I,{radius:$,value:Y,hollow:k}=R,j=$-q,ge=Y-L,Z=k-X,W={length:U,startRadius:q,endRadius:$,addedRadius:j,defaultRadius:i,radius:i,startValue:L,endValue:Y,addedValue:ge,defaultValue:a,startHollow:X,endHollow:k,addedHollow:Z,defaultHollow:o,clim:t},T=Math.max(q,$),xe=new n.Vector3(-T,0,-T),wt=new n.Vector3(T,U,T),Re=new n.Box3(xe,wt);Re.applyMatrix4(V);const{min:Te,max:Be}=Re;Te.max(A),Be.min(g);let{x:J,y:ee,z:te}=Te;J=Math.trunc(J),ee=Math.trunc(ee),te=Math.trunc(te);let{x:ne,y:ie,z:ae}=Be;if(ne=Math.trunc(ne),ie=Math.trunc(ie),ae=Math.trunc(ae),J>ne||ee>ie||te>ae)return w;V.invert();for(let oe=te;oe<=ae;oe++){const Dt=oe*D;for(let re=ee;re<=ie;re++){const bt=Dt+re*S;for(let se=J;se<=ne;se++){const Ee=bt+se,le=new n.Vector3(se,re,oe),Le=le.clone();Le.applyMatrix4(V);const{x:Pt,y:_e,z:Ct}=Le;if(_e<0||_e>U)continue;const ce=Math.hypot(Pt,Ct),ue={...W,ratio:_e/U,point:le},K=c(ue);ue.radius=K;const ke=d(ue),je=K*ke;if(ce>K||ce<je)continue;const Vt=u(ue),Nt={...W,value:Vt,ratio:ce/K,radius:K,hollow:ke,hollowRadius:je,distance:ce,point:le};let me=s(Nt);const He=x[Ee];He!==void 0&&(me=p([He,me],t)),_[Ee]=me,z.push(le),x.push(me)}}}}return w}function gt(r,l){const e=new n.Vector3().setFromMatrixScale(l),t=Math.sqrt(e.lengthSq()/3),i=new n.Matrix3().setFromMatrix4(l);let{points:a,size:o,radius:s,...c}=r;if(o&&(o=new n.Vector3().copy(o).applyMatrix3(i)),s&&(s*=t),a){const d=[];for(let{x:u,y:p,z:f,radius:v,...g}of a){v&&(v*=t);const y=new n.Vector3(u,p,f).applyMatrix4(l);Object.assign(y,g,{radius:v}),d.push(y)}a=d}return{points:a,size:o,radius:s,...c}}function Fe(r,l,e=1){let{points:t,size:i,radius:a,...o}=r;if(a&&(a*=e),t){const s=[];for(let{x:c,y:d,z:u,radius:p,...f}of t){p&&(p*=e);const v=new n.Vector3(c,d,u).add(l).multiplyScalar(e);Object.assign(v,f,{radius:p}),s.push(v)}t=s}return i&&(i=new n.Vector3().copy(i).add(l).multiplyScalar(e)),{points:t,size:i,radius:a,...o}}function xt(r,l){const{points:e,radius:t}=r;if(!e)return{options:r,position:new n.Vector3,scale:1};const i=new n.Box3;for(const p of e){const f=new n.Vector3().copy(p),v=p.radius??t;v?(i.expandByPoint(f.clone().subScalar(v)),i.expandByPoint(f.addScalar(v))):i.expandByPoint(f)}const a=new n.Vector3().max(i.min),o=i.max.clone().sub(a).addScalar(1);r.size&&o.min(r.size);let{scale:s,maxSize:c}=l||{};s||(c=c??100,s=c/Math.max(o.x,o.y,o.z)),o.multiplyScalar(s);const d=a.clone().negate(),u=Fe(r,d,s);return u.size=o,{options:u,position:a,scale:1/s}}const Ie={uniforms:{u_size:{value:new n.Vector3(1,1,1)},u_renderstyle:{value:0},u_renderthreshold:{value:.5},u_clim:{value:new n.Vector2(1,1)},u_data:{value:null},u_cmdata:{value:null}},vertexShader:`
varying vec4 v_nearpos;
varying vec4 v_farpos;
varying vec3 v_position;
void main() {
// Prepare transforms to map to "camera view". See also:
// https://threejs.org/docs/#api/renderers/webgl/WebGLProgram
mat4 viewtransformf = modelViewMatrix;
mat4 viewtransformi = inverse(modelViewMatrix);
// Project local vertex coordinate to camera position. Then do a step
// backward (in cam coords) to the near clipping plane, and project back. Do
// the same for the far clipping plane. This gives us all the information we
// need to calculate the ray and truncate it to the viewing cone.
vec4 position4 = vec4(position, 1.0);
vec4 pos_in_cam = viewtransformf * position4;
// Intersection of ray and near clipping plane (z = -1 in clip coords)
pos_in_cam.z = -pos_in_cam.w;
v_nearpos = viewtransformi * pos_in_cam;
// Intersection of ray and far clipping plane (z = +1 in clip coords)
pos_in_cam.z = pos_in_cam.w;
v_farpos = viewtransformi * pos_in_cam;
// Set varyings and output pos
v_position = position;
gl_Position = projectionMatrix * viewMatrix * modelMatrix * position4;
}`,fragmentShader:`
precision highp float;
precision mediump sampler3D;
uniform vec3 u_size;
uniform int u_renderstyle;
uniform float u_renderthreshold;
uniform vec2 u_clim;
uniform sampler3D u_data;
uniform sampler2D u_cmdata;
varying vec3 v_position;
varying vec4 v_nearpos;
varying vec4 v_farpos;
// The maximum distance through our rendering volume is sqrt(3).
const int MAX_STEPS = 887; // 887 for 512^3, 1774 for 1024^3
const int REFINEMENT_STEPS = 4;
const float relative_step_size = 1.0;
const vec4 ambient_color = vec4(0.2, 0.4, 0.2, 1.0);
const vec4 diffuse_color = vec4(0.8, 0.2, 0.2, 1.0);
const vec4 specular_color = vec4(1.0, 1.0, 1.0, 1.0);
const float shininess = 40.0;
void cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);
void cast_iso(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);
float sample1(vec3 texcoords);
vec4 apply_colormap(float val);
vec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray);
void main() {
// Normalize clipping plane info
vec3 farpos = v_farpos.xyz / v_farpos.w;
vec3 nearpos = v_nearpos.xyz / v_nearpos.w;
// Calculate unit vector pointing in the view direction through this fragment.
vec3 view_ray = normalize(nearpos.xyz - farpos.xyz);
// Compute the (negative) distance to the front surface or near clipping plane.
// v_position is the back face of the cuboid, so the initial distance calculated in the dot
// product below is the distance from near clip plane to the back of the cuboid
float distance = dot(nearpos - v_position, view_ray);
distance = max(distance, min((-0.5 - v_position.x) / view_ray.x,
(u_size.x - 0.5 - v_position.x) / view_ray.x));
distance = max(distance, min((-0.5 - v_position.y) / view_ray.y,
(u_size.y - 0.5 - v_position.y) / view_ray.y));
distance = max(distance, min((-0.5 - v_position.z) / view_ray.z,
(u_size.z - 0.5 - v_position.z) / view_ray.z));
// Now we have the starting position on the front surface
vec3 front = v_position + view_ray * distance;
// Decide how many steps to take
int nsteps = int(-distance / relative_step_size + 0.5);
if ( nsteps < 1 )
discard;
// Get starting location and step vector in texture coordinates
vec3 step = ((v_position - front) / u_size) / float(nsteps);
vec3 start_loc = front / u_size;
// For testing: show the number of steps. This helps to establish
// whether the rays are correctly oriented
//'gl_FragColor = vec4(0.0, float(nsteps) / 1.0 / u_size.x, 1.0, 1.0);
//'return;
if (u_renderstyle == 0)
cast_mip(start_loc, step, nsteps, view_ray);
else if (u_renderstyle == 1)
cast_iso(start_loc, step, nsteps, view_ray);
if (gl_FragColor.a < 0.05)
discard;
}
float sample1(vec3 texcoords) {
/* Sample float value from a 3D texture. Assumes intensity data. */
return texture(u_data, texcoords.xyz).r;
}
vec4 apply_colormap(float val) {
val = (val - u_clim[0]) / (u_clim[1] - u_clim[0]);
return texture2D(u_cmdata, vec2(val, 0.5));
}
void cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray) {
float max_val = -1e6;
int max_i = 100;
vec3 loc = start_loc;
// Enter the raycasting loop. In WebGL 1 the loop index cannot be compared with
// non-constant expression. So we use a hard-coded max, and an additional condition
// inside the loop.
for (int iter=0; iter<MAX_STEPS; iter++) {
if (iter >= nsteps)
break;
// Sample from the 3D texture
float val = sample1(loc);
// Apply MIP operation
if (val > max_val) {
max_val = val;
max_i = iter;
}
// Advance location deeper into the volume
loc += step;
}
// Refine location, gives crispier images
vec3 iloc = start_loc + step * (float(max_i) - 0.5);
vec3 istep = step / float(REFINEMENT_STEPS);
for (int i=0; i<REFINEMENT_STEPS; i++) {
max_val = max(max_val, sample1(iloc));
iloc += istep;
}
// Resolve final color
gl_FragColor = apply_colormap(max_val);
}
void cast_iso(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray) {
gl_FragColor = vec4(0.0); // init transparent
vec4 color3 = vec4(0.0); // final color
vec3 dstep = 1.5 / u_size; // step to sample derivative
vec3 loc = start_loc;
float low_threshold = u_renderthreshold - 0.02 * (u_clim[1] - u_clim[0]);
// Enter the raycasting loop. In WebGL 1 the loop index cannot be compared with
// non-constant expression. So we use a hard-coded max, and an additional condition
// inside the loop.
for (int iter=0; iter<MAX_STEPS; iter++) {
if (iter >= nsteps)
break;
// Sample from the 3D texture
float val = sample1(loc);
if (val > low_threshold) {
// Take the last interval in smaller steps
vec3 iloc = loc - 0.5 * step;
vec3 istep = step / float(REFINEMENT_STEPS);
for (int i=0; i<REFINEMENT_STEPS; i++) {
val = sample1(iloc);
if (val > u_renderthreshold) {
gl_FragColor = add_lighting(val, iloc, dstep, view_ray);
return;
}
iloc += istep;
}
}
// Advance location deeper into the volume
loc += step;
}
}
vec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray)
{
// Calculate color by incorporating lighting
// View direction
vec3 V = normalize(view_ray);
// calculate normal vector from gradient
vec3 N;
float val1, val2;
val1 = sample1(loc + vec3(-step[0], 0.0, 0.0));
val2 = sample1(loc + vec3(+step[0], 0.0, 0.0));
N[0] = val1 - val2;
val = max(max(val1, val2), val);
val1 = sample1(loc + vec3(0.0, -step[1], 0.0));
val2 = sample1(loc + vec3(0.0, +step[1], 0.0));
N[1] = val1 - val2;
val = max(max(val1, val2), val);
val1 = sample1(loc + vec3(0.0, 0.0, -step[2]));
val2 = sample1(loc + vec3(0.0, 0.0, +step[2]));
N[2] = val1 - val2;
val = max(max(val1, val2), val);
float gm = length(N); // gradient magnitude
N = normalize(N);
// Flip normal so it points towards viewer
float Nselect = float(dot(N, V) > 0.0);
N = (2.0 * Nselect - 1.0) * N; // == Nselect * N - (1.0-Nselect)*N;
// Init colors
vec4 ambient_color = vec4(0.0, 0.0, 0.0, 0.0);
vec4 diffuse_color = vec4(0.0, 0.0, 0.0, 0.0);
vec4 specular_color = vec4(0.0, 0.0, 0.0, 0.0);
// note: could allow multiple lights
for (int i=0; i<1; i++)
{
// Get light direction (make sure to prevent zero devision)
vec3 L = normalize(view_ray); //lightDirs[i];
float lightEnabled = float( length(L) > 0.0 );
L = normalize(L + (1.0 - lightEnabled));
// Calculate lighting properties
float lambertTerm = clamp(dot(N, L), 0.0, 1.0);
vec3 H = normalize(L+V); // Halfway vector
float specularTerm = pow(max(dot(H, N), 0.0), shininess);
// Calculate mask
float mask1 = lightEnabled;
// Calculate colors
ambient_color += mask1 * ambient_color; // * gl_LightSource[i].ambient;
diffuse_color += mask1 * lambertTerm;
specular_color += mask1 * specularTerm * specular_color;
}
// Calculate final color by componing different components
vec4 final_color;
vec4 color = apply_colormap(val);
final_color = color * (ambient_color + diffuse_color) + specular_color;
final_color.a = color.a;
return final_color;
}`};var Ge=(r=>(r[r.MIP=0]="MIP",r[r.ISO=1]="ISO",r))(Ge||{});class _t extends n.ShaderMaterial{constructor(e){const{style:t,threshold:i,clim:a,map:o,gradient:s}=e??{},c=new n.Vector3(1,1,1);if(o){const{width:p,height:f,depth:v}=o.image;c.set(p,f,v)}const d=s==null?s:O.createLinearGradientTexture(s),u=n.UniformsUtils.clone(Ie.uniforms);u.u_size.value.copy(c),u.u_clim.value.copy(a??new n.Vector2(1,1)),u.u_renderstyle.value=t??1,u.u_renderthreshold.value=i??.5,u.u_data.value=o??null,u.u_cmdata.value=d??null;super({...Ie,uniforms:u,side:n.BackSide});h(this,"isExampleVolumeMaterial_1",!0);h(this,"_gradient")}get map(){return this.uniforms.u_data.value}set map(e){this.uniforms.u_data.value=e,this.uniformsNeedUpdate=!0}get gradientMap(){return this.uniforms.u_cmdata.value}get gradient(){return this._gradient}set gradient(e){this._gradient=e;const t=e==null?e:O.createLinearGradientTexture(e);this.uniforms.u_cmdata.value=t??null,this.uniformsNeedUpdate=!0}get size(){return this.uniforms.u_size.value}set size(e){this.uniforms.u_size.value.copy(e),this.uniformsNeedUpdate=!0}get style(){return this.uniforms.u_renderstyle.value}set style(e){this.uniforms.u_renderstyle.value=e,this.uniformsNeedUpdate=!0}get clim(){return this.uniforms.u_clim.value}set clim(e){this.uniforms.u_clim.value=e,this.uniformsNeedUpdate=!0}get threshold(){return this.uniforms.u_renderthreshold.value}set threshold(e){this.uniforms.u_renderthreshold.value=e,this.uniformsNeedUpdate=!0}}const Oe=`in vec3 position;
uniform mat4 modelMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 cameraPos;
out vec3 vOrigin;
out vec3 vDirection;
void main() {
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
vOrigin = vec3( inverse( modelMatrix ) * vec4( cameraPos, 1.0 ) ).xyz;
vDirection = position - vOrigin;
gl_Position = projectionMatrix * mvPosition;
}`,yt=`precision highp float;
precision highp sampler3D;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
in vec3 vOrigin;
in vec3 vDirection;
out vec4 color;
uniform vec3 base;
uniform sampler3D map;
uniform float threshold;
uniform float range;
uniform float opacity;
uniform float steps;
uniform float frame;
uint wang_hash(uint seed)
{
seed = (seed ^ 61u) ^ (seed >> 16u);
seed *= 9u;
seed = seed ^ (seed >> 4u);
seed *= 0x27d4eb2du;
seed = seed ^ (seed >> 15u);
return seed;
}
float randomFloat(inout uint seed)
{
return float(wang_hash(seed)) / 4294967296.;
}
vec2 hitBox( vec3 orig, vec3 dir ) {
const vec3 box_min = vec3( - 0.5 );
const vec3 box_max = vec3( 0.5 );
vec3 inv_dir = 1.0 / dir;
vec3 tmin_tmp = ( box_min - orig ) * inv_dir;
vec3 tmax_tmp = ( box_max - orig ) * inv_dir;
vec3 tmin = min( tmin_tmp, tmax_tmp );
vec3 tmax = max( tmin_tmp, tmax_tmp );
float t0 = max( tmin.x, max( tmin.y, tmin.z ) );
float t1 = min( tmax.x, min( tmax.y, tmax.z ) );
return vec2( t0, t1 );
}
float sample1( vec3 p ) {
return texture( map, p ).r;
}
float shading( vec3 coord ) {
float step = 0.01;
return sample1( coord + vec3( - step ) ) - sample1( coord + vec3( step ) );
}
void main(){
vec3 rayDir = normalize( vDirection );
vec2 bounds = hitBox( vOrigin, rayDir );
if ( bounds.x > bounds.y ) discard;
bounds.x = max( bounds.x, 0.0 );
vec3 p = vOrigin + bounds.x * rayDir;
vec3 inc = 1.0 / abs( rayDir );
float delta = min( inc.x, min( inc.y, inc.z ) );
delta /= steps;
// Jitter
// Nice little seed from
// https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/
uint seed = uint( gl_FragCoord.x ) * uint( 1973 ) + uint( gl_FragCoord.y ) * uint( 9277 ) + uint( frame ) * uint( 26699 );
vec3 size = vec3( textureSize( map, 0 ) );
float randNum = randomFloat( seed ) * 2.0 - 1.0;
p += rayDir * randNum * ( 1.0 / size );
//
vec4 ac = vec4( base, 0.0 );
for ( float t = bounds.x; t < bounds.y; t += delta ) {
float d = sample1( p + 0.5 );
d = smoothstep( threshold - range, threshold + range, d ) * opacity;
float col = shading( p + 0.5 ) * 3.0 + ( ( p.x + p.y ) * 0.25 ) + 0.2;
ac.rgb += ( 1.0 - ac.a ) * d * col;
ac.a += ( 1.0 - ac.a ) * d;
if ( ac.a >= 0.95 ) break;
p += rayDir * delta;
}
color = ac;
if ( color.a == 0.0 ) discard;
}`;class Mt extends n.RawShaderMaterial{constructor(e){let{color:t,threshold:i,map:a,opacity:o,range:s,steps:c,frame:d,cameraPos:u}=e??{};t=t??new n.Color(1,1,1),i=i??.25,o=o??.25,s=s??.1,c=c??100,d=d??100,u=u??new n.Vector3;const p={base:{value:t},map:{value:a},cameraPos:{value:u},threshold:{value:i},opacity:{value:o},range:{value:s},steps:{value:c},frame:{value:d}};super({glslVersion:n.GLSL3,uniforms:p,vertexShader:Oe,fragmentShader:yt,side:n.BackSide,transparent:!0});h(this,"isExampleVolumeMaterial_Cloud",!0);this.opacity=o}get map(){return this.uniforms.map.value}set map(e){this.uniforms.map.value=e,this.uniformsNeedUpdate=!0}get color(){return this.uniforms.base.value}set color(e){this.uniforms.base.value=e,this.uniformsNeedUpdate=!0}get opacity(){return this.uniforms.opacity.value}set opacity(e){this.uniforms&&(this.uniforms.opacity.value=e),this.uniformsNeedUpdate=!0}get threshold(){return this.uniforms.threshold.value}set threshold(e){this.uniforms.threshold.value=e,this.uniformsNeedUpdate=!0}get range(){return this.uniforms.range.value}set range(e){this.uniforms.range.value=e,this.uniformsNeedUpdate=!0}get steps(){return this.uniforms.steps.value}set steps(e){this.uniforms.steps.value=e,this.uniformsNeedUpdate=!0}get frame(){return this.uniforms.frame.value}set frame(e){this.uniforms.frame.value=e,this.uniformsNeedUpdate=!0}get cameraPos(){return this.uniforms.cameraPos.value}set cameraPos(e){this.uniforms.ca