如何仅在三js中仅渲染透明对象的最近深度水平



我试图以3.js库在WebGL中看起来像全息图或X射线的方式渲染3D对象。它需要在中心透明(要查看背景,以后的某些对象将在此卷内(,并且边缘具有明亮的不透明颜色。背面隐藏的一侧的面不应呈现。我在基于Web的图形中非常新手,所以我不知道我应该使用GLSL着色器或使用混合选项。抱歉,愚蠢的问题。

根据本教程,可以使用自定义的Glow着色器达到类似的结果。但这并不能用后面解决问题。我在搅拌机中获得了足够的外观,可以通过透明度深度大于0.5来限制光路,从而消除了此类面部。我的搅拌机材料有节点。有没有办法在WebGL中做类似的事情?当前情况和预期(第二行(的屏幕截图在这里。

目前,我使用objloader,webglrenderer和shadermaterial three.js库。材料定义为以下内容。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 );
}

如果我没记错的话,下面的效果就是您要实现的目标。

这里有一些有趣的事情。

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

接下来,我就是这样做的。我两次渲染相同的场景。但是您会注意到,我第一次渲染它时,我正在设置scene.overrideMaterial,该CC_3用覆盖替代了场景中的所有材料。我需要这样做,原因是覆盖材料中的原因。

在覆盖材料中,我要设置colorWrite: false。这意味着,尽管对象将被"渲染",但不会绘制任何颜色,因此尚无可见效果(尚未(。它 do 写入深度缓冲区,这是我们想要的,因为对象将隐藏其背后的事物。这就像把东西藏在一块魔术杯后面。(我还将多边形偏移设置在此处避免Z战斗,这是另一个主题,因此我不会在此答案中详细介绍任何细节(。

最后,我使用您定义的着色器材料再次渲染场景。 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>

事实证明,我的评论想法并不像我想象的那么糟。JSFIDDLE示例

为了创建片段着色器内部的深度遮挡,您需要创建一个启用深度扣和深度文本的唯一WebGLRenderTarget

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的网眼渲染常规场景,然后更新自定义的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位置与深度文本上的各个像素进行比较。这需要很少的操作才能在相同的坐标空间中获得两个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 );
}

最新更新