代码之家  ›  专栏  ›  技术社区  ›  deblocker

具有多个点光源的Cook Torrance材质球

  •  1
  • deblocker  · 技术社区  · 7 年前

    我正在努力实现 Cook-Torrance lighting mode 有四个点光源。虽然我只使用一个点光源就得到了很好的结果,但我不明白用哪种方法来总结光循环中的镜面反射项是正确的。

    我对材料的定义如下:

    struct material {
        vec3 ambient; /* ambient color */
        vec3 diffuse; /* diffuse color */
        vec3 specular; /* speculr color */
        float metallic;
        float roughness;
    };
    

    …因此我的灯光只有一个颜色/强度属性,

    struct light {
        vec3 position;
        vec3 color;
        bool enabled;
    };
    

    下面是我的片段着色器中的函数,其中包含片段颜色计算:

    vec3 lighting() {
        vec3 color = vec3(0.0,0.0,0.0);
        float r0 = pow(material.metallic - 1.0,2.0)/pow(material.metallic + 1.0,2.0);
        vec3 V = normalize(-v_viewpos);
        vec3 N = normalize(v_normal);
        for (int i = 0; i < 4; i++) {
            if (light[i].enabled) {
                vec3 L = normalize(light[i].position - v_viewpos);
                // Half-way vector 
                vec3 halfVector = normalize(L + V);
                float NdotL = max(dot(N, L),0.0);
                float NdotV = max(dot(N, V),0.0);
                if (NdotL > 0.001 && NdotV > 0.001) {
                    float NdotH = max(0.0, dot(N, halfVector));
                    float HdotV = max(0.0, dot(halfVector, V));
                    // Beckmann 
                    float tanAlpha = sqrt(1.0-NdotH*NdotH)/NdotH;
                    float D = exp(-pow(tanAlpha/material.roughness,2.0))/(4.0*pow(material.roughness,2.0)*pow(NdotH,4.0));
                    // Shadowing-masking term 
                    float G1 = (2.0 * NdotH * NdotV) / HdotV;
                    float G2 = (2.0 * NdotH * NdotL) / HdotV;
                    float G = min(1.0, min(G1, G2));
                    // Fresnel reflection, Schlick approximation 
                    float F = r0 + (1.0 - r0) * pow(1.0 - NdotL, 5.0);
                    float R = (F*G*D) / (3.14159 * NdotL * NdotV);
                    color += light[i].color * R * NdotL;
                }
                color += material.diffuse * light[i].color;
            }
        }
        return color;
    }
    

    我认为这里的关键是我在光循环中的错误计算:

    color += light[i].color * R * NdotL;
    

    这是我的意思的一个例子,产生的片段颜色要么太暗,要么太亮。我无法总结每一个光的贡献,以获得一个很好的平滑颜色梯度之间的镜面反射项和材料的颜色。

    enter image description here

    我在看书 here 关于伽马校正,但我不知道这是否适用于我的问题。

    我应该如何将每个light.color与材质的漫反射、环境光和镜面反射颜色相加,通过正确地包含每个灯光的镜面反射高光贡献总量来计算最终片段颜色?

    2 回复  |  直到 7 年前
        1
  •  1
  •   pleup    7 年前
    • vec3 V 应该是从片段位置开始到摄影机位置的规格化向量。
    • vec3 L 应该是从片段位置开始到灯光位置的规格化向量。

    其中一个向量在着色器中是错误的,具体取决于 v_viewpos 是的。


    菲涅尔应基于霍夫而非诺尔: pow(1.0 - HoV, 5.0)


    对于漫反射部分,您将灯光视为环境光而不是点光源。

    color += material.diffuse * light[i].color;

    应该是(对于简单的lambertian)

    color += material.diffuse * light[i].color * NoL;

        2
  •  1
  •   Nico Schertler    7 年前

    你的大部分计算都很好(包括 V L 以及菲涅尔项)。唯一的问题是,您可能混淆了如何组合各个照明组件。对于镜面反射和漫反射

    color += light[i].color * R * NdotL;
    

    R 对应于镜面反射部分 NdotL 到漫反射部分。然而,两者都是加性的。因此,方程应为(加上考虑材料参数):

    color += light[i].color * (material.specular * R + material.diffuse * NdotL);
    

    对于环境术语

    color += material.diffuse * light[i].color;
    

    替换 material.diffuse 具有 material.ambient 这应该是正确的。

    确保你的灯不太亮。屏幕不能显示任何比白色(或完全饱和的红色)更亮的内容。

    推荐文章