我正在尝试在three.js中实现Cook-Torrance阴影算法,但我的解决方案最多,但是它没有显示环境光的影响。立方体没有被光照亮的侧面是完全黑色的。如果删除“贝克曼项”,那么我确实可以看到环境光的效果:



而用总是返回0.0的函数替换Beckmann时,我得到:





似乎错误行为的原因在于以下方面:

vec3 Specular = (Beckmann(NdotH) * G(NdotH, NdotV, VdotH, NdotL) * R_F(VdotH)) / ( NdotL* NdotV);


如果我将NdotL * NdotV修改为NdotV并将gl_FragColor的计算更改为:

gl_FragColor = vec4(beta * NdotL * (1.0-s)*Kd + beta * s*Specular + ambient*Kd, 1.0);


一切似乎正常工作。

我不明白的是:为什么?这个部门的问题在任何地方都没有提及,我也不是100%确信即使剩下的部门也不会在其他情况下引起问题。



这是完整的MWE:

<html>
    <head>
        <title>Cook-Torrance BRDF computed by shader</title>
        <style>

        body {
            font-family: Monospace;
            background-color: #f0f0f0;
            margin: 0px;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
        }

    </style>
        <script src="lib/three.min.js"></script>
        <script src="lib/OrbitControls.js"></script>
    </head>
    <body>

        <script type="text/x-glsl" id="vertex">
        varying vec3 transformedNormal;
        varying vec3 pointPosition;
        varying vec3 lightVector;
        uniform vec3 pointLightPosition;

        void main()
        {
            transformedNormal = normalMatrix * normal;
            pointPosition = (modelViewMatrix * vec4( position, 1.0 )).xyz;
            vec4 lPosition = viewMatrix * vec4( pointLightPosition, 1.0 );
            lightVector = lPosition.xyz - pointPosition;
            gl_Position = projectionMatrix * vec4(pointPosition,1.0);
        }
        </script>

        <script type="text/x-glsl" id="ct-fragment">
            uniform vec3 lightPower;
            uniform vec3 ambient;
            uniform vec3 Kd; // surface diffuse color
            uniform vec3 Ks; // surface specular color: equal to R_F(0)
            uniform float m; // material roughness (average slope of microfacets)
            uniform float s; // percentage of incoming light which is specularly reflected

            varying vec3 transformedNormal;
            varying vec3 pointPosition;
            varying vec3 lightVector;

            #define PI 3.14159265

            float G(float NdotH, float NdotV, float VdotH, float NdotL)
            {
                float G1 = 2.0 * NdotH * NdotV / VdotH;
                float G2 = 2.0 * NdotH * NdotL / VdotH;
                return min( 1.0, min( G1, G2 ));
            }

            vec3 R_F(float VdotH) {
                return Ks + (1.0 - Ks)*pow(1.0-VdotH, 5.0);
            }

            float Beckmann(float NdotH){
                float A = 1.0 / (pow(m,2.0)+pow(NdotH,4.0)*PI);
                float B = exp( - pow( tan(acos(NdotH)) , 2.0) / pow(m,2.0));
                return A*B;
            }

            void main()
            {
                vec3  n                 = normalize( transformedNormal );
                vec3  v                 = normalize( -pointPosition );
                vec3  l                 = normalize(  lightVector );
                vec3  h                 = normalize( v+l );
                float  NdotH            = max(0.0, dot( n, h ));
                float  VdotH            = max(0.0, dot( v, h ));
                float  NdotV            = max(0.0, dot( n, v ));
                float  NdotL            = max(0.0, dot( n, l ));
                // specular BRDF
                vec3 Specular = (Beckmann(NdotH) * G(NdotH, NdotV, VdotH, NdotL) * R_F(VdotH)) / ( NdotL* NdotV);
                vec3 beta = lightPower / ( 4.0  * PI * pow( length(lightVector),2.0) );
                gl_FragColor = vec4(beta * NdotL * ((1.0-s)*Kd + s*Specular) + ambient*Kd, 1.0);
            }
        </script>


        <script>
            var scene = new THREE.Scene();
            var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
            camera.position = new THREE.Vector3(0,0,5);

            var renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize( window.innerWidth, window.innerHeight );
            renderer.setClearColor( 0xf0f0f0 );
            document.body.appendChild( renderer.domElement );

            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.target.set(0, 0, 0);

            var uniforms = {
                        Ks: { type: "v3", value: new THREE.Vector3() },
                        Kd: { type: "v3", value: new THREE.Vector3() },
                        ambient:    { type: "v3", value: new THREE.Vector3() },
                        pointLightPosition: { type: "v3", value: new THREE.Vector3() },
                        lightPower: { type: "v3", value: new THREE.Vector3() },
                        s: {type: "f", value: 0},
                        m: {type: "f", value: 0}
                    };

            var vs = document.getElementById("vertex").textContent;
            var fs = document.getElementById("ct-fragment").textContent;

            var material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vs, fragmentShader: fs });

            var geometry = new THREE.CubeGeometry(1, 1, 1);
            var mesh = new THREE.Mesh(geometry, material);
            scene.add(mesh);

            light = new THREE.Mesh( new THREE.SphereGeometry( 1, 16, 16), new THREE.MeshBasicMaterial ({color: 0xffff00, wireframe:true}));
            light.position = new THREE.Vector3( 10.0, 10.0, 10.0 );
            scene.add( light );

            uniforms.Ks.value = new THREE.Vector3( 0.95, 0.93, 0.88 );
            uniforms.Kd.value = (new THREE.Vector3( 0.50754, 0.50754, 0.50754 ));
            uniforms.ambient.value = (new THREE.Vector3( 0.5, 0.5, 0.5 ));
            uniforms.pointLightPosition.value = new THREE.Vector3(light.position.x, light.position.y, light.position.z);
            uniforms.lightPower.value = new THREE.Vector3( 7000.0, 7000.0, 7000.0 );
            uniforms.s.value = 0.5;
            uniforms.m.value = 0.1;

            function animate() {

                requestAnimationFrame( animate );
                render();

            }

            function render() {
                controls.update();
                renderer.render(scene, camera);
            }

            animate();
        </script>
    </body>
