I tried to generate SDF in Three.js and use it to calculate shadows, but it looks ugly! help!

this is my code:

getSDFTexture(group:any){
        this.initWposMat(group)

        this.ray = new Raycaster();
        this.ray.firstHitOnly = true;

        let boundingBox = new Box3().setFromObject(group) //min max

        this.sdfDebug.uniforms.bbox.value = [boundingBox.min, boundingBox.max]


        this.totalSize = boundingBox.getSize(new Vector3());
        group.traverse((obj:any)=>{
            if(obj.isMesh){
                obj.geometry.computeBoundingSphere() //obj.geometry.boundingSphere.radius
                obj.userData.radius = obj.geometry.boundingSphere.radius
                this.objList.push(obj)
            }
        })
        //划分网格
        let reso = 64
        // let minBorder = Math.min(this.totalSize.x,this.totalSize.y,this.totalSize.z)
        let maxBorder = Math.sqrt(Math.pow(reso,2)*3)
        let data32Size = new Vector3(reso,reso,reso) //new Vector3().copy(this.totalSize).divideScalar(minBorder).ceil().multiplyScalar(32)
        
        let step = new Vector3().copy(this.totalSize).divide(data32Size);
        let halfStep = new Vector3().copy(step).multiplyScalar(0.5)

       

        let currentPos = new Vector3()
        let offset = new Vector3()
        let data = []
        let curIdx = 0
        for (let z = 0; z < data32Size.z; z++) {
            for (let y = 0; y < data32Size.y; y++) {
              for (let x = 0; x < data32Size.x; x++) {
                offset = new Vector3().copy(boundingBox.min).add(halfStep)
                currentPos.set(x,y,z).multiply(step)
                offset.add(currentPos)
                //找出包含这个位置的所有obj,找出最接近的面,需要把坐标用临时变量转到对应空间
                let minDistance = maxBorder
                let isOutside = 1
                this.objList.forEach((obj:any)=>{
                    // let sphereCenter = obj.getWorldPosition(new Vector3())
                    // let radius = obj.userData.radius
                    // let distanceOfsToSphCenter = offset.distanceTo(sphereCenter)
                    // if(distanceOfsToSphCenter>radius){
                    //     return
                    // }
                    let meshInvMatrix = new Matrix4()
                    meshInvMatrix.copy(obj.matrixWorld).invert();
                    let local = offset.clone().applyMatrix4(meshInvMatrix)
                    //找出距离最近的三角形
                    let geo = obj.geometry
                    let positionAtt = geo.attributes.position
                    let indexAtt = geo.index
                    let triangle
                    if(indexAtt){
                        for (let i = 0; i < indexAtt.count; i += 3) {
                            const a = indexAtt.getX(i);
                            const b = indexAtt.getX(i + 1);
                            const c = indexAtt.getX(i + 2);
                    
                            const vertexA = new Vector3().fromBufferAttribute(positionAtt, a);
                            const vertexB = new Vector3().fromBufferAttribute(positionAtt, b);
                            const vertexC = new Vector3().fromBufferAttribute(positionAtt, c);
                            triangle = new Triangle(vertexA, vertexB, vertexC)
                            let clostLocPos = triangle.closestPointToPoint(local,new Vector3())

                            

                            let distSquat = local.distanceTo(clostLocPos)
                            if(distSquat<minDistance){
                                minDistance = distSquat
                                //请在这里计算local点在模型的正面还是反面
                                let normal = triangle.getNormal(new Vector3())
                                let toPoint = local.clone().sub(clostLocPos)
                                isOutside = Math.sign(normal.dot(toPoint))
                            }
                        }
                    }else{
                        for (let i = 0; i < positionAtt.count; i += 9) {
                            const vertexA = new Vector3(
                                positionAtt.getX(i),
                                positionAtt.getY(i),
                                positionAtt.getZ(i)
                            );
                            const vertexB = new Vector3(
                                positionAtt.getX(i + 3),
                                positionAtt.getY(i + 3),
                                positionAtt.getZ(i + 3)
                            );
                            const vertexC = new Vector3(
                                positionAtt.getX(i + 6),
                                positionAtt.getY(i + 6),
                                positionAtt.getZ(i + 6)
                            );
                            triangle = new Triangle(vertexA, vertexB, vertexC)

                            let clostLocPos = triangle.closestPointToPoint(local,new Vector3())

                            

                            let distSquat = local.distanceTo(clostLocPos)
                            if(distSquat<minDistance){
                                minDistance = distSquat

                                //请在这里计算local点在模型的正面还是反面
                                let normal = triangle.getNormal(new Vector3())
                                let toPoint = local.clone().sub(clostLocPos)
                                isOutside = Math.sign(normal.dot(toPoint))
                            }
                        }
                    }
                    //内外

                })
                data.push(minDistance * isOutside) //isOutside区分正反面
                curIdx++
                let process = curIdx/(data32Size.x*data32Size.y*data32Size.z)
                console.log(process,isOutside)
                if(curIdx%500===0){
                    console.clear()
                }
              }
            }
        }

        let texture = new Data3DTexture(
            new Float32Array(data),
            data32Size.x,
            data32Size.y,
            data32Size.z
        );
        texture.format = RedFormat;
        texture.type = FloatType;
        texture.minFilter = texture.magFilter = LinearFilter;
        texture.unpackAlignment = 1;
        texture.needsUpdate = true;

        this.sdfDebug.uniforms.sdfTexture.value = texture
        console.log(texture)
        return texture
    }

