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