如何提高phong照明模型中的照明计算性能

时间:2016-05-31 19:44:23

标签: opengl shader lighting

我已经成功实现了一个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
}

结果如此 enter image description here

所以在顶点着色器中计算光线时

#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
}

输出是这样的: enter image description here

2 个答案:

答案 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的区别。