</html>

最佳答案

阴影方程式是Cook-Torrance阴影模型的数学描述。
编写实际的着色器是另一回事,应考虑到以下事实:并非浮点之间的所有运算都具有方程式中实际数学运算的相同属性。

在这种情况下,通过0潜水会引起问题。实际上,问题在于Specular的定义正在被0潜水,但是当分配给gl_FragColor时,我将再次乘以NdotL获得0 * inf = NaN,似乎NaN被解释为GPU的零/负数(因此显示为黑色)。



作为参考,正确的main()为:

void main()
{
    vec3  n = normalize( transformedNormal );
    vec3  v = normalize( -pointPosition );
    vec3  l = normalize(  lightVector );
    vec3  h = normalize( v+l );

    vec3 specular = vec(0.0, 0.0, 0.0);
    float  NdotH = max(0.0, dot( n, h ));
    float  VdotH = max(0.0, dot( v, h ));
    float  NdotV = max(0.0, dot( n, v ));
    float  NdotL = max(0.0, dot( n, l ));
    if (NdotL > 0 && NdotV > 0)
    {
        specular = (Beckmann(NdotH) * G(NdotH, NdotV, VdotH, NdotL) * R_F(VdotH)) / ( NdotL* NdotV);
    }
    vec3 beta = lightPower / ( 4.0  * PI * pow( length(lightVector),2.0) );
    gl_FragColor = vec4(beta * NdotL * ((1.0-s)*Kd + s*specular) + ambient*Kd, 1.0);
}

关于javascript - 如何在three.js中正确实现Cook-Torrance底纹?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24096041/

10-10 07:13