在 LearnOpenGL 教學的 Multiple Lights 章節中,需實現:1 個平行光、4 個點光源、1 個聚光燈對 10 個箱子的光照效果。註解掉聚光燈後物體變黑,表示平行光與點光源計算異常。
// 材質
struct Material { sampler2D texture0, texture1, texture2; float shininess; };
// 聚光燈
struct FlashLight { vec3 ambient, diffuse, specular, position, directional; float cutoff, outcutoff, constant, linear, quadratic; };
// 點光源
struct PointLight { vec3 ambient, diffuse, specular, position; float constant, linear, quadratic; };
// 平行光
struct DirectLight { vec3 ambient, diffuse, specular, directional; };
uniform Material material;
uniform PointLight pointlight[4];
uniform FlashLight flashlight;
uniform DirectLight directlight;
uniform vec3 cameraPos;
void main() {
vec3 normal = normalize(Normal);
vec3 viewDir = normalize(cameraPos - fragPos);
vec3 result = calcDirectLight(directlight, normal, viewDir);
for (int i = 0; i < 4; i++)
result += calcPointLight(pointlight[i], fragPos, normal, viewDir);
result += calcFlashLight(flashlight, fragPos, normal, viewDir);
color = vec4(result, 1.0);
}
glUniform3fv, glUniform1f 等),特別是 directlight.directional、pointlight[i].position、衰減係數等。若未設定,預設為 0 導致光照為黑。material.shininess、貼圖單元 texture0/1/2 是否綁定並賦值?Normal 到片段著色器?viewDir = normalize(cameraPos - fragPos),確認 cameraPos 為世界座標且正確傳入。directlight.directional 應為「光線傳播方向」(從光源指向場景),片段著色器中使用 normalize(-light.directional) 取得入射方向,確認 CPU 傳入方向正確。pointlight[i].position 必須與 fragPos 在同一空間(通常為世界空間)。constant=1.0, linear=0.09, quadratic=0.032 為常見預設值,過大會導致光照極快衰減至 0。color = vec4(normal * 0.5 + 0.5, 1.0))驗證法線正確性。glGetError() 或 RenderDoc 捕獲幀進行 Uniform 檢查。平行光計算修正:確保 directional 為單位向量且方向正確。
vec3 calcDirectLight(DirectLight light, vec3 normal, vec3 viewDir) {
vec3 lightDir = normalize(-light.directional); // 入射方向
float diff = max(dot(normal, lightDir), 0.0);
// ... ambient, diffuse, specular 計算
return ambient + diffuse + specular; // 平行光無衰減
}
CPU 端設定範例:
// 平行光
glUniform3fv(glGetUniformLocation(shader, "directlight.directional"), 1, glm::value_ptr(glm::normalize(glm::vec3(-0.2f, -1.0f, -0.3f))));
glUniform3fv(glGetUniformLocation(shader, "directlight.ambient"), 1, glm::value_ptr(glm::vec3(0.05f)));
glUniform3fv(glGetUniformLocation(shader, "directlight.diffuse"), 1, glm::value_ptr(glm::vec3(0.4f)));
glUniform3fv(glGetUniformLocation(shader, "directlight.specular"), 1, glm::value_ptr(glm::vec3(0.5f)));