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(片元/片段着色器)阶段来计算的。

Phong 模型既然在 Fragment Shader 中计算的,那么就需要将相关的数据放到 Fragment Shader 中处理。如:Vertex Shader 阶段中的法线传入到插值数据到 Fragment Shader 中,那么每个片段的法线都过渡都会平滑多,那么光照效果差距就会很大。

看看效果:

Gouraud的效果

可以查看前一篇:LearnGL - 11.1 - 实现简单的Gouraud光照模型

diffuse

LearnGL - 11.2 - 实现简单的Phong光照模型_第1张图片

specular

LearnGL - 11.2 - 实现简单的Phong光照模型_第2张图片

Phong 效果

difusse

diffuse 漫反射的都差不多的效果,就不发了图了。

specular

主要是 specular 高光的效果相差比较大

LearnGL - 11.2 - 实现简单的Phong光照模型_第3张图片

与 Gouraud-Phong 的高光效果图比较

如下图,上面的的球体是使用 Phong,在法线经过了插值平滑,然后在 Fragment Shader 处理光照,效果比下面的球体使用 Gouraud-Phong 在 Vertex Shader 在计算光照的效果好很多。
LearnGL - 11.2 - 实现简单的Phong光照模型_第4张图片
弄个GIF来显示两个效果对比
LearnGL - 11.2 - 实现简单的Phong光照模型_第5张图片

纯高光 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)在计算光照,因为一般顶点数并没有片段数量那么多。

你可能感兴趣的:(OpenGL,图形,理论)