【问题标题】:How to render only nearest depth level of transparent object in three.js?如何在three.js中仅渲染最接近的透明对象深度?
【发布时间】:2019-10-01 08:54:29
【问题描述】:

我正在尝试使用three.js 库将3D 对象呈现为webGL 中的全息图或X 射线。它需要在中心是透明的(以查看背景,可能稍后一些对象将在此体积内)并且在边缘具有明亮的不透明颜色。不应渲染背面隐藏面的面。我是基于网络的图形的新手,所以我不知道我应该使用 GLSL 着色器还是使用混合选项。对不起,愚蠢的问题。

根据tutorial,我可以使用自定义发光着色器达到类似的结果。但这并不能解决背面的问题。 我在 Blender 中获得了足够的外观,创建了一个着色器,通过大于 0.5 的透明度深度限制光路来消除这些面。 There is 我的 Blender 材质的节点。有没有办法在 webGL 中做类似的事情? 当前情况和预期的截图(第二行)是here

目前我使用来自three.js 库的OBJLoader、WebGLRenderer 和ShaderMaterial。材料定义如下。 CustomShader.js:

const customBrainShader = () => { return {

uniforms: 
{ 
    "c":   { type: "f", value: 1.0 },
    "p":   { type: "f", value: 1.9 },
    glowColor: { type: "c", value: new THREE.Color(0xcfdfff) },
    viewVector: { type: "v3", value: new Vector3(0, 100, 400) }
},

    vertexShader: vertexShaderSource,
    fragmentShader: fragmentShaderSource,

    side: THREE.FrontSide,
    blending: THREE.AdditiveBlending,
    depthTest: true,
    depthWrite: true,
    opacity: 0.5
}};
export { customBrainShader };

片段着色器:

 uniform vec3 glowColor;
 varying float intensity;

 void main() 
 {
  vec3 glow = glowColor * intensity;
  gl_FragColor = vec4( glow, 1.0 );
 }

顶点着色器:

uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;