this is my shader:

this.sdfDebug = new ShaderPass({
            uniforms:{
                tDiffuse:{value:null},
                sdfTexture:{value:null},
                wPosTexture:{value:null},
                totalSize:{value:null},
                bbox: {
                    value: null,
                },
                cameraPos:{value:null},
            },
            vertexShader:`
                varying vec2 vUv;
                void main(){
                    vUv = uv;
                    vec4 mv = modelViewMatrix*vec4(position,1.);
                    gl_Position = projectionMatrix*mv;
                }       
            `,
            fragmentShader:`
            #include <packing>
            precision highp float;
            precision highp sampler3D;
            precision highp int;
            varying vec2 vUv;
            uniform sampler2D tDiffuse;
            uniform sampler3D sdfTexture;
            uniform sampler2D wPosTexture;
            uniform vec3 totalSize;
            uniform vec3[2] bbox;
            uniform vec3 cameraPos;

            vec2 sampleSDF(vec3 uv3){
                vec3 uvSpatial = (uv3 - bbox[0]) / (bbox[1] - bbox[0]);

                // 检查 uvSpatial 的每个分量是否在 [0, 1] 范围内
                bool outOfBounds = any(lessThan(uvSpatial, vec3(0.))) || any(greaterThan(uvSpatial, vec3(1.)));
                
                // 如果超出范围,y 分量赋值为 1,否则为 0
                float outOfBoundsFlag = outOfBounds ? 1.0 : 0.0;
                
                return vec2(texture(sdfTexture, uvSpatial).r,outOfBoundsFlag);
            }
            
            float isInShadow(vec3 fragPos, vec3 lightDir) {
                


                float res = 1.;
                float t = 0.01;
                float k = 8.;
                for( int i=0; i<256 ; i++ )
                {
                    vec2 rs = sampleSDF(fragPos+lightDir*t);
                    float h = rs.x;
                    if(rs.y==1.){
                        break;
                    }
                    if( h<0.001 )
                        return 0.0;
                    res = min( res, k*h/t );
                    t += h;
                }
                return res;

            }
            
            void main() {
                vec3 lightPos = vec3(-5., 5., 5.);
                vec3 color = texture(tDiffuse, vUv).rgb;
                vec4 base = texture(wPosTexture, vUv);
                vec3 dirToLi = normalize(lightPos);
                float _0isShadow = isInShadow(base.xyz, dirToLi);
                gl_FragColor = vec4(vec3(color * _0isShadow), 1.);
            }
            `
        })
        this.mainComposer.addPass(this.sdfDebug)

I can’t help with SDF as they are not a part of my arsenal.

However, looking at how you calculate isOutside, something confuses me. In the following image of a torus, point P is inside the torus in respect to the blue face, but it is outside the torus in respect to the red face.

image

As a result, data.push(minDistance * isOutside) might push the correct or the wrong value, depending on which face is processed last.

This is just a guess (the irregular shape of the black areas is even more confusing).