【问题标题】:Physically Based Rendering Results Don't Look Correct基于物理的渲染结果看起来不正确
【发布时间】:2015-03-15 13:47:29
【问题描述】:

我一直在尝试使用以下resource 实现基于物理的渲染。它描述了可以用来实现基于物理的着色的方程。这是我的着色器代码:

#define M_PI 3.1415926535897932384626433832795

#ifdef SHADER_VERTEX

layout (location = 0) in vec3 position;
layout (location = 2) in vec3 normal;

out vec3 Position;
out vec3 Normal;

uniform mat4 model;
uniform mat4 mvp;

void main()
{
    gl_Position = mvp * vec4(position, 1.0);
    Position = vec3(model * vec4(position, 1.0));
    Normal = vec3(model * vec4(normal, 0.0));
}

#endif

#ifdef SHADER_FRAGMENT

in vec3 Position;
in vec3 Normal;

out vec4 color;

uniform vec3 lightPos;
uniform vec3 objectColor;
uniform vec3 cameraPos;
uniform float roughness;
uniform vec3 specularColor;

vec3 D(vec3 normal, vec3 h)
{
    float a2 = roughness * roughness * roughness * roughness;
    float NdotH = dot(normal, h);
    float x = (NdotH * NdotH) * (a2 - 1) + 1;
    return vec3(a2 / (M_PI * (x * x)));
}

vec3 G1(vec3 normal, vec3 v)
{
    float k = ((roughness + 1) * (roughness + 1)) / 8.0f;
    float NdotV = dot(normal, v);

    return vec3(NdotV / ((NdotV * (1 - k) + k)));
}

vec3 G(vec3 normal, vec3 l, vec3 v)
{
    return G1(normal, l) * G1(normal, v);
}

vec3 F(vec3 v, vec3 h)
{
    vec3 f0 = specularColor;
    float VdotH = dot(v, h);
    float exponent = (-5.55473 * VdotH - 6.98316) * VdotH;
    return vec3(f0 + ((vec3(1, 1, 1) - f0) * pow(2, exponent)));
}

vec3 specular(vec3 n, vec3 l, vec3 h, vec3 v)
{
    float NdotL = dot(n, l);
    float NdotV = dot(n, v);
    return (D(n, h) * F(v, h) * G(n, l, v)) / ((4 * NdotL * NdotV));
}

vec3 diffuse(vec3 normal, vec3 lightDir)
{
    float NdotL = dot(normal, lightDir);
    vec3 result = objectColor / M_PI;
    return result * NdotL;
}

void main()
{
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - Position);
    vec3 eyeVector = normalize(cameraPos - Position);
    vec3 halfVector = normalize(lightDir + eyeVector);

    color = vec4(diffuse(norm, lightDir) + specular(norm, lightDir, halfVector, eyeVector), 1.0);
}

#endif

结果如下:

如您所见,中间有一条奇怪的白色带。灯光位置在球体上方,相机在球体前面。粗糙度变量当前设置为 0.2。我不知道我是否错误地执行了方程式,或者我传入的参数应该是什么样子。

【问题讨论】:

  • 看起来您的代码依赖于与 π 具有相同值的 M_PI。它类似于一个接缝,来自四舍五入。
  • 我该如何解决这个问题?由于四舍五入,我认为不会有问题。我使用方程式的方式有问题吗?我尝试在所有点积上使用 max 函数来确保数字不是负数,并且它似乎已经摆脱了白带,但我不知道这在数学上是否正确。

标签: c++ opengl glsl shader pbr


【解决方案1】:

问:n 垂直于l 时会发生什么?

vec3 specular(vec3 n, vec3 l, vec3 h, vec3 v)
{
    float NdotL = dot(n, l);
    float NdotV = dot(n, v);
    return (D(n, h) * F(v, h) * G(n, l, v)) / ((4 * NdotL * NdotV));
}

答:这个函数将返回 0/0,除了 0 之外会有舍入错误,所以你可以得到任何结果。

您需要重新排列等式,以免在任何地方以 0/0 结束。

【讨论】:

  • 我明白了。我对其进行了更改,以便为分母添加一个非常小的值,这样它就永远不会被 0 整除,结果会更好。感谢您的帮助。
  • 分母略为负会不会失败?
  • 我通过使用 max 函数使点积永远不会是负数,因此分母永远不会是负数。我认为这些点积无论如何都不应该是负面的。我不知道这是否正确。
  • 两个单位向量的点积将在 -1...+1 范围内,因此负数结果是完全正常的。当向量相同时为+1,当它们成直角时为0,当向量指向相反方向时为-1。
  • 但是在光照方面,如果点积是负数,那么光照会在三角形的后面。另外,负数没有意义,所以我假设你会忽略负数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-17
  • 1970-01-01
  • 2015-04-19
  • 1970-01-01
  • 2021-11-10
  • 2014-06-13
  • 1970-01-01
相关资源
最近更新 更多