化石原创文章,转载请注明来源并保留原文链接


Vertex Shader(顶点着色器),在渲染管线中处理一个个顶点。一个Vertex Shader,接收一个顶点和它附带的一系列属性(比如UV、比如法线)。在执行着色器代码后,Vertex Shader产生一个输出的顶点。输入的顶点和输出的顶点是1:1的对应。

最重要的是,硬件或者说是driver认为:

Vertex Shader输出的顶点值是在Clip Space中。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


在这里,我把fragment shader叫做片段着色器。而开始学着色器的时候,我们往往可以遇到很多文章,把这个叫做像素着色器。在不断学习的过程中,这个着色器的功能似乎也很容易让人感觉它就是个像素着色器。

在英文中,我们也可以看到Pixel Shader这个字眼,而且很多是NVidia这样的官方(NVidia 是CG 语言的缔造者。也是微软的HLSL语言的合作者)。那么,这两个词究竟是什么联系?

Pixel Shader是从这个着色器的处理单位来讲的,它处理的是对应每个像素的计算。但是,这个着色器的输出,事实上并不马上形成最后显示缓冲区(在我的文章里,“空间”和“缓冲区”可能是一个词)中的一个值。比如,这个着色器的输出,还需要经过depth test、stencil test、alpha test等,只有通过这些后,才能最后存在于缓冲区中,成为一个我们看到的像素值。所以,fragment shader、片段着色器这样的词,是现在常用的词,更能精准的表达。而像素着色器(Pixel Shader),也能用。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


这个是即使看了维基上的图文解释,也不容易搞清楚的概念。但是拿出代码实现来看,就非常清楚。

所谓Column-major,即在连续的内存中,表现的元素是列-连续的。而Row-major,则是行-连续的。维基上用二维数组来解释这个概念,在我看来反而让人混淆。其实用一维看这个问题更清楚。

比如,如下矩阵,在数学书上这么表示:

Row-major matrix AND Column-major

图是借来的,带红色线的是Row-major,湖蓝的是Column-major。去除线两个矩阵其实是一样的。那什么导致了它们会有所谓的,Row-major和Column-major差异?

答案是:在程序里存储的连续性方式。

现在把上面的两个矩阵在你心里抹去那些线,两个矩阵就是同一个矩阵。我们在写代码的时候,必须就这个形式上矩阵元素做存储,我这里以javascript代码做演示:

function Matrix3(a11, a21, a31,
                 a12, a22, a32,
                 a13, a23, a33) {

     //column major
     this[0] = a11 || 0.0;
     this[1] = a21 || 0.0;
     this[2] = a31 || 0.0;
     this[3] = a12 || 0.0;
     this[4] = a22 || 0.0;
     this[5] = a32 || 0.0;
     this[6] = a13 || 0.0;
     this[7] = a23 || 0.0;
     this[8] = a33 || 0.0;
}

上面的代码中,类Matrix3的构造,存储9个参数带进来的元素。由this的下标0、1、2、3、4、5、6、7、8连续着看,我们是按照先“列”、再“行”的方式进行整个过程。这个就叫Column-major。 回到上面的图,正好是我们的湖蓝色线的走向。

参数命名好的话就是这样:

function Matrix3(column0Row0, column0Row1, column0Row2,
                 column1Row0, column1Row1, column1Row2,
                 column2Row0, column2Row1, column2Row2) {

     //column major
     this[0] = column0Row0 || 0.0;
     this[1] = column0Row1 || 0.0;
     this[2] = column0Row2 || 0.0;
     this[3] = column1Row0 || 0.0;
     this[4] = column1Row1 || 0.0;
     this[5] = column1Row2 || 0.0;
     this[6] = column2Row0 || 0.0;
     this[7] = column2Row1 || 0.0;
     this[8] = column2Row2 || 0.0;
}

那么很显然,Row-major写法的话,就是让0到7的元素以红色线的顺序存储既是。这里我就不贴代码了。

如果是现实中加入了translation的矩阵,则

OpenGL标准中说到(下文第2段,黑体):

For programming purposes, OpenGL matrices are 16-value arrays with base vectors laid out contiguously in memory. The translation components occupy the 13th, 14th, and 15th elements of the 16-element matrix, where indices are numbered from 1 to 16 as described in section 2.11.2 of the OpenGL 2.1 Specification.

Column-major versus row-major is purely a notational convention. Note that post-multiplying with column-major matrices produces the same result as pre-multiplying with row-major matrices. The OpenGL Specification and the OpenGL Reference Manual both use column-major notation. You can use any notation, as long as it's clearly stated.

Sadly, the use of column-major format in the spec and blue book has resulted in endless confusion in the OpenGL programming community. Column-major notation suggests that matrices are not laid out in memory as a programmer would expect. 

出处:  https://www.opengl.org/archives/resources/faq/technical/transformations.htm 

第一段(斜体,特别是黑色)中OpenGL标准定义,一个4×4的矩阵-包含了16个元素,作为translation的x、y、z应该放在第13,14,15位置(正整数,以1位起始)。

在OpenGL的很多地方,可以看到纸面的表述,矩阵如下:

1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 0

但是在内存中,其实它是如下存放(先摆行里的元素,也就是先第一行,再第二行):

[1, 0, 0, 0,
 0, 1, 0, 0,
 0, 0, 1, 0,
 x, y, z, 0]

所以,OpenGL标准里说的矩阵都是“Column-major”。

所以,如果写自己的矩阵库,概念(名称)重要,重要的是要符合OpenGL的标准:连续的内存中,13、14、15个元素是translation的x、y、z。


化石原创文章,转载请注明来源并保留原文链接