OpenGL3.3-初级光照

143 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天点击查看活动详情

一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小。

光照在OpenGL中 光照实际上就是用光照的颜色参数乘上物体自身的颜色参数 得出最终物体的颜色 一般在漫反射的时候不需要考虑摄像机的位置 但是在计算镜面反射的时候需要考虑摄像机相对物体片段之间的位置 镜面反射就是先计算(光线照射到物体之后与法线的反射光线)和(摄像机到物体法线之间的夹角)之间的夹角的值 方向光:设置方向光的颜色 方向 先计算方向光方向和物体表面的法线之间的夹角 通过夹角的大小从而判断光线颜色在物体表面显示的多少


/*方向光结构体*/
struct DirLight {
    vec3 direction;//方向光方向
	
    vec3 ambient;//环境光值
    vec3 diffuse;//漫反射值
    vec3 specular;//镜面反射值
};

vec4 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);//单位化方向光方向(取反目前不清楚)
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);//将方向和法线点乘取得漫反射分量,取最大值是因为防止分量取负数(负数也就意味值夹角大于90了 也就是从后面射向平面的了 这个时候应该为0)
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);//计算反射角向量(lightDir取反的原因是reflect函数需要的参数是从光源指向片段位置的向量)
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);//计算镜面反射向量 同上
    // combine results/*这里综合了纹理的像素信息*/
    vec4 ambient = vec4(light.ambient,1.0) * vec4(texture(material.diffuse,TexCoords));  //环境光颜色*材质的漫反射那部分颜色
    vec4 diffuse = vec4(light.diffuse,1.0) * diff * vec4(texture(material.diffuse,TexCoords));//漫反射光*漫反射分量*材质的漫反射那部分颜色
    vec4 specular = vec4(light.specular,1.0) * spec * vec4(texture(material.specular, TexCoords));//镜面反射光*镜面反射分量*材质的镜面反射那部分颜色
    return (ambient + diffuse + specular);//最后返回三个分量颜色的和作为最终颜色
}

点光源:设置点光源的位置,颜色,衰减度 点光源是像灯泡这种 由一个点向四周发光 所以他在路途中会存在衰减 随着距离减少

/*点光源结构体*/
struct PointLight {
    vec3 position;//点光源位置
    
    float constant;//常数项
    float linear;//一次项系数
    float quadratic;//二次项系数
	
    vec3 ambient;//环境光值
    vec3 diffuse;//漫反射值
    vec3 specular;//镜面反射值
};
/*计算点光源的函数 参数 点光源结构体   法向量  目标物体的位置 观察者的观察方向*/
vec4 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);//取得灯光位置到片段世界坐标的方向向量
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);//取得漫反射分量
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);//取得反射角向量
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);//取得镜面反射分量
    // attenuation
    float distance = length(light.position - fragPos);//取得光源到片段世界坐标的距离
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    //通过这个二次函数进行衰减亮度
    // combine results
    vec4 ambient = vec4(light.ambient,1.0) * vec4(texture(material.diffuse, TexCoords));
    vec4 diffuse = vec4(light.diffuse,1.0) * diff * vec4(texture(material.diffuse,TexCoords));
    vec4 specular = vec4(light.specular,1.0) * spec * vec4(texture(material.specular, TexCoords));
   //在这里乘上衰减值
    ambient *= attenuation;
    diffuse *= attenuation;
    specular *= attenuation;
    return (ambient + diffuse + specular);//返回最终颜色
}

筒光源:设置光源的颜色,位置(一般是摄像机的位置),内光圈的半径,外光圈的半径,衰减度,方向(一般是面朝的方向) 筒光源就类似与手电筒 就是会形成一个光圈,筒光源中心是最亮的点 然后离中心越远就越暗,所以先计算物体片段到筒光源(也就是摄像机的位置)的向量和中心向量的夹角值 从而判断筒光源对物体片段的影响,其他漫反射和镜面反射和其他的一样,还有距离衰减值 具体算法看代码

/*筒光源结构体*/
struct SpotLight {

    vec3 position;//光的位置
    vec3 direction;//光的方向
    float cutOff;//光方向和物体方向之间的夹角余弦值
    float outerCutOff;//外圈的

    vec3 ambient;//环境光的值
    vec3 diffuse;//漫反射的值
    vec3 specular;//镜面反射的值

    float constant;//常数项
    float linear;//一次项系数
    float quadratic;//二次项系数
};
/*计算聚光灯的函数 参数是 聚光灯结构体   法向量  目标物体的位置 观察者的观察方向*/
vec4 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // attenuation
    float distance = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));  //这个上面都和前面一样  
    // spotlight intensity
    float theta = dot(lightDir, normalize(-light.direction)); //目标到光源向量和光源方向的夹角的余弦值
    float epsilon = light.cutOff - light.outerCutOff;//外光圈和内光圈角度的余弦差值
    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);//如果目标在内光圈内 则目标和光源方向夹角的余弦差值/内外光圈余弦差值会等于1 如果在内外光圈之间则会在1-0之间 如果在之外 则会等于0
    // combine results
    vec4 ambient = vec4(light.ambient,1.0) * vec4(texture(material.diffuse,TexCoords));
    vec4 diffuse = vec4(light.diffuse,1.0) * diff * vec4(texture(material.diffuse,TexCoords));
    vec4 specular = vec4(light.specular,1.0) * spec * vec4(texture(material.specular, TexCoords));
    ambient *= attenuation * intensity;
    diffuse *= attenuation * intensity;
    specular *= attenuation * intensity;
    return (ambient + diffuse + specular);
}

衰减的计算主要是判断物体片段点到点光源的距离值 通过距离值乘上相应的衰减函数求得最终光照颜色在物体表面的强度

光线衰减的原理就是在最后 光照颜色材质颜色角度之后 乘上一个由距离决定的并且由一个计算公式得到的值 聚光灯的原理是把灯光位置设置成摄像机的位置 灯光的方向就是朝向 给聚光灯设置一个角度 如果在这个角度内则亮度最亮 否则没什么灯光

reflect函数要求第一个向量是从光源指向片段位置的向量 第二个参数要求是一个法向量 这个是计算反射光线夹角的内置函数