Three.js开启transparent后gl_FrontFacing失效问题求助
解决方案:透明模式下区分Three.js双面材质的正反面
核心问题原因
当材质开启transparent: true时,Three.js会调整DoubleSide材质的渲染逻辑:它会分两次绘制正面和背面,但此时WebGL的gl_FrontFacing会被强制设为true(内部通过翻转法线实现背面绘制,而非依赖原生面剔除机制),导致无法用gl_FrontFacing区分正反面。
解决方法:手动计算面朝向
不依赖gl_FrontFacing,在顶点着色器中手动计算面的朝向,传递给片元着色器使用:
步骤1:修改顶点着色器
添加朝向计算逻辑,将结果通过varying变量传递给片元着色器:
varying float vIsFront; void main() { // 计算视图方向与法线的点积,判断面朝向 vec3 viewDir = normalize(cameraPosition - modelMatrix * vec4(position, 1.0)).xyz; vec3 normal = normalize(normalMatrix * normal); vIsFront = dot(normal, viewDir) > 0.0 ? 1.0 : 0.0; // 保留Three.js原有顶点着色器逻辑 #include <begin_vertex> #include <project_vertex> }
步骤2:修改片元着色器
使用传递过来的vIsFront变量区分正反面:
varying float vIsFront; void main() { // 根据朝向设置颜色 diffuseColor = vIsFront > 0.5 ? vec4(0.0,0.0,0.0,1.0) : vec4(1.0,1.0,1.0,1.0); // 保留Three.js原有片元着色器逻辑 #include <color_fragment> #include <alphatest_fragment> #include <map_fragment> }
修改后的完整可运行代码
<!DOCTYPE html> <head> <title> SimpleDoubleSide(Custom Face Check) </title> <meta charset="utf-8" /> </head> <body> </body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.js"></script> <script> const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 60, window.innerWidth/window.innerHeight, 0.01, 1000 ); camera.position.set( 1, 2, 3 ); camera.lookAt(0,0,0); const renderer = new THREE.WebGLRenderer({ antialias: true }); const container = document.createElement( 'div' ); document.body.appendChild( container ); container.appendChild( renderer.domElement ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setClearColor( 0xdedede, 1 ); const shMaterial = new THREE.MeshBasicMaterial( { side: THREE.DoubleSide, transparent: true } ); shMaterial.onBeforeCompile = shader => { // 注入顶点着色器的朝向计算逻辑 shader.vertexShader = ` varying float vIsFront; ` + shader.vertexShader; shader.vertexShader = shader.vertexShader.replace( `#include <begin_vertex>`, `#include <begin_vertex> vec3 viewDir = normalize(cameraPosition - modelMatrix * vec4(position, 1.0)).xyz; vec3 normal = normalize(normalMatrix * normal); vIsFront = dot(normal, viewDir) > 0.0 ? 1.0 : 0.0; ` ); // 注入片元着色器的判断逻辑 shader.fragmentShader = ` varying float vIsFront; ` + shader.fragmentShader; shader.fragmentShader = shader.fragmentShader.replace( `#include <map_fragment>`, `#include <map_fragment> diffuseColor = vIsFront > 0.5 ? vec4(0.0,0.0,0.0,1.0) : vec4(1.0,1.0,1.0,1.0); ` ); }; const cylGeom = new THREE.CylinderGeometry( 0.5, 0.4, 1, 36, 8, true ); const cylinder = new THREE.Mesh( cylGeom, shMaterial ); scene.add( cylinder ); // 添加窗口 resize 适配 window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); function render( ) { renderer.render( scene, camera ); requestAnimationFrame( render ); } render(); </script> </html>
内容的提问来源于stack exchange,提问作者munHunger
相关产品推荐
相关产品推荐

