我已经成功实现了一个phong照明模型,在片段着色器中进行所有光照计算 - 它看起来很流畅,但如果在场景中有很多对象,那么会导致一些性能问题。一种解决方案是在顶点着色器中进行所有计算 - 但由于顶点着色器的线性插值,结果不准确且平滑。那么如何在不牺牲性能的情况下使对象看起来漂亮而流畅? 顶点着色器
#version 450 core
layout (location = 0) in vec4 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 normal;
uniform mat4 mvp;
uniform mat4 model;
uniform mat4 view;
uniform mat4 normalMatrix;
uniform vec3 lightPosition;
out vec4 fragPosition;
out vec3 vNormal;
out vec3 LightPosition;
void main() {
gl_Position = mvp * position;
fragPosition = view * model * position;
vNormal = normalize(mat3(normalMatrix) * normal);
LightPosition = vec3(view * vec4(lightPosition, 1.0));
}
片段着色器
#version 450 core
struct Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
struct Light {
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Material material;
uniform Light light;
in vec3 vNormal;
in vec3 LightPosition;
in vec4 fragPosition;
out vec4 color;
void main() {
vec3 norm = vNormal;
vec3 lightDir = normalize(LightPosition - vec3(fragPosition.xyz));
vec3 viewDir = normalize(-vec3(fragPosition.xyz));
vec3 reflectDir = reflect(-lightDir, norm);
vec3 ambient = material.ambient * light.ambient;
vec3 diffuse = (material.diffuse * max(dot(norm, lightDir), 0.0)) * light.diffuse;
vec3 specular = (material.specular * pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess)) * light.specular;
color = vec4(ambient + diffuse + specular, 1.0f); // phong lighting model
}
所以在顶点着色器中计算光线时
#version 450 core
layout (location = 0) in vec4 position;
layout (location = 2) in vec3 normal;
struct Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
struct Light {
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Material material;
uniform Light light;
uniform mat4 mvp;
uniform mat4 model;
uniform mat4 view;
uniform mat4 normalMatrix;
uniform vec3 lightPosition;
out vec3 finalColor;
void main() {
gl_Position = mvp * position;
vec4 fragPosition = view * model * position;
vec3 vNormal = normalize(mat3(normalMatrix) * normal);
vec3 _lightPosition = vec3(view * vec4(lightPosition, 1.0));
vec3 lightDir = normalize(_lightPosition - vec3(fragPosition.xyz));
vec3 viewDir = normalize(-vec3(fragPosition.xyz));
vec3 reflectDir = reflect(-lightDir, vNormal);
vec3 ambient = material.ambient * light.ambient;
vec3 diffuse = (material.diffuse * max(dot(vNormal, lightDir), 0.0)) * light.diffuse;
vec3 specular = (material.specular * pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess)) * light.specular;
finalColor = ambient + diffuse + specular;
}
和相应的片段
#version 450 core
in vec3 finalColor;
out vec4 color;
void main() {
color = vec4(finalColor, 1.0f); // phong lighting model
}
答案 0 :(得分:2)
它被称为“Phong”着色的具体原因是它是在片段着色器上完成的; Gouraud着色是相同的,但是在顶点着色器上完成(并且是你在实例中实际完成的),并且是提高性能的主要方式,但是以牺牲视觉准确性为代价
因此,在一般意义上,您可以更改算法的唯一方法是牺牲Phong对Gouraud速度的准确性,或反之亦然。
现在,根据您的代码的具体情况,可能会在此处进行优化。那里加快了速度。例如,由于您渲染的对象是立方体(因此具有清晰的平边),您可以在顶点着色器中计算片段的法线+位置,并将其传递给片段着色器,可以在那里保存一些计算。可能还有其他一些方法可以改善算法的详细性能。
但总的来说,对整体算法没有重大改变,可以让你摆脱“性能与外观”的决定。
答案 1 :(得分:0)
一个明显的优化是将所有内容移动到线性的顶点着色器(即其评估不包括幂或三角函数)。这是因为线性映射链可以简化为单个线性映射,或者分解为多个。
在您的情况下,片段着色器中的以下计算是线性的,除了normalize
部分(包含pow(x,0.5)),因此可以移动到顶点着色器中。
vec3 norm = vNormal;
vec3 lightDir = normalize(LightPosition - vec3(fragPosition.xyz));
vec3 viewDir = normalize(-vec3(fragPosition.xyz));
vec3 reflectDir = reflect(-lightDir, norm);
但即使进行归一化,它也会线性插值(为读者练习:为什么?提示:在线性插值下,矢量的长度如何变化?)。移动它已经大大降低了片段着色器的复杂性。
然后存在均匀常数表达式的问题,例如:
vec3 ambient = material.ambient * light.ambient;
现在,根据GLSL编译器的质量和/或有关统一缓冲区块的硬件功能,此表达式可能会被驱动程序级别的单个统一常量替换。但是,将这些统一值显式评估为前着色器总是一个好主意。漫反射和镜面反射术语都包含类似的统一常数子项。因此,将它们合并为单个LightMaterial
结构并在CPU端进行评估可能是个好主意。
dot
( cos(角度)·长度(a)·长度(b),长度(v)= sqrt(点(a,a)) )和pow
是非线性的,片段着色器中有两个是Phong与Gourad的区别。