您的位置:首页 > 运维架构

OpenGL 入门8

2016-12-25 21:38 134 查看
原帖地址:

http://ogldev.atspace.co.uk/www/tutorial09/tutorial09.html

http://blog.csdn.net/cordova/article/details/52562663

http://blog.csdn.net/janestar/article/details/44244849

齐次坐标

在传统的欧式几何里,两条平行线是不会相交的。然而在透视空间里,两条平行线是可以相交的。如下图:



铁轨在极远处相交于一点了。

欧氏空间描述2D/3D非常适合,但是这种方法不适合处理透视空间的问题。2维笛卡尔坐标系可以表示为(x,y),无穷远处的坐标可以表示为(∞,∞)。在欧氏空间,这个点没有意义。平行线在透视空间的无穷远处交于一点,但是在欧氏空间却不能,数学家们发现了一种方式来解决这个问题。这种方式就是其次坐标。

齐次坐标简而言之就是用N+1维表示N维坐标。

我们可以在一个2D笛卡尔坐标末尾加上一个分量w来形成2D的其次坐标。因此一个坐标(x,y)的其次坐标变为了(x,y,w),并且有

X = x/w

Y = y/w

如果有一个点(1,2),那么他的其次坐标为(1,2,1),如果把他移动到无穷远处,他的坐标是(∞,∞),我们可以用(1,2,0)来表示(∞,∞),因为任何数除以0等于无穷大。

为什么要叫做其次坐标呢?

我们把齐次坐标转换成笛卡尔坐标的方法是,将前面的N个元素除以第N+1个元素



将其次坐标转换到笛卡尔坐标时,我们发现了一个规律:



你会发现(1, 2, 3), (2, 4, 6) 和(4, 8, 12)对应同一个Euclidean point (1/3, 2/3),任何标量的乘积,例如(1a, 2a, 3a) 对应 笛卡尔空间里面的(1/3, 2/3) 。因此,这些点是“齐次的”,因为他们代表了笛卡尔坐标系里面的同一个点。换句话说,齐次坐标有规模不变性。

证明:两条直线可以相交

考虑如下方程组:



我们知道在笛卡尔坐标系里面,该方程组无解,因为C ≠ D,如果C=D,两条直线就相同了。

让我们在透视空间里面,用齐次坐标x/w, y/w代替x ,y,



现在我们有一个解(x, y, 0),两条直线相交于(x, y, 0),这个点在无穷远处。

插值

这篇教程讲的是3D渲染管线中非常重要的一部分内容–也就是光栅器对vertex shader传出的变量进行插值处理。在之前的的代码里,为了能让屏幕上显示图像,我们让我们的vertex shader输出了一个叫做gl_Position的内置变量。gl_Position是一个四元向量,也叫做顶点的齐次坐标。我们会将这个齐次坐标转化为笛卡尔坐标。转化完成之后如果向量里面的元素不再[-1,1]范围之内,那说明这个点改被丢弃(还记得在之前教程里我们传入的vertex buffer中的点的坐标吗,都是在范围[-1,1]里面的哦)。转换之后的最终结果将会被映射到屏幕空间中去,然后在用这些点画出图元。

比如从vertex shader传出的数据为三角形的三个顶点,那么光栅器就会对处于这个三角形内的所有像素点启用fragment shader。fragment shader通常会返回一个像素颜色,而这个颜色是存储在一个颜色缓存中的(当然只有在这个像素通过了深度测试或其他测试之后才会返回一个像素颜色)。从vertex shader中传出的其他数据并不会进行上述的过程。如果fragment shader并没有显示的要求某个变量(你可以使用多个fragment shader对应一个vertex shader),那么通常的优化策略会让编译器丢掉vertex shader中与此变量相关的指令(这只是对特定的一对vertex shader和fragment shader而言)。如果fragment shader确实是使用了某个变量,对每一个特定的位置,光栅器都会产生一个此变量的插值,这通常就意味着在两个相邻的像素点上的这个变量会有些许不同(不过如果这个物体离相机太远了,那么他所产生的像素之间的插值可能就不是有特别大的差异,因为离相机太远就说明他看起来越小,也就是占用的像素区域越小,区域内各个像素的插值就不那么明显了)。

有两种类型的变量很依赖这种插值:法线和贴图坐标。一个顶点的法线是由包含这个顶点的所有三角形的法线的平均值构成的,如图:



顶点A的法向量是由三角形a,b,c的法线的平均值构成的。这个法线通常用来进行光照计算以产生真实的光照效果。纹理坐标也是类似的。每个顶点都有自己的纹理坐标,我们用插值的方式来计算出在这些顶点之内的像素的纹理坐标,根据纹理坐标找到纹理中的颜色,进而就确定了这个像素的颜色。

我们暂时在vertex shader里面直接产生颜色信息,还有一种特别麻烦的方式是从vertex buffer里面传入颜色信息(这都是固定管线那会儿的事了)。通常情况下我们不应该在vertex shader里面直接产生颜色。我们通常在vertex shader里面使用纹理坐标,然后根据纹理坐标来采样我们的颜色。这个颜色在光照计算的时候需要用到。

代码

out vec4 Color;


在vertex shader中,我们声明这个vertex shader将会输出一个四个元素构成的颜色向量。这个颜色向量叫做Color。

Color = vec4(clamp(Position, 0.0, 1.0), 1.0);


在opengl里,在颜色通常都是用4个范围处于[0,1]之间的浮点数表示的。对于每一个颜色通道,这些分量都会乘以255,所以颜色的种类数量是255*255*255。我们用一个叫做clamp的函数来用位置信息生成颜色信息。这个函数的作用是将position中的元素的范围限制在[0,1]之间。我们的Position的元素的取值范围是从[-1,1],而小于等于0的颜色会是黑色,这将导致我们啥也看不到。

in vec4 Color;


在fragment shader里面我们声明传入的是Color,也就是veretx shader输出的颜色。

FragColor = Color;


定义fragment shader输出的颜色为vertex shader输入的颜色。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: