Cook-Torrance shader with more than one point light
我正在尝试用四个点光源实现Cook-Torrance照明模式。虽然仅使用一个点光源即可获得不错的结果,但我不明白哪种方法是总结光回路内镜面反射项的正确方法。
我将材料定义如下:
1 2 3 4 5 6 7 | struct material { vec3 ambient; /* ambient color */ vec3 diffuse; /* diffuse color */ vec3 specular; /* speculr color */ float metallic; float roughness; }; |
...我的灯只有一种颜色/强度属性,
1 2 3 4 5 | struct light { vec3 position; vec3 color; bool enabled; }; |
这是片段着色器中片段着色器内部的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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; } |
我相信这里的关键是我在光循环中的错误计算:
1 | color += light[i].color * R * NdotL; |
这是我的意思的示例,生成的片段颜色要么太暗,要么太亮。我无法对每种光贡献进行汇总,以在镜面反射项和材质颜色之间获得良好的平滑颜色渐变。
我在这里阅读有关伽玛校正的信息,但是我不知道这是否适用于我的问题。
我如何将每种light.color与材质的漫反射,环境和镜面反射颜色相加,以通过正确地包括每种光源的镜面反射高光贡献的总量来计算最终的片段颜色?
您的大多数计算看起来不错(包括
1 | color += light[i].color * R * NdotL; |
1 | color += light[i].color * (material.specular * R + material.diffuse * NdotL); |
对于环境而言
1 | color += material.diffuse * light[i].color; |
用
并确保您的灯光不是太亮。屏幕无法显示比白色(或完全饱和的红色)更亮的任何东西。
-
vec3 V 应该是从片段位置到摄像机位置的归一化向量。 -
vec3 L 应该是从片段位置到亮位置的归一化向量。
根据
菲涅耳应基于HoV而非NoL:
对于漫射部分,您将光源视为环境光,而不是点光源。
应该是(对于简单的Lambertian)