我试图以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 );
}