void main() 
{
    vec3 vNormal = normalize( normalMatrix * normal );
vec3 vCamera = vec3(0.0,0.0,1.0);
intensity = pow( c - dot(vNormal, vCamera), p );
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

【问题讨论】:

  • @gman 不,我已经看过那篇文章,我的认识给出了相同的结果。问题是如何隐藏形状背面的面孔。例如,如何隐藏内部器官。
  • 设置透明:false,不透明度:1,?我想我对你想要达到的目标感到困惑
  • 这些不只是因为你的vCamera固定在+z坐标上才出现吗?您是否尝试过在每一帧上将其更新为制服,看看是否会发生同样的情况?
  • 欢迎来到 Stack Overflow。感谢您分享详细的描述和您的代码。如果我正确理解您的目标,您是否希望达到这样的效果? stackoverflow.com/questions/36947704/…

标签: javascript three.js glsl rendering shader


【解决方案1】:

如果我没记错的话,下面的效果就是你想要达到的效果。

这里发生了一些有趣的事情。

首先,我设置了renderer.autoClear = false,它可以防止渲染器在调用renderer.render 之间清除其缓冲区。这允许多次调用该函数以多次写入缓冲区。

接下来,我正在这样做。我将同一场景渲染两次。但是您会注意到,第一次渲染它时,我设置了一个scene.overrideMaterial,它将场景中的所有材质替换为覆盖。出于覆盖材料中的原因,我需要这样做。

在覆盖材料中,我设置了colorWrite: false。这意味着虽然对象将被“渲染”,但它不会绘制任何颜色,因此(还)没有可见的效果。它确实写入深度缓冲区,这正是我们想要的,因为对象将隐藏它后面的东西。这就像在一块神奇的玻璃后面藏着什么东西。 (我还在这里设置了多边形偏移以避免z-fighting,这完全是另一个话题,所以我不会在这个答案中详细介绍)。

最后,我使用您定义的着色器材质再次渲染场景。 noColor 渲染会遮挡应被遮挡的形状,因此当正面位于网格的另一部分后面时,您不会出现不必要的渗色。您的着色器处理其余部分,创建发光效果。

// Your shader code
const fragmentShaderSource = `
 uniform vec3 glowColor;
 varying float intensity;

 void main() 
 {
  vec3 glow = glowColor * intensity;
  gl_FragColor = vec4( glow, 1.0 );
 }
`
const vertexShaderSource = `
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;

void main() 
{
  vec3 vNormal = normalize( normalMatrix * normal );
  vec3 vCamera = vec3(0.0,0.0,1.0);
  intensity = pow( c - dot(vNormal, vCamera), p );
  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`

const customBrainShader = new THREE.ShaderMaterial({
  uniforms: {
    c: {
      value: 1.0
    },
    p: {
      value: 1.9
    },
    glowColor: {
      value: new THREE.Color(0xcfdfff)
    },
    viewVector: {
      value: new THREE.Vector3(0, 100, 400)
    }
  },
  vertexShader: vertexShaderSource,
  fragmentShader: fragmentShaderSource,
  side: THREE.FrontSide,
  opacity: 0.5
})

// male02 model from the three.js examples
const modelPath = "https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/models/obj/male02/male02.obj"

const renderer = new THREE.WebGLRenderer({
  antialias: true
})
renderer.autoClear = false
renderer.setSize(200, 200)
document.body.appendChild(renderer.domElement)

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(28, 1, 1, 1000)
camera.position.set(0, 90, 500)
const cameraTarget = new THREE.Vector3(0, 90, 0)
camera.lookAt(cameraTarget)

const light = new THREE.PointLight(0xffffff, 1)
camera.add(light)

scene.add(camera)

function render() {
  renderer.clear()
  scene.overrideMaterial = noColor
  renderer.render(scene, camera)
  scene.overrideMaterial = null
  renderer.render(scene, camera)
}

const axis = new THREE.Vector3(0, 1, 0)
const noColor = new THREE.MeshBasicMaterial({
  colorWrite: false,
  polygonOffset: true,
  polygonOffsetUnits: 1,
  polygonOffsetFactor: 1
})

function animate() {
  requestAnimationFrame(animate)
  camera.position.applyAxisAngle(axis, 0.0025)
  camera.lookAt(cameraTarget)
  render()
}
animate()

const loader = new THREE.OBJLoader()
loader.load(modelPath, (results) => {
  results.traverse(node => {
    if (node instanceof THREE.Mesh) {
      node.material = customBrainShader
    }
  })
  scene.add(results)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.js"></script>
<script src="https://threejs.org/examples/js/loaders/OBJLoader.js"></script>

【讨论】:

  • 谢谢@TheJim01,我已经检查了您在之前评论中发送的帖子,这正是我想要的效果!而且效果很好。非常感谢您能从这个模糊的描述中理解我的目标。
【解决方案2】:

事实证明,我的评论想法并没有我想象的那么糟糕。 JSFiddle example

为了在片段着色器中创建深度遮挡剔除,您需要创建一个独特的WebGLRenderTarget,并启用 depthBuffer 和 depthTexture。

target = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight );
target.texture.format = THREE.RGBFormat;;
target.stencilBuffer = false;
target.depthBuffer = true;
target.depthTexture = new THREE.DepthTexture();
target.depthTexture.type = THREE.UnsignedShortType;

然后,在您的动画循环中,您需要使用占位符MeshBasicMaterial 渲染常规场景到WebGLRenderTarget,更新您的自定义ShaderMaterial 的统一纹理以使用新渲染的depthTexture。

最后,您可以将ShaderMaterial 分配给网格并正常渲染场景。

function animate() {

    requestAnimationFrame( animate );

    // render scene into target
    mesh.material = basicMaterial;
    renderer.setRenderTarget( target );
    renderer.render( scene, camera );

    // update custom shader uniform
    shaderMaterial.uniforms.tDepth.value = target.depthTexture;

    // render scene into scene
    mesh.material = shaderMaterial;
    renderer.setRenderTarget( null );
    renderer.render( scene, camera );

}

在片段着色器中,您需要将当前片段 z 位置与 depthTexture 上的相应像素进行比较。这需要很少的操作即可在同一坐标空间中获得两个 z 值。

#include <packing>

uniform sampler2D tDepth;
uniform vec3 glowColor;
uniform vec2 viewportSize;
uniform float cameraNear;
uniform float cameraFar;

varying float intensity;

float readDepth( sampler2D depthSampler, vec2 coord ) {
    float fragCoordZ = texture2D( depthSampler, coord ).x;
    float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );
    return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
}

void main() {

    float zDepth = readDepth( tDepth, gl_FragCoord.xy / viewportSize.xy );
    float fragDepth = gl_FragCoord.z/gl_FragCoord.w/cameraFar;

    if ( fragDepth > zDepth + 0.001 ) discard; // 0.001 offset to prevent self-culling.

    vec3 glow = glowColor * intensity;
    gl_FragColor = vec4( glow, 1.0 );

}

【讨论】:

    猜你喜欢
    • 2018-01-27
    • 2013-07-05
    • 1970-01-01
    • 2012-10-24
    • 1970-01-01
    • 2019-11-07
    • 2018-03-09
    • 2015-04-17
    • 2018-05-11
    相关资源
    最近更新 更多