如何使用在质心处计算的光来实现平面着色?

Posted

技术标签:

【中文标题】如何使用在质心处计算的光来实现平面着色?【英文标题】:How to achieve flat shading with light calculated at centroids? 【发布时间】:2013-11-28 00:54:44 【问题描述】:

我想编写一个 GLSL 着色器程序来进行逐面着色。我的第一次尝试使用带有激发顶点flat 插值限定符。我对法线和位置顶点属性都使用平面插值,这给了我想要的纯色表面的老式效果。

虽然渲染看起来正确,但着色器程序实际上并没有做正确的工作:

    仍然基于每个片段(在片段着色器中)执行光照计算, 位置向量取自激发顶点,而不是三角形的质心(对吗?)。

是否可以将照明方程一次应用于三角形的质心,然后将计算出的颜色值用于整个图元?该怎么做?

【问题讨论】:

【参考方案1】:

使用几何着色器,其输入为三角形,输出为三角形。从顶点着色器将法线和位置传递给它,自己计算质心(通过平均位置),然后进行照明,将输出颜色作为输出变量传递给片段着色器,它只是将其读入并写出。

【讨论】:

这是个好主意!但是引入额外的着色阶段对性能有何影响?从理论上讲,您的方法应该快得多,因为它会为每个图元计算一次照明方程。另一方面,在浏览相关问题和答案时,我发现人们认为几何着色是一种非常缓慢的技术。也许这些只是偏见,但总是值得一问。 扩展几何着色器(即输出的顶点数多于输入的顶点数)总体上非常慢。非扩展几何着色器(例如您为此使用的着色器)通常至少在性能上是可以接受的,而且我认为在较新的硬件上它们总体上相当不错。请注意,除非您的照明计算非常复杂,或者您使用的硬件功能极低,否则在管道中向上移动照明计算可能不会为您节省太多:成本就像像素着色器线程的实际启动一样。 【参考方案2】:

另一种简单的方法是使用屏幕空间位置的导数在片段着色器中计算(屏幕空间)面法线。实现起来非常简单,甚至表现良好。

我已经写了一个例子here(需要支持WebGL的浏览器):

顶点:

attribute vec3 vertex;

uniform mat4 _mvProj;
uniform mat4 _mv;

varying vec3 fragVertexEc;

void main(void) 
    gl_Position = _mvProj * vec4(vertex, 1.0);
    fragVertexEc = (_mv * vec4(vertex, 1.0)).xyz;

片段:

#ifdef GL_ES
precision highp float;
#endif

#extension GL_OES_standard_derivatives : enable

varying vec3 fragVertexEc;

const vec3 lightPosEc = vec3(0,0,10);
const vec3 lightColor = vec3(1.0,1.0,1.0);

void main()

    vec3 X = dFdx(fragVertexEc);
    vec3 Y = dFdy(fragVertexEc);
    vec3 normal=normalize(cross(X,Y));

    vec3 lightDirection = normalize(lightPosEc - fragVertexEc);

    float light = max(0.0, dot(lightDirection, normal));

    gl_FragColor = vec4(normal, 1.0);
    gl_FragColor = vec4(lightColor * light, 1.0);

【讨论】:

请注意,在这种情况下,位置是按片段计算的,而不是在质心处计算一次。这意味着对于非定向灯或阴影投射灯,整个三角形的照明不会相同。 感谢您的示例!但是dFdxdFdy 函数的含义是什么?我应该如何解释它们的输出值?根据我最初的研究,它们“在”四个(对吗?)片段着色器线程之间运行,并对属于两个不同坐标系(3D view space 和 2D viewport space)的向量进行操作>,对吧?)。但它们是什么意思?如何“可视化”它们背后的概念? 在这种用法中,您可以将 dFdx 和 dFdy 视为当前三角形的斜率或切线。用更抽象的术语来说,dFdx 和 dYdy 告诉你当你水平或垂直移动一个像素时给定变量会发生什么。或者用更数学的术语来说,是变量在 x 或 y 方向上的导数。 据我了解:dFdx 返回顶点属性插值函数的差商,沿 2D 视口空间的 X 轴,但关于存在于3D 眼睛空间(例如,位置向量)。或者简单地说:告诉“我”和“我的”下一个水平邻居(在屏幕上)之间属性的变化方向。对吗? @KrzysztofAbramowicz:没有视口空间这样的东西。视口用于限制图像在图像平面上的投影位置,但坐标空间实际上是窗口空间。

以上是关于如何使用在质心处计算的光来实现平面着色?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用OpenGL着色器在平面上绘制没有透视的纹理?

如何使用计算着色器计算值并将它们存储在 3D 纹理中?

如何使用 mask-RCNN 在 python 中计算(x,y)(质心)和掩码的宽度和高度?

如何计算质心和数据矩阵之间的距离(用于 kmeans 算法)

如何将平面阴影应用于 RGB 颜色?

OpenCV:如何找到运动信息的质心/质心