LearnGL - 11.3 - 实现简单的Blinn-Phong光照模型

49 篇文章 0 订阅
47 篇文章 0 订阅


LearnGL - 学习笔记目录

前些篇:

了解了 Gouraud、Phong 光照模型的基本认识。

这篇:我们将对 Blinn-Phong 光照模型实现一个简单的实现。

本人才疏学浅,如有什么错误,望不吝指出。


Blinn-Phong

也是基于 Phong 模型的 改进 ,注意这里的改进是用了 双引号 的。

以前在学习 Blinn-Phong 的时候,看到很多人都说效率会更好,因为 Blinn-Phong 与 Phong 模型的核心区别在于计算高光的部分,如下,但是真的有高很多效率吗?

  • Phong 模型公式: I = I a ⋅ K a + ∑ i = 0 n ( I d ⋅ K d ⋅ d o t ( N , L i ) + I s ⋅ K s ⋅ p o w ( d o t ( r e f l e c t ( − L i , N ) , V ) , G l o s s y ) ) I=I_a\cdot K_a+\sum_{i=0}^n(I_d\cdot K_d\cdot dot(N,L_i) + I_s\cdot K_s\cdot pow(dot(\red{reflect(-L_i,N)},\blue{V}),Glossy)) I=IaKa+i=0n(IdKddot(N,Li)+IsKspow(dot(reflect(Li,N),V),Glossy))
    简写: I = I a ⋅ K a + ∑ i = 0 n ( I d ⋅ K d ⋅ d o t ( N , L i ) + I s ⋅ K s ⋅ p o w ( d o t ( R , V ) , G l o s s y ) ) I=I_a\cdot K_a+\sum_{i=0}^n(I_d\cdot K_d\cdot dot(N,L_i) + I_s\cdot K_s\cdot pow(dot(\red{R},\blue{V}),Glossy)) I=IaKa+i=0n(IdKddot(N,Li)+IsKspow(dot(R,V),Glossy))
  • Blinn-Phong 模型公式: I = I a ⋅ K a + ∑ i = 0 n ( I d ⋅ K d ⋅ d o t ( N , L i ) + I s ⋅ K s ⋅ p o w ( d o t ( n o r m a l i z e ( L + V ) , N ) , G l o s s y ) ) I=I_a\cdot K_a+\sum_{i=0}^n(I_d\cdot K_d\cdot dot(N,L_i) + I_s\cdot K_s\cdot pow(dot(\red{normalize(L+V)},\green{N}),Glossy)) I=IaKa+i=0n(IdKddot(N,Li)+IsKspow(dot(normalize(L+V),N),Glossy))
    简写: I = I a ⋅ K a + ∑ i = 0 n ( I d ⋅ K d ⋅ d o t ( N , L i ) + I s ⋅ K s ⋅ p o w ( d o t ( H , N ) , G l o s s y ) ) I=I_a\cdot K_a+\sum_{i=0}^n(I_d\cdot K_d\cdot dot(N,L_i) + I_s\cdot K_s\cdot pow(dot(\red{H},\green{N}),Glossy)) I=IaKa+i=0n(IdKddot(N,Li)+IsKspow(dot(H,N),Glossy))

Phong 的高光计算量

Phong 当中的 R R R 使用的 GLSL reflect 函数其内部实现是: I − 2 ⋅ d o t ( N , I ) ⋅ N I-2 \cdot dot(N,I) \cdot N I2dot(N,I)N,其中 I I I光源入射角向量 N N N 是物体表面法线向量

这个 reflect 的计算量有:1次减法、2次乘法、和1个 dot,而 dot 内部是就向量点积,举个2维向量例子: d o t ( v e c 2   a , v e c 2   b ) = a . x ∗ b . x + a . y ∗ b . y dot(vec2\space a, vec2\space b) = a.x * b.x + a.y * b.y dot(vec2 a,vec2 b)=a.xb.x+a.yb.y,所以1个 dot 有2个乘法和1一个加法。

那么以2维的 reflect 的基础计算量有:1次减法(2分量)、1次标量乘法,1次标量与向量N乘法(2分量),1次加法(2分量)(这里还没算个每个分量的计算),运算量可不少了。

Blinn-Phong 的高光计算量

那么接下来介绍的是 Blinn-Phong 中的 H H H,它是由:光源方向观察者方向 之和的单位向量, H = n o r m a l i z e ( L + V ) H=normalize(L+V) H=normalize(L+V),从式子得知,有1次加法,还有1个 normalize 函数,normalize 的计算方式是,以2维向量为例: n o r m a l i z e ( v e c 2   v ) = v s q r t ( d o t ( v , v ) ) = v s q r t ( v . x ∗ v . x + v . y ∗ v . y ) = v ⋅ 1 s q r t ( v . x ∗ v . x + v . y ∗ v . y ) = normalize(vec2\space v) = \frac{v}{sqrt(dot(v,v))}=\frac{v}{sqrt(v.x*v.x+v.y*v.y)}=v\cdot\frac{1}{sqrt(v.x*v.x+v.y*v.y)}= normalize(vec2 v)=sqrt(dot(v,v))v=sqrt(v.xv.x+v.yv.y)v=vsqrt(v.xv.x+v.yv.y)1=,有1个dot和一个sqrt,dot 在上面介绍 Phong 时有说明。也就说, H H H的计算量有:2次加法、2次乘法,1次 sqrt,还有1次除法,而其中,sqrt 与除法计算量也是不少的,这两种运算在图形学中基本是能避免就避免,能减少就减少的 (例如:先求某个系数的倒数,再用向量乘以该系数的倒数,来减少除法)。所以 目测 比 Phong 的计算量还高,不过目测什么的也是不准的,需要巨量的运算用时比较才知道呢,那么这些测试这里就不去做了,有兴趣同学可以去测试一下。

但,如果我们将一些必要的向量,先在 VS 中算好,再传到 FS,那么就可以避免片段过多导致的消耗,如上面所将的 H H H 也可以放再 VS 算好再传到 FS

那么 目测 的性能比较就到这吧,接下来看一下他们在视觉上的效果有什么样的区别。

在 GGB 看看数值效果

在这里插入图片描述

留意这个值的变化。

其中 d o t dot dot 在 Phong 光照模型我有详细的介绍。

H = n o r m a l i z e ( L + V ) H=normalize(L+V) H=normalize(L+V) 在我移动 L 、 V L、V LV 过程中可以看到 H H H 的变化,也可以看到 d o t ( H , N ) dot(H,N) dot(H,N) 的变化。

看下 GIF 效果
在这里插入图片描述

Blinn Phong in vs

// jave.lin - testing_blinn_phong_only_specular_shading_in_vs.vert
#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 是聚光灯
// transform matrix uniform
uniform mat4 mMat;			// model matrix
uniform mat4 mvpMat;		// m.v.p 矩阵
uniform mat4 IT_mMat;		// model matrix 的逆矩阵的转置
// vertex data
attribute vec3 vPos;		// 顶点坐标
attribute vec3 vNormal;		// 顶点法线
// vertex data - interpolation
varying vec3 fCol;			// 片段插值颜色
// 将对象空间的法线转换到世界空间下的法线
vec3 ObjectToWorldNormal(vec3 n) {
	return normalize(mat3(IT_mMat) * n);	// 等价于:transpose(I_mMat) * vec4(n, 0)
}

void main() {
	vec3 worldPos 		= (mMat * vec4(vPos, 1.0)).xyz;				// 世界坐标
	vec3 worldNormal 	= ObjectToWorldNormal(vNormal);				// 获取世界坐标下的法线
	vec3 viewDir 		= normalize(_CamWorldPos - worldPos); 	    // 顶点坐标 指向 镜头坐标 的方向

	float LdotN 		= dot(LightPos.xyz, worldNormal);
	vec3 H 				= normalize(LightPos.xyz + viewDir);		// H = normalize(L + V)
	float HdotN 		= max(0, dot(H, worldNormal));					// 注意这里 Phong 是 dot(R,V),而 BlinnPhong 是 dot(H,N)
	float S = 0;
	if (LdotN > 0) {
		S = pow(HdotN, Glossy);
	}
	fCol = vec3(S);
	// uv0
	gl_Position = mvpMat * vec4(vPos, 1.0);
}

// jave.lin - testing_blinn_phong_only_specular_shading_in_vs.frag
#version 450 compatibility

// interpolation - 插值数据
varying vec3 fCol;			// 片段插值颜色

void main() {
	gl_FragColor 	= vec4(fCol, 1.0);
}

Blinn Phong in fs

