LearnGL - 学习笔记目录
前些篇:
- LearnGL - 11.1 - 实现简单的Gouraud光照模型
- LearnGL - 11.2 - 实现简单的Phong光照模型
- LearnGL - 11.3 - 实现简单的Blinn-Phong光照模型
了解了 Gouraud、Phong、Blinn-Phong 光照模型的基本认识。
这篇:我们将对 Flat Blinn-Phong 光照模型实现一个简单的实现。
本人才疏学浅,如有什么错误,望不吝指出。
GLSL flat
GLSL 中如何对顶点传到片段着色器的数据指定不插值,可以参考:GLSL 中实现类似 Unity noiterpolation
实现起来非常简单,值要对法线数据不插值即可,这也可以在 Gouraud-Phong 来实现更节省性能。
GIF
Flat 风格
一般有些游戏风格是简模、平旦着色(Flat Shading) 都可以使用
如下:
Shader
Flat Blinn-Phong 的就不发出来了,只是在VS,FS中对法线插值数据添加上 flat
关键字即可。
所以这里只发一个性能上稍微好一些的 Flat Gouraud-Phong in vs 的 shader
// jave.lin - testing_flat_shading_in_vs.vert - 测试 phong 光照模型
#version 450 compatibility
// camera uniform
uniform vec3 _CamWorldPos; // 镜头世界坐标
// 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 传入的插值
flat varying vec3 fDiffuse; // 漫反射颜色
flat varying vec3 fSpecular; // 高光颜色
// varying vec3 fNormalTest;
// 将对象空间的法线转换到世界空间下的法线
vec3 ObjectToWorldNormal(vec3 n) {
return normalize(mat3(IT_mMat) * n); // 等价于:transpose(I_mMat) * vec4(n, 0)
}
vec3 my_reflect(vec3 L, vec3 N) {
// return -L + 2 * N * dot(N, L);
return -L + 2 * dot(N, L) * N; // 优化调整分量相同先乘
}
void main() {
vec4 worldPos = mMat * vec4(vPos, 1.0); // 世界坐标
vec3 viewDir = normalize(_CamWorldPos - worldPos.xyz); // 顶点坐标 指向 镜头坐标 的方向
vec3 worldNormal = ObjectToWorldNormal(vNormal); // 获取世界坐标下的法线
// fNormalTest = worldNormal;
// phong shading
if (LightPos.w == 0) {
// 下面使用的是Phong 光照模型
// 如果是方向光,那么 LightPos.xyz 是灯光方向的反方向
float LdotN = max(0, dot(LightPos.xyz, worldNormal));
fDiffuse = LightColor.rgb * LightColor.w * DiffuseK * LdotN; // lambert
// fNormalTest = vec3(LdotN);
vec3 R = my_reflect(LightPos.xyz, worldNormal);
float RdotV = max(0, dot(R, viewDir));
if (LdotN > 0) {
fSpecular = LightColor.rgb * LightColor.w * SpecularK * pow(RdotV, Glossy);
} else {
fSpecular = vec3(0, 0, 0);
}
} else {
// 点光源 或是 聚光灯
if (LightPos.w == 1) {
// 点光
} else { // LightPos.w == 0.5,即:LightPos.w !=0 && LightPos.w != 1
// 聚光灯
}
}
// uv0
fUV0 = vUV0;
// fNormalTest = worldNormal;
gl_Position = pMat * vMat * worldPos;
}
// jave.lin - testing_flat_shading_in_vs.frag - 测试 phong 光照模型
#version 450 compatibility
uniform vec4 _Ambient; // .xyz 环境光颜色, .w 环境光系数
uniform int AmbientType; // 环境光类别,测试用
// interpolation - 插值数据
varying vec2 fUV0; // uv 坐标
flat varying vec3 fDiffuse; // 漫反射颜色
flat varying vec3 fSpecular; // 高光颜色
// varying vec3 fNormalTest;
// local uniform
uniform sampler2D main_tex; // 主纹理
void main() {
// gl_FragColor = vec4(fNormalTest, 1.0);
// return;
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);
}