Javascript 如何在three.js中正确实现Cook-Torrance着色?

Javascript 如何在three.js中正确实现Cook-Torrance着色?,javascript,three.js,fragment-shader,Javascript,Three.js,Fragment Shader,我正在尝试在three.js中实现Cook-Torrance着色算法。我有一个基本可行的解决方案,但是它没有显示环境光的效果。未被灯光照亮的立方体侧面是完全黑色的。如果我去掉“贝克曼项”,那么我确实可以看到环境光效果: 同时,将Beckmann替换为始终返回0.0的函数,我得到: 似乎错误行为的原因在于: vec3 Specular = (Beckmann(NdotH) * G(NdotH, NdotV, VdotH, NdotL) * R_F(VdotH)) / ( NdotL* Ndo

我正在尝试在three.js中实现Cook-Torrance着色算法。我有一个基本可行的解决方案,但是它没有显示环境光的效果。未被灯光照亮的立方体侧面是完全黑色的。如果我去掉“贝克曼项”,那么我确实可以看到环境光效果:

同时,将Beckmann替换为始终返回
0.0
的函数,我得到:


似乎错误行为的原因在于:

vec3 Specular = (Beckmann(NdotH) * G(NdotH, NdotV, VdotH, NdotL) * R_F(VdotH)) / ( NdotL* NdotV);
如果我将
NdotL*NdotV
修改为
NdotV
,并将
gl\u 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 BRDF
身体{
字体系列:Monospace;
背景色:#f0;
边际:0px;
溢出:隐藏;
}
帆布{
宽度:100%;
身高:100%;
}
可变vec3转换正常;
改变vec3点位置;
可变vec3光矢量;
均匀vec3点光源位置;
void main()
{
transformedNormal=法线矩阵*法线;
pointPosition=(modelViewMatrix*vec4(位置,1.0)).xyz;
vec4 lPosition=viewMatrix*vec4(pointLightPosition,1.0);
lightVector=lPosition.xyz-pointPosition;
gl_位置=投影矩阵*vec4(点位置,1.0);
}
均匀vec3光功率;
均匀vec3环境;
均匀vec3 Kd;//表面漫反射颜色
均匀矢量3 Ks;//曲面镜面反射颜色:等于R_F(0)
均匀浮点数m;//材料粗糙度(微面平均坡度)
均匀浮点数s;//镜面反射的入射光百分比
可变vec3转换正常;
改变vec3点位置;
可变vec3光矢量;
#定义PI 3.14159265
浮点G(浮点NdotH、浮点NdotV、浮点VdotH、浮点NdotL)
{
浮点G1=2.0*NdotH*NdotV/VdotH;
浮点数G2=2.0*NdotH*NdotL/VdotH;
返回最小值(1.0,最小值(G1,G2));
}
vec3 R_F(浮动VdotH){
返回Ks+(1.0-Ks)*功率(1.0-VdotH,5.0);
}
浮点数贝克曼(浮点数){
浮点数A=1.0/(功率(m,2.0)+功率(NdotH,4.0)*PI;
浮动B=exp(-pow(tan(acos(NdotH)),2.0)/pow(m,2.0));
返回A*B;
}
void main()
{
vec3 n=正常化(转换正常);
vec3 v=标准化(-pointPosition);
vec3 l=标准化(光向量);
vec3 h=标准化(v+l);
float NdotH=最大值(0.0,点(n,h));
浮动VdotH=最大值(0.0,点(v,h));
浮点数NdotV=最大值(0.0,点(n,v));
float NdotL=最大值(0.0,点(n,l));
//镜面反射BRDF
vec3镜面反射=(贝克曼(NdotH)*G(NdotH,NdotV,VdotH,NdotL)*R_F(VdotH))/(NdotL*NdotV);
vec3β=光功率/(4.0*PI*pow(长度(光向量),2.0));
gl_FragColor=vec4(β*NdotL*((1.0-s)*Kd+s*镜面反射)+环境*Kd,1.0);
}
var scene=new THREE.scene();
var摄像机=新的三透视摄像机(75,window.innerWidth/window.innerHeight,0.11000);
camera.position=新的三个矢量3(0,0,5);
var renderer=new THREE.WebGLRenderer({antialas:true});
renderer.setSize(window.innerWidth、window.innerHeight);
renderer.setClearColor(0xF0);
document.body.appendChild(renderer.doElement);
控件=新的三个.轨道控件(摄影机、渲染器.doElement);
controls.target.set(0,0,0);
变量={
Ks:{type:“v3”,值:new THREE.Vector3()},
Kd:{type:“v3”,值:new THREE.Vector3()},
环境:{type:“v3”,值:new THREE.Vector3()},
pointLightPosition:{类型:“v3”,值:new THREE.Vector3()},
lightPower:{type:“v3”,值:new THREE.Vector3()},
s:{type:“f”,值:0},
m:{type:“f”,值:0}
};
var vs=document.getElementById(“顶点”).textContent;
var fs=document.getElementById(“ct片段”).textContent;
var material=new THREE.ShaderMaterial({uniforms:uniforms,vertexShader:vs,fragmentShader:fs});
var geometry=新的三立方测量法(1,1,1);
var mesh=新的三个网格(几何体、材质);
场景。添加(网格);
灯光=新的三点网格(新的三点球面测量法(1,16,16),新的三点网格基本材质({color:0xffff00,线框:true}));
light.position=新的三个矢量3(10.0,10.0,10.0);
场景。添加(灯光);
councies.Ks.value=新的三个向量3(0.95,0.93,0.88);
uniforms.Kd.value=(新的三个向量3(0.50754,0.50754,0.50754));
uniforms.ambient.value=(新的3.Vector3(0.5,0.5,0.5));
uniforms.pointLightPosition.value=新的3.Vector3(light.p
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);
}