世界矩阵(World Matrix)、视图矩阵(View Matrix)以及投影矩阵(Projection Matirx);
世界矩阵确定一个统一的世界坐标,用于组织独立的物体形成一个完整的场景;
视图矩阵就是我们能看到的那部分场景,由虚拟摄像机负责拍摄;
投影矩阵就是3维物体的平面影射.把三维场景在一个二维的平面上显示.
可编程流水线带来了极大的灵活性,通过Shader语言,可以在GPU上进行计算,释放CPU。而Shader的层次越越来越高,从Suerface级别的Vertexshader和Freagment Shader 到Shape级别的 Geometry shader和Tesselation shader。
帧缓冲(Frame Buffer)
颜色缓冲(Color Buffer)
模板缓冲(Stencil Buffer)
顶点缓冲(Vertice Buffer)
深度缓冲(Depth Buffer)
具体说明参见:Real time rendering - 图形硬件
假设一种不透明东西的颜色是A,另一种透明的东西的颜色是B,那么透过B去看A,看上去的颜色C就是B和A的混合颜色,可以用这个式子来近似,设B物体的透明度为alpha(取值为0-1,0为完全透明,1为完全不透明)
R(C)=alpha*R(B)+(1-alpha)*R(A)
G(C)=alpha*G(B)+(1-alpha)*G(A)
B(C)=alpha*B(B)+(1-alpha)*B(A)
R(x)、G(x)、B(x)分别指颜色x的RGB分量。看起来这个东西这么简单,可是用它实现的效果绝对不简单,应用alpha混合技术,可以实现出最眩目的火光、烟雾、阴影、动态光源等等一切你可以想象的出来的半透明效果。
首先当然是glewint().
讲Shader文件读取进来。
初始化shader...
uniform变量
uniform变量是外部application程序传递给(vertex和fragment)shader的变量。因此它是application通过函数glUniform**()函数赋值的。在(vertex和fragment)shader程序内部,uniform变量就像是C语言里面的常量(const ),它不能被shader程序修改。
attribute变量
attribute变量是只能在vertex shader中使用的变量。(它不能在fragment shader中声明attribute变量,也不能被fragment shader中使用)
一般用attribute变量来表示一些顶点的数据,如:顶点坐标,法线,纹理坐标,顶点颜色等。
在application中,一般用函数glBindAttribLocation()来绑定每个attribute变量的位置,然后用函数glVertexAttribPointer()为每个attribute变量赋值。
varying变量
varying变量是vertex和fragment shader之间做数据传递用的。一般vertex shader修改varying变量的值,然后fragment shader使用该varying变量的值。因此varying变量在vertex和fragment shader二者之间的声明必须是一致的。application不能使用此变量。
如图分别连接BC,BD。当CAD不断的变化的时候,可以得到个无数个三角形CBD,这些三角形的夹BCA和角BDA都是相同的,分别是BA对应的圆心角的一半。
则这些三角形都是相似的。
那么当BD最大的时候,也就是三角形最大的时候,也就是CAD最长的时候。
BD最大的时候就是BD为圆的直径,则BAD为直角,同理,BAC也是直角=>BA 垂直CAD。
程序的基本步骤就是:
1.把N个点按x坐标排序。
2.遍历,求相邻的两个点的斜率,找最大值。
时间复杂度Nlog(N)
9.顶点法线和面法线的作用。
面法线,垂直于平面,位于中央,经常用于flat着色。
点的法线是在使用Phone或Gouraud模型时计算光照使用。如果一个面上的所有法线都一样,他们的光照也就一样,就会产生 flatness 效果;而如果把每个顶点的法向设置不同,则更平滑。
********************************
问题1:大概说下着色器是怎么工作的
对这个问题我就说了一下管线的处理流程。
参考learnopengl着色器
着色器(shader)是运行在GPU上的小程序,类似于C语言,构造一个着色器在其开头必须声明版本。本质上来说,着色器是一个把输入转化为输出的程序。
着色器定义了in
和out
等关键字实现数据的输入和输出,从而实现数据的交流。如果从一个着色器向另一个着色器发送数据,则必须在发送方声明一个输出,在接收方声明一个类似的输入。当类型和名字都相同的时候,便会自动链接在一起,实现数据传递。
另一种从cpu向gpu发送数据的方式是uniform
。uniform
是全局的,无需借助其他中介实现数据传递。在着色器程序中声明uniform
变量,在主程序中通过glGetUniformLocation
获得其地址,从而设置着色器中uniform变量的值。
如1中所述,GLSL运行在GPU,其通过接口实现和CPU之间的数据转换。
opengl程序涉及到两种类型的处理单元--CPU和GPU。opengl主程序由CPU调度运行,图像处理部分通过GLSL交由GPU执行。CPU与GPU之间的数据传递分三个步骤:一,首先利用内置的OpenGL函数生成一个ID号码;二,根据需要对该ID号码进行内存类型的绑定;在经过上面两个步骤之后,GPU中用于接收系统内存中数据的“标识符”就准备好了,第三部对这部分内存进行初始化,初始化的内容来自于系统内存中,这一部分功能利用glBufferData函数完成。
数据提交到GPU专用的内存中之后,需要根据应用场景对这些数据进行适当的分配。比如,有的数据当做顶点,有的是作为颜色,有的用于控制光照等等
此外,由于GPU具有高并行结构(heighly parallel structure),所以GPU在处理图形和复杂算法方面计算效率较高。CPU大部分面积为控制器和寄存器,而GPU拥有更多的ALU(Arithmetric Logic Unit,逻辑运算单云)用于数据处理,而非数据的高速缓存和流控制。
[1] 帧缓冲(frame buffer):帧缓冲是下面几种缓冲的合集。通过帧缓冲可以将你的场景渲染到一个不同的帧缓冲中,可以使我们能够在场景中创建镜子这样的效果,或者做出一些炫酷的特效,存放显示用的数据的。
[2] 颜色缓冲(color buffer):存储所有片段的颜色:即视觉输出的效果。
[3] 深度缓冲(depth buffer):根据缓冲的z值,确定哪些面片被遮挡。由GLFW自动生成。
[4] 模板缓冲(stencil buffer):与深度测试类似,通过比较模板值和预设值,决定是否丢弃片段。
数据在opengl中处理顺序是: 顶点着色器 - 片段着色器 - 模板测试 - 深度测试
参考链接:http://blog.csdn.net/silangqu...
Mipmap是多级渐远纹理,也是目前应用最为广泛的纹理映射(map)技术之一。简单来说,就是实现 “实物(图片)看起来近大远小,近处清晰远处模糊”的效果。它简单来说就是一系列的纹理图像,后一个纹理图像是前一个的二分之一。多级渐远纹理背后的理念很简单:距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个。由于距离远,解析度不高也不会被用户注意到。同时,多级渐远纹理另一加分之处是它的性能非常好
局部空间(local space):或称为 物体空间.指对象所在的坐标空间
世界空间(world space):指顶点相对于(游戏)世界的坐标。物体变换到的最终空间就是世界坐标系
观察空间(view space):观察空间(View Space)经常被人们称之OpenGL的摄像机(Camera)(所以有时也称为摄像机空间(Camera Space)或视觉空间(Eye Space))。观察空间就是将对象的世界空间的坐标转换为观察者视野前面的坐标。因此观察空间就是从摄像机的角度观察到的空间
裁剪空间(clip sapce):或称为视觉空间.在一个顶点着色器运行的最后,OpenGL期望所有的坐标都能落在一个给定的范围内,且任何在这个范围之外的点都应该被裁剪掉(Clipped)。被裁剪掉的坐标就被忽略了,所以剩下的坐标就将变为屏幕上可见的片段。这也就是裁剪空间(Clip Space)名字的由来。
屏幕空间(screen space):顾名思义,一般由glViewPort
设置。
参考链接:http://learnopengl-cn.readthe...
model
:主要针对模型的平移、旋转、缩放、错切等功能,将模型由局部空间转换到世界空间view
:视图矩阵。摄像机/观察者的位置等信息(设置鼠标移动、滚轮等效果),将所有世界坐标转换为观察坐标。projection
:投影矩阵。裁剪坐标 转换到屏幕上
首先简单了解一下什么是"状态机",比如我们使用的电脑,接受各种输入(鼠标,键盘,摄像头等),然后改变自己当前的状态,但却并不知道状态的改变是如何实现的。opengl类似,接受各种参数,然后参数的改变引起当前状态的改变,达到一种新的状态(如:颜色改变,纹理变化,光照强弱变化)。
opengl状态机
冯式光照模型
:环境光照(Ambient)、漫反射(Diffuse)、镜面(Specular)
光源:点光、定向光、手电筒(聚光灯)
主要是通过图形渲染管线(管线:实际上是指一堆原始图像数据途径一个输送管道,期间经过经过各种变换处理,最终输出在屏幕上的过程)
管理的,其被划分为两个过程:1. 把3D坐标转换为2D坐标。 2. 把2D坐标转换为实际有颜色的像素。
2D坐标和像素是不同的,2D坐标精确表示一个点在空间的位置,而2D像素(好像都是整数)是这个点的近似值,2D像素受到个人屏幕/窗口 分辨率的限制。
投影
:在计算机图形学中,投影可以看做一种将三维坐标变换为二维坐标的方法。常用的有:正投影和透视投影。透视投影
:是为了获得接近真实的三维物体效果而在二维平面上绘制、渲染的方法。类似于现实中人对事物的认识(近大远小,远处模糊近期清晰)。正投影
:正射投影矩阵定义了一个类似立方体的平截头箱,它定义了一个裁剪空间,在这空间之外的顶点都会被裁剪掉。摄像机
:观察空间经常被人们称之OpenGL的摄像机(Camera)(所以有时也称为摄像机空间(Camera Space)或视觉空间(Eye Space))。观察空间是将世界空间坐标转化为用户视野前方的坐标而产生的结果。因此观察空间就是从摄像机的视角所观察到的空间。而这通常是由一系列的位移和旋转的组合来完成,平移/旋转场景从而使得特定的对象被变换到摄像机的前方。这些组合在一起的变换通常存储在一个观察矩阵(View Matrix)里,它被用来将世界坐标变换到观察空间。
- 图元:
- 凸多边形(convex polygon):凸多边形满足以下特点:
- 1,所有内角小于180°;
- 2,内部所有点仅在任意一边延长线的一侧;
- 3,内部任意两点的连线也在内部。
- 退化多边形(degenerate polygon):存在共线或者重叠坐标和线段的多边形。
- 图形对象/标准图形对象(graphics object/standard graphics object):使用一组多边形片面描述的对象。
- 填充区(fill area):使用某种颜色或图案进行填充的区域。
- 片元:
- 凹多边形(concave polygon):不是凸多边形的多边形。
- 深度测试:
- 多边形(polygon):任意有封闭折线边界的平面。
- GPU:
- 光栅化:
- 光栅化单元(rasterizer):光栅化单元负责对所有的剪切区域内的图元生成片元数据,然后堆每一个生成的片元都执行一个片元着色器。
- 帧缓存:
- 着色器:
- 着色管线装配(shader plumbing):将应用程序数据与着色器程序的变量关联起来。
- 渲染(render):表示计算机从模型创建最终图像的过程。
- 渲染管线(rendering pipeline):它是一系列数据处理过程,并且将应用程序的数据转换到最终渲染的图像。OpenGL首先接收用户提供的几何数据(顶点或几何图元),并且将他输入到一系列着色器阶段中进行处理,包括:顶点着色、细分着色,以及最后的几何着色,然后它将被送入光栅化单元。
- 像素(pixel):显示设备上的最小可见单位。
- 裁剪空间:
- 标准多边形(standard polygon):不存在边界重叠交叉的多边形。
- 模版测试:
- 模型/场景对象:
- 1、准备向OpenGL传输数据
OpenGL需要将所有的数据都保存到缓存对象(buffer object)中,它相当于OpenGL服务端维护的一块内存区域。- 2、将数据传输到OpenGL
在缓存初始化完成之后,通过调用绘制命令将顶点数据传送到OpenGL服务端。我们可以将一个顶点视为一个需要统一处理的数据包。这个包中的数据可以是我们需要的任何数据。- 3、顶点着色(vertex shading stage)
该阶段将接收你顶点缓存中给出的顶点数据。对于绘制命令传输的每个点,OpenGL都会用一个顶点着色器来处理顶点相关的数据(至少会计算出每个顶点经过建模和观察投影变换后在裁剪空间的坐标)。传递着色器会将数据复制并传递到下一个着色阶段。对于复杂的场景也会有相应的着色方式。- 4、细分着色(tessellation shading stage)
这是一个可选阶段,与应用程序中显式地指定几何图元的方法不同,它会在OpenGL管线内部生成新的几何体。这个阶段启用之后,会收到来自顶点着色器阶段的输出数据,并且对收到的数据进一步处理。- 5、 几何着色(geometry shading stage)
该阶段也是一个可选阶段,假如启用了该阶段,它会收到来自顶点着色或者细分着色(如果它也被启用)的数据,然后在OpenGL管线内部对几乎所有的几何图形进行修改。几何着色器不同于前两个着色器,它不仅仅只能修改输入数据,甚至可以创建新的图元交给后续的流水线处理。该阶段作用于每个独立的几何图元。- 6、图元装配
该阶段会将这些顶点与相关的集合图元之间组织起来,准备下一步的剪切和光栅化工作。- 7、剪切
该阶段用来保证相关的像素不会在窗口之外进行绘制。也就是说将不能被窗口显示的部分去除。- 8、光栅化
图元信息最终被传递给光栅化单元,生成对应的片元。- 9、片元着色(fragment shading stage)
这是最后一个可以通过编程控制屏幕显示颜色的阶段。在这个阶段中会使用着色器来计算片元的最终颜色和它的深度值。(顶点着色决定了一个图元应该处于屏幕的什么位置,片元着色决定了某个片元的颜色应该是什么)。- 10、片元操作
在该阶段会使用深度测试和模版测试的方式来决定一个片元是否是可见的。如果一个片元成功的通过了所有激活的测试,那么他就可以直接被绘制到帧缓存中了,它所对应的像素的颜色值会被更新,如果开启了融合模式,那么对应的片元颜色会与该像素的颜色相叠加,形成一个新的颜色并写入帧缓存中。
整个着色器的使用流程如下:
- 1,创建着色器对象
- 2,把源码关联到每个着色器对象
- 3,编译着色器
- 4,创建程序
- 5,关联着色器到程序对象
- 6,链接程序
- 7,激活和关闭着色器程序
- 8,删除着色器和程序
更多详细信息请参考OpenGL——使用着色器
OpenGL中提供了日志系统,我们只需要查询日志系统,就可以的到相应的错误信息。对于日志,我们需要查询到日志的长度,然后将日志读到一个字符缓冲区再输出。下面的例子实现了这个过程
GLint length;
GLsizei num;
GLchar *log;
glGetProgramiv(program,GL_INFO_LOG_LENGTH,&length);
if(length>0){
log = (GLchar *)malloc(sizeof(GLchar)*length);
glGetProgramInfoLog(program,length,&num,log);
println(log);
free(log);
}
- GL_LINES:可以连接每一对相邻点而得到一组直线。如果最后一个端点处于奇数位置,则该点会被忽略。
- GL_LINE_STRIP:会将传入的点依次连接直到最后一个端点。
- GL_LINE_LOOP:在GL_LINE_STRIP的基础上又进行了首尾相连。
- GL_TRIANGLES:会将矩阵中每三个点取出绘制一个三角形,最后一组不足三个则自动忽略;
- GL_TRIANGLE_STRIP:绘制时满足以下规则:第n(奇数,从1到k)个三角形将会连接n,n+1,n+2三个顶点,对于第n(偶数)个三角形将会连接n+1,n,n+2三个顶点。所以总共会绘制k-2个三角形。
- GL_TRIANGLE_FAN:绘制时满足以下规则:第n(从1到k)个三角形将会连接1,n+1,n+2三个顶点。总共会绘制k-2个三角形。
- GL_QUADS:每四个点绘制一个四边形,最后一组不足四个则忽略。
- GL_QUAD_STRIP:在第n(1..k)个四边形中,奇数满足(2n-1,2n,2n+2,2n+1),偶数满足(2n,2n-1,2n+2,2n-1)。
贝塞尔曲线是依据任意位置的点坐标绘制出的一条光滑曲线。其设计思路是按照规律u从0到1的移动过程中,在各个控制点连线的相应位置取点,并对相邻两条线上的点再次连线,重复以上过程使得没有可连接的两个点。
详细内容请参考图形算法:贝塞尔曲线。
凹多边形中至少有一个内角大于180°。凹多边形会有至少一条边的延长线与其他边相交,有时一对内点的连线会与会与多边形边界相交。我们可以根据里的任意一个特征识别凹多边形。
- 向量法:我们按照同方向为每一条边做一个向量,然后分别计算相邻两个向量的叉积,凸多边形的所有相邻向量叉积的分量均同号,对于正负同时存在的情况,则可确定为凹多边形。
- 观察法:我们可以观察多边形的每一条边的延长线与其他顶点的关系。如果某些顶点在延长线的一侧,而有一些在另一侧,则可以确定为凹多边形。
当我们需要对一个图形进行处理的时候通常需要区分图形的内外区域。对于简单图形的内外识别通常是一件很简单的事,但是对于比较复杂的图形,我们可以采用下面两种常见的方法。
- 奇偶规则(odd-even rule):在任意位置到无限远处做一条射线,统计这条射线穿过的边界数量。如果是奇数表示点所在的位置是图形内部,否则是外部。
- 非零环绕数规则(nonzero winding-number):以逆时针作为旋转方向为每一条边界做一条向量。在任意位置到无限远处做一条射线。在这条射线与边界交叉时根据向量从左到右穿过射线为-1,反之为1,然后统计所有交叉处的和。当和为0时表示当前点所在的位置是外部,否则是内部。
非零环绕数规则可以用来计算两个图形交、并、差运算
- 交集:对两个图形都采用逆时针的环绕方向,然后进行非零环绕计算,所有结果大于1的点所在的集合就是两个图形的交集。
- 并集:对两个图形都采用逆时针的环绕方向,然后进行非零环绕计算,所有非零点的集合就是两个图形的并集。
- 差集:假设有图形A和B,计算A-B的差,我们可以取A为逆时针环绕,取B为顺时针环绕,然后进行非零环绕计算,所有非零点的集合就是A与B的差。
通常情况下我们通过图形顶点的旋转方向来区分前向面与后向面,在此之前我们需要指定前向面的旋转方向。那么我们应该如何判断一个点之于一个面的关系呢?这里我们用到了平面方程
我们可以逆时针选取凸多边形的三个连续顶点 , , ,联立求解上述方程,可以得到求得系数
对于判断控件中任意一点 与该平面的关系,只要将点的坐标带入上述方程即可:
如果 则该点在平面上
如果 则该点在平面后方
如果 则该点在平面前方
上述测试在右手笛卡尔坐标系中有效,多边形表面的方向可由笛卡尔法向量 表示。
简述FrameBuffer,RenderBuffer,Depth Buffer,Framebuffer attachment,Stencil buffer的关系