You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 22:50:07