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
文章图片
specular
文章图片
Phong 效果 difusse
diffuse 漫反射的都差不多的效果,就不发了图了。
specular
主要是 specular 高光的效果相差比较大
文章图片
与 Gouraud-Phong 的高光效果图比较 如下图,上面的的球体是使用 Phong,在法线经过了插值平滑,然后在 Fragment Shader 处理光照,效果比下面的球体使用 Gouraud-Phong 在 Vertex Shader 在计算光照的效果好很多。
文章图片
弄个GIF来显示两个效果对比
文章图片
纯高光 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)在计算光照,因为一般顶点数并没有片段数量那么多。
推荐阅读
- 2017.11.24|2017.11.24 晴 (27)
- 11.27|11.27 星期一 晴
- 2.关于OpenGL|2.关于OpenGL 坐标系以及渲染流程
- 2019-11-27
- Xcode|Xcode 11.2 开启推送按钮
- OpenGL|OpenGL ES之LUT(滤镜基准图)
- 9.喵喵的周记(11.19-11.25)
- OpenGL|OpenGL 绘制甜甜圈深度测试、多边形偏移、裁剪、 混合
- FFmpeg|FFmpeg 开发(07)(FFmpeg + OpenGLES 实现 3D 全景播放器)
- open基础笔记