【问题标题】:phong and goraud shading - need knowledge about how the fragments get shadedphong 和 gouraud 着色 - 需要有关片段如何着色的知识
【发布时间】:2020-04-04 04:32:57
【问题描述】:

关于 Phong 着色和 Goraud 着色的唯一实际区别是,如果片段颜色的计算是在顶点着色器中完成的,那么它就是 Goraud,否则就是 phong。我在下面有一点顶点着色器和片段着色器代码:

//vertexShader.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;
out vec3 Normal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = mat3(transpose(inverse(model))) * aNormal;  

    gl_Position = projection * view * vec4(FragPos, 1.0);
}  


//FragmentShader.fs
#version 330 core
out vec4 FragColor;

in vec3 Normal;  
in vec3 FragPos;  

uniform vec3 lightPos; 
uniform vec3 viewPos; 
uniform vec3 lightColor;
uniform vec3 objectColor;

void main()
{
    // ambient
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;

    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;

    // specular
    float specularStrength = 0.5;
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0),32);
    vec3 specular = specularStrength * spec * lightColor;  

    vec3 result = (ambient + diffuse + specular) * objectColor;
    FragColor = vec4(result, 1.0);
} 

事实证明,Phong 在低多边形物体上看起来更平滑一些。因此,知识障碍基本上在于片段如何获得着色器。首先让我们看看wonderful resource learnopenGL 已经提供了什么。

总而言之,它告诉我们在顶点提供的颜色值是在由顶点构成的片段的边界内插值的。因此,这非常有意义,就像对构成顶点的颜色进行加权平均一样。

但是,就像 Phong 着色模型,计算直接在片段着色器中完成,会发生什么?片段像素如何着色?假设有三个顶点,那么片段是否完全用第一种颜色着色,完全用第二种颜色,然后用第三种颜色,是颜色的总中位数还是什么?在片段着色器中计算时片段是如何着色的?

【问题讨论】:

  • "片段像素是如何着色的?" 通过执行片段着色器。片段着色器是确定片段颜色的代码。你运行代码;这就是片段获得颜色的方式。
  • @NicolBolas,当然可以。但是,以什么输出颜色运行代码,请阅读我的最后一段。由于给出了每个像素的代码,因此没有插值的 3 个具有 3 种不同颜色的顶点。它需要哪种颜色?平均三个?三的总和?三种颜色的某种因素?
  • "3 种不同颜色的顶点" 什么颜色?我没有看到您的片段着色器给定颜色。我看到他们计算颜色,但这些颜色不是来自输入,对吗?
  • @NicolBolas ,是的,正是我的观点,没有给出颜色,输出​​颜色仅取决于顶点和法线的位置,但其中只有三个来自顶点着色器的常量,但表面在 phong 中是光滑的,而不是在 goroud 中。这就是为什么关于如何计算像素还有一些事情需要了解
  • 顶点着色器对每个顶点执行一次。片段着色器对每个片段执行一次。顶点着色器的输出根据三角形基元上片段的Barycentric coordinate 进行插值,这是片段着色器的输入。

标签: opengl shader


【解决方案1】:

顶点着色器对每个顶点执行一次。片段着色器对每个片段执行一次。顶点着色器的输出根据三角形基元上片段的Barycentric coordinate 进行插值,这是片段着色器的输入。
每个片段的片段着色器的输入都是不同的(因为它是插值的)。

在您的特殊情况下,这意味着 NormalFragPos 对于每个片段都是不同的。每个三角形有 3 个角。 NormalFragPos 是针对顶点着色器中三角形的每个角计算的。角的属性为三角形覆盖的每个片段进行插值,并且插值向量是片段着色器的输入。
由于每个片段都有不同的输入(NormalFragPos),因此每个片段的计算输出(FragColor)是不同的。
相邻片段的输出略有不同,因为输入也略有不同。这会导致平滑的照明。

注意,即使法线向量 (Normal) 是面法线(3 个顶点的法线相同),那么 FragPos 仍然不同。

此外,镜面高光 (float spec = pow(max(dot(viewDir, reflectDir), 0.0),32)) 不是线性函数。因此,不能通过线性插值正确计算镜面高光。它必须按片段计算。
实际上,如果 NormalFragPos 被插值,result 在片段着色器中计算,则存在差异,相比之下,result 在顶点着色器中计算并通过片段进行插值。


顶点属性是顶点着色器的输入。顶点着色器的输出是插值的(总是),插值是片段着色器的输入(Rasterization)。片段着色器的输出被写入帧缓冲区:

顶点属性 -> 顶点着色器 -> 插值/光栅化 -> 片段着色器 -> 帧缓冲区。

因此,如果您在片段着色器中插入 NormalFragPos 并计算 result,或者如果您在顶点着色器中计算 result 并插入 result,则会有所不同

