颜色
直接混合物体的灯光的颜色
1
| FragColor = vec4(lightColor * objectColor, 1.0);
|
基础光照
环境光照(Ambient Lighting):可以认为是常量
漫反射光照(Diffuse Lighting):物体表面与光照越垂直则越亮,可以认为是单位面积收到的光线数量(能量)减少了。需要法向量和灯光方向
镜面光照(Specular Lighting):Phong光照模型通过判断观察方向与光线反射方向的接近程度,Blinn-Pnong光照模型则是通过判断观察方向与光线方向的半程向量与表面法线的夹角的大小。需要法向量、灯光方向和观察方向(用摄像机位置计算)
顶点着色器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #version 330 core layout(location = 0) in vec3 aPos; layout(location = 1) in vec2 aTexCoord; layout(location = 2) in vec3 aNormal;
uniform mat4 modelMat; uniform mat4 viewMat; uniform mat4 projMat;
out vec2 TexCoord; out vec3 FragPos; out vec3 Normal;
void main(){ gl_Position = projMat * viewMat * modelMat * vec4(aPos.xyz, 1.0); TexCoord = aTexCoord; FragPos = (modelMat * vec4(aPos.xyz, 1.0)).xyz; Normal = mat3(transpose(inverse(modelMat))) * aNormal; }
|
片段着色器
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
| #version 330 core
in vec2 TexCoord; in vec3 FragPos; in vec3 Normal;
uniform sampler2D ourTexture; uniform vec3 lightPos; uniform vec3 ambientColor; uniform vec3 cameraPos;
out vec3 FragColor;
void main(){ vec3 lightDir = normalize(lightPos - FragPos); vec3 cameraDir = normalize(cameraPos - FragPos); vec3 normal = normalize(Normal); vec4 objColor = texture(ourTexture, TexCoord); vec3 diffuseColor = max(dot(normal, lightDir), 0) * lightColor; vec3 specularColor; vec3 reflectDir = reflect(-lightDir, normal); specularColor = pow(max(dot(cameraDir, reflectDir), 0), 100) * lightColor; vec3 halfwayDir = normalize(lightDir + cameraDir); specularColor = pow(max(dot(normal, halfwayDir), 0), 100) * lightColor; FragColor = vec4(ambientColor + diffuseColor + specularColor, 1) * objColor; }
|
这里是在世界空间里进行光照计算,所以需要传入观察者(相机)位置。如果是在观察空间里,那么观察者的位置就是(0,0,0),这样的话FragPos
、Normal
、LightPos
还要左乘以viewMat
如果是在顶点着色器里计算顶点颜色,传给片段着色器之后直接输出,就叫做Gouraud着色。其他像素的着色是每个三角形三个点直接插值的结果,于是每两个三角形间会有明显的分界线,很不真实,好处是效率高。
材质
把几种系数封装起来,在shader中声明材质结构体并定义实例,可以在在C++程序中指定的uniform名为material.ambient
的值
片段着色器
1 2 3 4 5 6 7 8 9 10
| ... struct Material{ vec3 ambient; vec3 diffuse; vec3 specular; float shininess; }; uniform Material material; ...
|
光照贴图
- 漫反射贴图:将shader里自定的物体颜色去掉,改用贴图上采样出来的颜色作为物体颜色,环境光也是,改为与漫反射一样
- 镜面光贴图:同样是采样然后作为系数,实现的是类似于遮罩的效果:贴图对应黑色的地方就会使得高光乘积为0,而高光贴图白色的地方对应高光更明显,也就实现了部分高光的效果(画工细腻的话可以在白色里话黑线,实现裂痕的效果)
- 自发光贴图:累加采样出来的颜色,可以传入时间实现循环亮暗和移动
片段着色器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ... struct Material{ vec3 ambient; sampler2D diffuse; sampler2D specular; sampler2D emission; float shininess; }; uniform Material material; ... void main(){ ... vec3 diffuseTexColor = texture(material.diffuse, TexCoord).rgb; vec3 ambient = diffuseTexColor * material.ambient * ambientColor; vec3 diffuse = diffuseTexColor * max(dot(normal, lightDir), 0) * lightColor; vec3 specular = texture(material.specular, TexCoord).rgb * pow(max(dot(normal, halfwarDir), 0), material.shininess) * lightColor; vec3 emission = texture(material.emission, TexCoord).rgb; FragColor = vec4(ambient + diffuse + specular + emission, 1); }
|
光源
平行光
光源无限远时光线近乎平行,可以视作平行光。光线方向与物体位置无关,可以认为光线不衰减。需要知道光的方向
将片段着色器中计算的lightDir
改为传入的灯光方向即可
点光源
光线方向是光源与物体的连线,会随距离发生平方衰减(二次函数为分母)。需要知道光源和物体的位置
片段着色器
只需要增加衰减
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ... struct LightPoint{ float constant; float linear; float quadratic; }; uniform LightPoint lightP; ... void main(){ float dis = length(lightPos - FragPos); float coefficient = 1.0 / (lightP.constant + lightP.linear * dis + lightP.quadratic * dis * dis); ... FragColor = vec4(ambient + emission + (diffuse + specular) * coefficient), 1); }
|
聚光
靠近中心光线的地方最亮。需要光源的位置,也需要中心光线的方向和张角,如果需要平滑边缘,需要两个夹角值区分内圈和外圈,内圈全亮,外圈之外(也就是聚光灯范围之外)全暗,两者之间则用夹角插值过渡
片段着色器
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
| ... struct LightSpot{ float cosPhyInner; float cosPhyOuter; }; LightSpot lightS; uniform vec3 lightDirUnifrom; ... void main(){ vec3 lightDir = normalize(lightPos - FragPos); ... float spotRatio; float cosTheta = dot(-lightDir, -lightDirUniform); if(cosTheta > lightS.cosPhyInner){ spotRatio = 1; } else if(cosTheta > lightS.cosPhyOuter){ spotRatio = (cosTheta - lightS.cosPhyOuter) / (lightS.cosPhyInner - lightS.cosPhyOuter); } else{ SpotRatio = 0; } FragColor = vec4(ambient + emission + (diffuse + specular) * spotRatio), 1); }
|
多光源
将每种灯光的计算都封装到一个函数中
片段着色器
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
| ... uniform LightDirectional lightD; uniform LightPoint lightP0; uniform LightPoint lightP1; uniform LightPoint lightP2; uniform LightPoint lightP3; uniform LightSpot lightS; ... void main(){ vec3 finalResult = vec3(0, 0, 0); vec3 uNormal = normalize(Normal); vec3 dirToCamera = normalize(cameraPos - FragPos);
finalResult += CalcLightDirectional(lightD, uNormal, dirToCamera); finalResult += CalcLightPoint(lightP0, uNormal, dirToCamera); finalResult += CalcLightPoint(lightP1, uNormal, dirToCamera); finalResult += CalcLightPoint(lightP2, uNormal, dirToCamera); finalResult += CalcLightPoint(lightP3, uNormal, dirToCamera); finalResult += CalcLightSpot(lightS, uNormal, dirToCamera);
vec3 ambient = texture(material.diffuse, TexCoord).rgb * material.ambient * ambientColor; finalResult += ambient;
FragColor = vec4(finalResult, 1); }
|
计算平行光
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
| struct LightDirectional{ vec3 pos; vec3 color; vec3 dirToLight; }; ...
vec3 CalcLightDirectional(LightDirectional light, vec3 uNormal, vec3 dirToCamera){ float diffIntensity = max(dot(light.dirToLight, uNormal), 0); vec3 diffuseColor = texture(material.diffuse, TexCoord).rgb * diffIntensity * light.color;
vec3 halfwayDir = normalize(light.dirToLight + dirToCamera); float specIntensity = pow(max(dot(halfwayDir, uNormal), 0), material.shininess); vec3 specularColor = texture(material.specular, TexCoord).rgb * specIntensity * light.color;
vec3 result = diffuseColor + specularColor; return result; }
|
计算点光
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 33
| struct LightPoint{ vec3 pos; vec3 color; vec3 dirToLight; float constant; float linear; float quadratic; }; ... vec3 CalcLightPoint(LightPoint light, vec3 uNormal, vec3 dirToCamera){ vec3 dirToLight = normalize(light.pos - FragPos); float dist = length(light.pos - FragPos); float attenuation = 1 / (light.constant + light.linear * dist + light.quadratic * (dist * dist));
float diffIntensity = max(dot(dirToLight, uNormal), 0); vec3 diffuseColor = texture(material.diffuse, TexCoord).rgb * diffIntensity * light.color;
vec3 halfwayDir = normalize(dirToLight + dirToCamera); float specIntensity = pow(max(dot(halfwayDir, uNormal), 0), material.shininess); vec3 specularColor = texture(material.specular, TexCoord).rgb * specIntensity * light.color;
vec3 result = (diffuseColor + specularColor) * attenuation; return result; }
|
计算聚光
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| struct LightSpot{ vec3 pos; vec3 color; vec3 dirToLight; float constant; float linear; float quadratic; float cosPhyInner; float cosPhyOuter; }; ... vec3 CalcLightSpot(LightSpot light, vec3 uNormal, vec3 dirToCamera){
vec3 dirToLight = normalize(light.pos - FragPos); float dist = length(light.pos - FragPos); float attenuation = 1 / (light.constant + light.linear * dist + light.quadratic * (dist * dist)); float cosTheta = dot(normalize(FragPos - light.pos), -light.dirToLight); float spotRatio; if(cosTheta > light.cosPhyInner){ spotRatio = 1; } else if(cosTheta > light.cosPhyOuter){ spotRatio = (cosTheta - light.cosPhyOuter) / (light.cosPhyInner - light.cosPhyOuter); } else{ spotRatio = 0; }
float diffIntensity = max(dot(dirToLight, uNormal), 0); vec3 diffuseColor = diffIntensity * light.color * texture(material.diffuse, TexCoord).rgb;
vec3 halfwayDir = normalize(dirToLight + dirToCamera); float specIntensity = pow(max(dot(halfwayDir, uNormal), 0), material.shininess); vec3 specularColor = texture(material.specular, TexCoord).rgb * specIntensity * light.color;
vec3 result = (diffuseColor + specularColor) * attenuation * spotRatio; return result; }
|