// jave.lin - testing_blinn_phong_only_specular_shading_in_vs.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_blinn_phong_only_specular_shading_in_vs.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;		// 世界坐标

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 H = normalize(LightPos.xyz + viewDir);
		float HdotN = max(0, dot(H, worldNormal));
		if (LdotN > 0) {
			S = pow(HdotN, Glossy);
		}
	} else {
		// 点光源 或是 聚光灯
		if (LightPos.w == 1) {
			// 点光
		} else { // LightPos.w == 0.5,即:LightPos.w !=0 && LightPos.w != 1
			// 聚光灯
		}
	}
	gl_FragColor = vec4(S);
}

运行效果

在这里插入图片描述

在这里插入图片描述

气球猫的效果

在这里插入图片描述

在这里插入图片描述

可以看到 Gouraud 的右手臂上的高光没那么平滑, Phong 的高光比较细小,Blinn Phong 的高光比较混大、更加平滑似的。

其实这个气球猫的顶点有 4700+ 个,算比较多的,可以使用 Blinn Phong 在 VS 中处理,效果还不错,性能也还行。

如果顶点数、面数比较少,则在 VS 中处理光照会有比较明显的三角面块的不平滑过渡问题,但这个气球猫面数不算少,所以可以看到 Gouraud 模型中的效果也不会太差,但如果想上面我们实验用的球体,面数不够,那么光照效果用 Gouraud 模型的话,效果就不太好了。

环境光

之前各种 Phong 中,都没有详细看看效果,其实按 Phong 模型的环境光的效果真的不好

Phong 中环境光效果

因为 Phong 中是直接加环境光叠加上结果的,效果极差,如下图:
在这里插入图片描述

特别是本身没什么光照的地方,更加的差的效果,如,背光部分,如下GIF:
在这里插入图片描述

效果真的是差!

呐,看看ambient的处理 最终是叠加的

gl_FragColor = vec4(ambient + diffuse + specular, 1.0);

在背光的时候,环境光相当于直接赋值颜色,而看不到物体本身的 反射率吸收率 的效果了(即:纹理效果)

稍微调整一下环境光的处理

为了让 ambient 效果好,可是看到 反射率吸收率 的效果(即:纹理效果),我们稍微调整一下 Phong 模型: I = I a ⋅ K a + ∑ i = 0 n ( I d ⋅ K d ⋅ d o t ( N , L i ) + I s ⋅ K s ⋅ p o w ( d o t ( r e f l e c t ( − L i , N ) , V ) , G l o s s y ) ) I=\red{I_a\cdot K_a}+\sum_{i=0}^n(I_d\cdot K_d\cdot dot(N,L_i) + I_s\cdot K_s\cdot pow(dot(reflect(-L_i,N),V),Glossy)) I=IaKa+i=0n(IdKddot(N,Li)+IsKspow(dot(reflect(Li,N),V),Glossy))

调整为: I = l e r p ( I a ⋅ K a , a l b e d o , K a ) + ∑ i = 0 n ( I d ⋅ K d ⋅ d o t ( N , L i ) + I s ⋅ K s ⋅ p o w ( d o t ( r e f l e c t ( − L i , N ) , V ) , G l o s s y ) ) I=\red{lerp(I_a\cdot K_a, albedo, K_a)}+\sum_{i=0}^n(I_d\cdot K_d\cdot dot(N,L_i) + I_s\cdot K_s\cdot pow(dot(reflect(-L_i,N),V),Glossy)) I=lerp(IaKa,albedo,Ka)+i=0n(IdKddot(N,Li)+IsKspow(dot(reflect(Li,N),V),Glossy))

I a ⋅ K a → l e r p ( I a ⋅ K a , a l b e d o , K a ) \red{I_a\cdot K_a} \rightarrow \red{lerp(I_a\cdot K_a, albedo, K_a)} IaKalerp(IaKa,albedo,Ka)

对应代码如下:

vec3 ambient;
if (AmbientType == 0) { // 原始 phong 模型
	ambient = _Ambient.rgb * _Ambient.a;
} else {
	ambient = mix(_Ambient.rgb * _Ambient.a, albedo, _Ambient.a); // 这个是我们调整过的模型
}

我们在编辑器做了一个开关:Ambient_Tweak 的复选框
在这里插入图片描述
那么来看看运行效果
在这里插入图片描述
留意上面GIF中 Inspector Panel 中的 Materials 栏中的 Amibnet_Tweak 的 开、关效果,关的时候就是原始 Phong 的效果,开的时候就是调整过的效果。