有关渲染管道的更多信息,请参阅Rendering Pipeline Overview

【讨论】:

  • 我不太明白这部分:“相邻片段的输出略有不同,因为输入也略有不同。这会导致平滑的光照。” ,如果无论我们是否将NormalFragPos 放在任何着色器中,都对这些值进行插值,那么为什么平滑度不同,它们被插值相同的值对吗?为什么代码的放置会影响插值?
  • @juztcode 我认为您没有掌握基本知识。仔细阅读第一段。请注意,片段着色器的执行次数比顶点着色器的执行次数要多得多。为三角形覆盖的每个片段(像素)执行片段。顶点着色器对三角形执行 3 次(每个角)。所以NormalFragPos的顶点着色器有3个输出值。这 3 个输出必须分配给片段着色器的可能输入(对于每个像素)。因此必须对输出进行插值,并且相邻片段的输入略有不同。
  • 我确实明白,每个片段/像素都有一个单独的法线和片段位置值,但我的观点是,当我们在顶点和中定义 goraud 和 phong 模型时,必须有两种插值方法片段着色器分别。否则不会有阴影差异。我认为 gkv311 在他的回答中告诉了我们这两种方法,但是 openGL 如何根据我们定义代码的位置来选择它对我来说是一个谜,你知道吗?
  • @juztcode 不,没有两种插值方法。你没看过可能会回答。 spec = pow(max(dot(viewDir, reflectDir), 0.0),32) 不是线性函数。因此,如果在片段着色器中插入 NormalFragPos 并计算 result,或者在顶点着色器中计算 result 并插入 result,则会有所不同。
  • @juztcode “在片段着色器中计算颜色导致没有插值” - 具有误导性。这个答案是一个糟糕的答案。他的意思是片段着色器的输出没有被插值。当然,顶点着色器的输出是插值的(总是)!顶点着色器的输出是插值的,插值是片段着色器的输入。片段着色器的输出被写入帧缓冲区。顶点属性 -> 顶点着色器 -> 插值 -> 片段着色器 -> 帧缓冲区。
【解决方案2】:

Phong 和 Gouraud 着色的区别在于:

  • Gouraud 平均颜色。 颜色由 Vertex Shader 基于顶点法线计算,然后根据与每个三角形顶点的距离在单个三角形的片段之间进行平均。
  • Phong 平均法线。 颜色由片段着色器根据顶点着色器通过的三角形的 3 个顶点法线的平均值计算得出。

在高多边形网格上,两者都给出了非常接近的结果,因为每个三角形颜色的色散变得更小。

在低多边形网格上,平均 Gouraud 中的阴影颜色会产生更差的视觉效果,因为平均颜色几乎没有物理意义。

Phong 着色中的平均法线模拟光滑的表面属性,因此它们的平均可能接近甚至匹配原始分析表面定义,从而产生更加合理和平滑的视觉结果。

平均不是由着色器程序本身完成,而是由顶点和片段着色器阶段之间的固定硬件功能完成。因此,当您在 Vertex Shader 中计算颜色或通过法线/UV 坐标和类似坐标时,这些值由硬件根据片段重心坐标在三角形内所有片段的 3 个顶点之间进行插值。

在 Fragment Shader 中计算的颜色是最终颜色(在应用混合或模板测试之前,这也是由固定的硬件功能完成的)。因此,将光照计算放在 Fragment 着色器的 Vertex 中定义了将插值的内容以及直接计算的内容。

【讨论】:

  • 但是在上面的 GLSL 代码中,没有对颜色或法线进行平均,大多数来源都给出了平滑的原因:因为它是在片段着色器中计算的,所以它是平滑的,这不是很合理,你不同意吗?
  • 平均不是由着色器完成,而是由固定的硬件功能完成 - 从顶点着色器传递到片段着色器的 out 参数作为 in 参数被插值。检查渲染管道。虽然它被称为可编程,但它仍然包含很多固定功能步骤:khronos.org/opengl/wiki/Rendering_Pipeline_Overview
  • 好的,我将它与 Rabbid76 和您的 answer/cmets 结合起来,但是 openGL 如何根据我们定义代码的位置选择一种插值方法?它如何知道是平均法线还是平均颜色
  • 片段着色器计算单个片段的颜色顶点着色器计算单个顶点。将属性从顶点传递到片段着色器会导致插值(三角形定义一组片段)。在 Fragment Shader 中计算颜色不会导致插值,它是 最终颜色(保存一些操作,如模板测试和类似操作)。
猜你喜欢
  • 1970-01-01
  • 2021-01-05
  • 1970-01-01
  • 2013-09-17
  • 2014-07-30
  • 1970-01-01
  • 1970-01-01
  • 2011-05-21
  • 1970-01-01
相关资源
最近更新 更多