OpenGL|LearnGL - 11.2 - 实现简单的Phong光照模型


文章目录

  • Phong
  • Phong、Gouraud的区别
    • Gouraud的效果
      • diffuse
      • specular
    • Phong 效果
      • difusse
      • specular
          • 与 Gouraud-Phong 的高光效果图比较
      • 纯高光 Specular 的 Shader
  • GLSL Include 测试
  • 总结

LearnGL - 学习笔记目录
前一篇:LearnGL - 11.1 - 实现简单的Gouraud 光照模型 了解了 Gouraud 光照模型的基本认识。
这篇:我们将对 Phong 光照模型实现一个简单的实现。
提前吐槽一下,GLSL 中的 include 也是麻烦,因为 GLSL 没有文件系统,我查看了一篇还不错:OpenGL shader文件 include,后面有空我再加上,因为现在没有 include"my_file.xxx" ,所以很多重复性的代码,只能在每一个文件都有一份,实在蛋疼!
本人才疏学浅,如有什么错误,望不吝指出。
Phong Phong Shading,也叫:冯氏着色,它是由谁在 Gouraud 基础上改进的模型,我暂时没去了解。
Phong、Blinn-Phong ,都是在 Gouraud 光照模型基础上稍作改进的模型。
有兴趣可以去搜索,这里不会详细讲解这些内容。
Phong、Gouraud的区别 在前一篇了解到 Gouraud-Phong 的实现,主要都是依赖 Vertex Shader(顶点着色器)阶段来计算的。
而 Phong 比较大的区别是,主要在 Fragment Shader(片元/片段着色器)阶段来计算的。
【OpenGL|LearnGL - 11.2 - 实现简单的Phong光照模型】Phong 模型既然在 Fragment Shader 中计算的,那么就需要将相关的数据放到 Fragment Shader 中处理。如:Vertex Shader 阶段中的法线传入到插值数据到 Fragment Shader 中,那么每个片段的法线都过渡都会平滑多,那么光照效果差距就会很大。
看看效果:
Gouraud的效果 可以查看前一篇:LearnGL - 11.1 - 实现简单的Gouraud光照模型
diffuse
OpenGL|LearnGL - 11.2 - 实现简单的Phong光照模型
文章图片

specular
OpenGL|LearnGL - 11.2 - 实现简单的Phong光照模型
文章图片

Phong 效果 difusse
diffuse 漫反射的都差不多的效果,就不发了图了。
specular
主要是 specular 高光的效果相差比较大
OpenGL|LearnGL - 11.2 - 实现简单的Phong光照模型
文章图片

与 Gouraud-Phong 的高光效果图比较 如下图,上面的的球体是使用 Phong,在法线经过了插值平滑,然后在 Fragment Shader 处理光照,效果比下面的球体使用 Gouraud-Phong 在 Vertex Shader 在计算光照的效果好很多。
OpenGL|LearnGL - 11.2 - 实现简单的Phong光照模型
文章图片

弄个GIF来显示两个效果对比
OpenGL|LearnGL - 11.2 - 实现简单的Phong光照模型
文章图片

纯高光 Specular 的 Shader
// jave.lin - testing_phong_only_specular_shading.vert #version 450 compatibility// transform matrix uniform uniform mat4 mMat; // m.v.p 矩阵 uniform mat4 vMat; uniform mat4 pMat; uniform mat4 IT_mMat; // model matrix 的逆矩阵的转置矩阵// vertex data attribute vec3 vPos; // 顶点坐标 attribute vec2 vUV0; // 顶点纹理坐标 attribute vec3 vNormal; // 顶点法线// vertex data - interpolation varying vec2 fUV0; // 给 fragment shader 传入的插值 varying vec3 fNormal; // 世界坐标顶点法线 varying vec3 fWorldPos; // 世界坐标// 将对象空间的法线转换到世界空间下的法线 vec3 ObjectToWorldNormal(vec3 n) { return normalize(mat3(IT_mMat) * n); // 等价于:transpose(I_mMat) * vec4(n, 0) }void main() { vec4 worldPos = mMat * vec4(vPos, 1.0); // 世界坐标 fUV0 = vUV0; // UV0 fNormal = ObjectToWorldNormal(vNormal); // 世界坐标顶点法线 fWorldPos = worldPos.xyz; // 世界坐标 gl_Position = pMat * vMat * worldPos; // Clip pos }// jave.lin - testing_phong_only_specular_shading.frag #version 450 compatibility// camera uniform uniform vec3 _CamWorldPos; // 镜头世界坐标// object uniform uniform float Glossy; // 光滑度// light uniform uniform vec4 LightPos; // 灯光世界坐标位置,w==0,或名是方向光,w==1说明是点光源,w == 0.5 是聚光灯// interpolation - 插值数据 varying vec2 fUV0; // uv 坐标 varying vec3 fNormal; // 顶点法线 varying vec3 fWorldPos; // 世界坐标vec3 my_reflect(vec3 L, vec3 N) { return -L + 2 * N * dot(N, L); }void main() { vec3 worldNormal= normalize(fNormal); // 世界坐标法线再次归一化一次,因为插值之后可能会导致不是归一化的值 vec3 viewDir= normalize(_CamWorldPos - fWorldPos); // 顶点坐标 指向 镜头坐标 的方向 float S = 0; if (LightPos.w == 0) { // 下面使用的是Phong 光照模型 // 如果是方向光,那么 LightPos.xyz 是灯光方向的反方向 float LdotN = dot(LightPos.xyz, worldNormal); vec3 R = my_reflect(LightPos.xyz, worldNormal); float RdotV = max(0, dot(R, viewDir)); if (LdotN > 0) { S = pow(RdotV, Glossy); } } else { // 点光源 或是 聚光灯 if (LightPos.w == 1) { // 点光 } else { // LightPos.w == 0.5,即:LightPos.w !=0 && LightPos.w != 1 // 聚光灯 } } gl_FragColor = vec4(S); }

GLSL Include 测试 开头部分已经吐槽过 GLSL Include 很麻烦
因为 Include 内容有点多,我就独立成单篇文章来说明了,参考:LearnGL - 12 - GLSL include - GL_ARB_shading_language_include (Extensions扩展) - 各种踩坑
总结 虽然效果下 Phong 的好很多,但是,一般图形渲染一般光照计算考量于性能的话,一般我们会尽可能在 VS(Vertex Shader)在计算光照,因为一般顶点数并没有片段数量那么多。

    推荐阅读