完整的气球猫 三种 Shader

Phong Shader

// jave.lin - testing_load_balloon_cat_mesh_shading.vert
#version 450 compatibility

// camera uniform
uniform vec3 _CamWorldPos;	// 镜头世界坐标

// scene uniform
uniform vec4 _Ambient;		// .xyz 环境光颜色, .w 环境光系数

// object uniform
uniform float Glossy;		// 光滑度
uniform vec3 DiffuseK;		// 漫反射系数
uniform vec3 SpecularK;		// 高光系数

// light uniform
uniform vec4 LightPos;		// 灯光世界坐标位置,w==0,或名是方向光,w==1说明是点光源,w == 0.5 是聚光灯
uniform vec4 LightColor;	// 灯光颜色,.xyz 顔色,.w 强度
// uniform vec3 LightDir;		// 灯光类型为聚光灯的方向

// 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 fAmbient;		// 环境光
varying vec3 fDiffuse; 		// 漫反射颜色
varying vec3 fSpecular; 	// 高光颜色

// 将对象空间的法线转换到世界空间下的法线
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);	// 世界坐标
	vec3 viewDir = normalize(_CamWorldPos - worldPos.xyz); 	// 顶点坐标 指向 镜头坐标 的方向
	vec3 worldNormal = ObjectToWorldNormal(vNormal);		// 获取世界坐标下的法线

	// phong shading
	// ambient
	fAmbient = _Ambient.xyz * _Ambient.w;

	if (LightPos.w == 0) {
		// 下面使用的是Phong 光照模型
		// 如果是方向光,那么 LightPos.xyz 是灯光方向的反方向
		float D = max(0, dot(LightPos.xyz, worldNormal));
		fDiffuse = LightColor.rgb * LightColor.a * D * DiffuseK;
		vec3 R = reflect(-LightPos.xyz, worldNormal);
		float S = 0;
		if (D > 0) S = pow(max(0, dot(R, viewDir)), Glossy);
		fSpecular = LightColor.rgb * LightColor.a * S * SpecularK;
	} else {
		// 点光源 或是 聚光灯
		if (LightPos.w == 1) {
			// 点光
		} else { // LightPos.w == 0.5,即:LightPos.w !=0 && LightPos.w != 1
			// 聚光灯
		}
	}
	// uv0
	fUV0 = vUV0;
	gl_Position = pMat * vMat * worldPos;
}

// jave.lin - testing_load_balloon_cat_mesh_shading.frag
#version 450 compatibility

uniform vec4 _Ambient;			// 环境光

uniform int AmbientType;		// 环境光类别,测试用

// interpolation - 插值数据
varying vec2 fUV0;					// uv 坐标
varying vec3 fAmbient;				// 环境光
varying vec3 fDiffuse; 				// 漫反射颜色
varying vec3 fSpecular; 			// 高光颜色

// local uniform
uniform sampler2D main_tex;			// 主纹理

void main() {
	vec3 albedo 	= texture(main_tex, fUV0).rgb;
	
	vec3 ambient;
	if (AmbientType == 0) {
		ambient = _Ambient.rgb * _Ambient.a;
	} else {
		ambient = mix(_Ambient.rgb * _Ambient.a, albedo, _Ambient.a);
	}

	gl_FragColor 	= vec4(ambient + albedo * fDiffuse + fSpecular, 1.0);
}

Gouraud Shader

// jave.lin - testing_balloon_cat_gouraud_phong_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_balloon_cat_gouraud_phong_shading.frag
#version 450 compatibility

// camera uniform
uniform vec3 _CamWorldPos;	// 镜头世界坐标

uniform vec4 _Ambient;		// 环境光

// object uniform
uniform float Glossy;		// 光滑度
uniform vec3 DiffuseK;		// 漫反射系数
uniform vec3 SpecularK;		// 高光系数
uniform int AmbientType;	// 环境光类别,测试用

// light uniform
uniform vec4 LightPos;		// 灯光世界坐标位置,w==0,或名是方向光,w==1说明是点光源,w == 0.5 是聚光灯
uniform vec4 LightColor;	// 灯光颜色,.xyz 顔色,.w 强度

// interpolation - 插值数据
varying vec2 fUV0;			// uv 坐标
varying vec3 fNormal;		// 顶点法线
varying vec3 fWorldPos;		// 世界坐标

