【问题标题】:Forge viewer: 3D markups with textForge 查看器:带有文本的 3D 标记
【发布时间】:2018-06-11 02:29:57
【问题描述】:

我们正在尝试在 3D 模型中每个对象顶部的特定位置(右上角)显示标记。我们还希望在 3D 标记上呈现文本。这可能吗?如果是,我们将在它上面放置一个后处理着色器,这样它就不会在我们导航时隐藏在模型中的其他东西下面。

选项 2: 在 2D 中,无论是在 Forge 中还是在 HTML 中,这将是顶部带有文本的图像,在每一帧期间,这些图像+文本的位置必须更新以匹配 3D 对象的 2D 位置,这可以工作,但它可能滞后,这就是为什么我们更愿意使用 3D 选项 - 如果可能的话。

【问题讨论】:

    标签: autodesk-forge autodesk-viewer


    【解决方案1】:

    查看我最新的PointCloud Markup demo,了解使用动态纹理的自定义着色器示例:High-Performance 3D markups with PointCloud in the Forge Viewer

    您可以使用类似的方法来生成包含标签的纹理...

    createShader (options) {
    
      // Vertex Shader code
      const vertexShader = options.vertexShader || `
          attribute float pointSize;
          attribute vec4 color;
          varying vec4 vColor;
          void main() {
            vec4 vPosition = modelViewMatrix * vec4(position, 1.0);
            gl_Position = projectionMatrix * vPosition;
            gl_PointSize = pointSize;
            vColor = color;
          }
        `
    
      // Fragment Shader code
      const fragmentShader = options.fragmentShader || `
          uniform sampler2D texture;
          varying vec4 vColor;
          void main() {
            vec4 tex = texture2D(texture, gl_PointCoord);
            if (tex.a < 0.2) discard;
            if (vColor.a == 0.0) {
              gl_FragColor = vec4(tex.r, tex.g, tex.b, tex.a);
            } else {
              gl_FragColor = vColor;
            }
          }
        `
    
      const tex = options.texture || defaultTex
    
      // Shader material parameters
      const shaderParams = options.shaderParams || {
          side: THREE.DoubleSide,
          depthWrite: false,
          depthTest: false,
          fragmentShader,
          vertexShader,
          opacity: 0.5,
          attributes: {
            pointSize: {
              type: 'f',
              value: []
            },
            color: {
              type: 'v4',
              value: []
            }
          },
          uniforms: {
            texture: {
              value: THREE.ImageUtils.loadTexture(tex),
              type: 't'
            }
          }
        }
    
      // creates shader material
      const material =
        new THREE.ShaderMaterial(
          shaderParams)
    
      const generateTexture = (size, radius) => {
    
        const pixels = []
    
        for (let u = 0; u < size; ++u) {
    
          for (let v = 0; v < size ; ++v) {
    
            const dist = Math.sqrt(
              (u/size - 0.5) * (u/size - 0.5) +
              (v/size - 0.5) * (v/size - 0.5))
    
            if (dist < 0.1) {
    
              pixels.push(0xff, 0x00, 0x00, 0xff)
    
            } else if (dist < (radius - 0.05)) {
    
              pixels.push(0xff, 0x00, 0x00, 0x00)
    
            } else if (dist < radius) {
    
              pixels.push(0xff, 0x00, 0x00, 0xff)
    
            } else {
    
              pixels.push(0x00, 0x00, 0x00, 0x00)
            }
          }
        }
    
        const dataTexture = new THREE.DataTexture (
          Uint8Array.from (pixels),
          size, size,
          THREE.RGBAFormat,
          THREE.UnsignedByteType,
          THREE.UVMapping
        )
    
        dataTexture.minFilter = THREE.LinearMipMapLinearFilter
        dataTexture.magFilter = THREE.LinearFilter // THREE.NearestFilter
        dataTexture.needsUpdate = true
    
        return dataTexture
      }
    
      const stopwatch = new Stopwatch()
    
      let radius = 0.0
    
      return {
        setTexture: (tex) => {
    
          const {texture} = shaderParams.uniforms
    
          texture.value = THREE.ImageUtils.loadTexture(tex)
    
          texture.needsUpdate = true
    
        },
        update: () => {
    
          const dt = stopwatch.getElapsedMs() * 0.001
    
          radius += dt * 0.25
    
          radius = radius > 0.5 ? 0.0 : radius
    
          const {texture} = shaderParams.uniforms
    
          texture.value = generateTexture(96, radius)
    
          texture.needsUpdate = true
    
        },
        material
      }
    }
    

    【讨论】:

    • 非常感谢您的更新。有了它,我们可以确定每帧应该显示什么符号(像素)。所以有了这个,我们可以做任何动画,但更重要的是,我们可以绘制任何可以想象的符号/文本,所以现在可以画一个带有数字的圆圈。缺点只是它必须按像素编程,所以你不能只说:绘制文本“50”,字体大小为 20px,颜色为白色居中。相反,您必须说:像素 1,1 透明,像素 1,2 白色等。使用它可能需要一些时间才能使文本呈现成为可能。不过,这不是不可撤销的,这才是最重要的。
    • 权力伴随着责任......着色器非常强大,但它的级别很低,所以是的,你需要自己处理这个问题。另一种解决方法是使用您提到的 2D DOM 覆盖,您可以在其中轻松显示文本以及 svg 图形,但性能将远不及使用着色器所能获得的效果,尤其是在您计划显示大量标记的情况下。跨度>
    猜你喜欢
    • 2018-12-31
    • 2020-01-26
    • 2020-05-19
    • 2019-03-27
    • 2019-05-04
    • 2018-03-16
    • 2020-12-04
    • 2019-08-05
    • 2018-10-07
    相关资源
    最近更新 更多