uniform sampler2D main_tex;

void main() {

	vec3 albedo = texture(main_tex, fUV0).rgb;

	vec3 worldNormal= normalize(fNormal);						// 世界坐标法线再次归一化一次,因为插值之后可能会导致不是归一化的值
	vec3 viewDir 	= normalize(_CamWorldPos - fWorldPos); 	    // 顶点坐标 指向 镜头坐标 的方向

	vec3 ambient;
	if (AmbientType == 0) {
		ambient = _Ambient.rgb * _Ambient.a;
	} else {
		ambient = mix(_Ambient.rgb * _Ambient.a, albedo, _Ambient.a);
	}
	vec3 diffuse = vec3(0);
	vec3 specular= vec3(0);

	if (LightPos.w == 0) {
		// 下面使用的是Phong 光照模型
		// 如果是方向光,那么 LightPos.xyz 是灯光方向的反方向
		float D = max(0, dot(LightPos.xyz, worldNormal));
		diffuse = LightColor.rgb * LightColor.a * D * DiffuseK * albedo;
		vec3 R = reflect(-LightPos.xyz, worldNormal);
		float S = 0;
		if (D > 0) S = pow(max(0, dot(R, viewDir)), Glossy);
		specular = LightColor.rgb * LightColor.a * S * SpecularK;
	} else {
		// 点光源 或是 聚光灯
		if (LightPos.w == 1) {
			// 点光
		} else { // LightPos.w == 0.5,即:LightPos.w !=0 && LightPos.w != 1
			// 聚光灯
		}
	}
	gl_FragColor = vec4(ambient + diffuse + specular, 1.0);
}

Blinn Phong Shader

// jave.lin - testing_balloon_cat_blinn_phong_shading_in_fs.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_balloon_cat_blinn_phong_shading_in_fs.frag
#version 450 compatibility

// camera uniform
uniform vec3 _CamWorldPos;	// 镜头世界坐标

uniform vec4 _Ambient;		// 环境光

// object uniform
uniform float Glossy;		// 光滑度
uniform vec3 DiffuseK;		// 漫反射系数
uniform vec3 SpecularK;		// 高光系数
uniform int AmbientType;	// 环境光类别,测试用

// light uniform
uniform vec4 LightPos;		// 灯光世界坐标位置,w==0,或名是方向光,w==1说明是点光源,w == 0.5 是聚光灯
uniform vec4 LightColor;	// 灯光颜色,.xyz 顔色,.w 强度

// interpolation - 插值数据
varying vec2 fUV0;			// uv 坐标
varying vec3 fNormal;		// 顶点法线
varying vec3 fWorldPos;		// 世界坐标

uniform sampler2D main_tex;

void main() {

	vec3 albedo = texture(main_tex, fUV0).rgb;

	vec3 worldNormal= normalize(fNormal);						// 世界坐标法线再次归一化一次,因为插值之后可能会导致不是归一化的值
	vec3 viewDir 	= normalize(_CamWorldPos - fWorldPos); 	    // 顶点坐标 指向 镜头坐标 的方向

	vec3 ambient;
	if (AmbientType == 0) {
		ambient = _Ambient.rgb * _Ambient.a;
	} else {
		ambient = mix(_Ambient.rgb * _Ambient.a, albedo, _Ambient.a);
	}
	vec3 diffuse = vec3(0);
	vec3 specular= vec3(0);

	if (LightPos.w == 0) {
		// 下面使用的是Phong 光照模型
		// 如果是方向光,那么 LightPos.xyz 是灯光方向的反方向
		float D = max(0, dot(LightPos.xyz, worldNormal));
		diffuse = LightColor.rgb * LightColor.a * D * DiffuseK * albedo;
		vec3 H = normalize(LightPos.xyz + viewDir);
		float S = 0;
		if (D > 0) S = pow(max(0, dot(H, worldNormal)), Glossy);
		specular = LightColor.rgb * LightColor.a * S * SpecularK;
	} else {
		// 点光源 或是 聚光灯
		if (LightPos.w == 1) {
			// 点光
		} else { // LightPos.w == 0.5,即:LightPos.w !=0 && LightPos.w != 1
			// 聚光灯
		}
	}
	gl_FragColor = vec4(ambient + diffuse + specular, 1.0);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值