TA的每日心情 | 开心 2020-3-20 17:50 |
---|
签到天数: 1 天 [LV.1]初来乍到
|
在OpenGL的整个渲染流程中很多资料上描述的过程大致相同却又略去了很多细节,可以说是每一份资料的描述都不尽相同,或许每个作者所阐述的重点都不太相同,不存在孰对孰错
《OpenGL ES 3.0编程指南》中对可编程管线的流程介绍如下:
VBO/VAO(顶点缓冲区对象或顶点数组对象)
VertexShader(顶点着色器)
rasterization(光栅化)
FragmentShader(片段着色器)
Per-Fragment Operations(逐片段操作)
pixelOwnershipTest(像素归属测试)
ScissorTest(剪裁测试)
StencilTest and DepthTest(模板和深度测试)
Blending(混合)
dithering(抖动)
Frame Buffer (帧缓冲区)
又例如:《实时计算机图形学》一书中,将图形绘制管线分为三个主要阶段:应用程序阶段、几何阶段、光栅阶段
应用程序阶段:
主要和CPU、内存进行交互,在此阶段有碰撞检测、场景图建立、空间八叉树更新、视锥剪裁等算法都在此阶段执行
几何体数据(顶点坐标、法向量、纹理坐标、纹理等)这些信息会通过数据总线传送到图形硬件数据总线规范了一个大的集成应用系统中同构系统、异构系统等方面进行数据共享和交换实现方法。 系统间数据交换标准
几何阶段:
顶点坐标变换、光照、剪裁、投影、屏幕映射 均在GPU进行运算(顶点着色器就在此阶段执行)
该阶段的末端会得到经过变换和投影之后的顶点坐标、颜色、纹理坐标,这些数据将作为光栅化阶段的输入进一步处理
光栅阶段:
光栅化:决定哪些像素被集合图元覆盖的过程(其实就是将图元拆分成多个片段的过程,图元是由几个点组成的点、线、面)
例如:面形状的图元最少3个点(A、B、C三个点)即可组成,那么屏幕是由众多个像素点组成的,那ABC三个点围成的三角形中有多少个像素点呢?那么其实光栅化就是用来分解图元得到这些像素点的过程,当然这其中还有一个重要的过程就是插值,插值出那个图形区域的像素(纹理坐标v_texCoord、颜色等信息)。注意,此时的像素并不是屏幕上的像素,是不带有颜色的。接下来的片段着色器完成上色的工作。
注意:光栅化不是着色的阶段!片段着色器才是给每个像素绘制颜色的阶段,我在很多博客上都看到过错误的讲解,都有提到光栅化是上色的过程
着色器上色
一.什么是openGL
OpenGL被定义为“图形硬件的一种软件接口”。从本质上说,它是一个3D图形和模型库,具有高度的可移植性,具有非常快的速度。
二.管线
管线这个术语描述了opengl渲染的整个过程。openGL采用cs模型:c是cpu,s是GPU,c给s的输入是vertex信息和Texture信息,s的输出是显示器上显示的图像。下面这2个图比较清楚的讲解了opengl的渲染管线。 三.管线详解
取自《OpenGL ES 3.0编程指南》,此流程为可编程管线。
1.VBO/VAO(顶点缓冲区对象或顶点数组对象):
VBO/VAO(到底是啥,下回讲解)是cpu提供给GPU的顶点信息,包括了顶点的位置、颜色(只是顶点的颜色,和纹理的颜色无关)、纹理坐标(用于纹理贴图)等顶点信息。
2.VertexShader(顶点着色器):
顶点着色器是处理VBO/VAO提供的顶点信息的程序。VBO/VAO提供的每个顶点都执行一遍顶点着色器。Uniforms(一种变量类型)在每个顶点保持一致,Attribute每个顶点都不同(可以理解为输入顶点属性)。执行一次VertexShader输出一个Varying和gl_positon。3.PrimitiveAssembly(图元装配):
顶点着色器下一个阶段是图元装配,图元(prmitive)是三角形、直线或者点精灵等几何对象。这个阶段,把顶点着色器输出的顶点组合成图元。
4.rasterization(光栅化):
光栅化是将图元转化为一组二维片段的过程,然后,这些片段由片段着色器处理(片段着色器的输入)。这些二维片段代表着可在屏幕上绘制的像素。用于从分配给每个图元顶点的顶点着色器输出生成每个片段值的机制称作插值(Interpolation)。这句不是人话的话解释了一个问题,就是从cpu提供的分散的顶点信息是如何变成屏幕上密集的像素的,图元装配后顶点可以理解成变为图形,光栅化时可以根据图形的形状,插值出那个图形区域的像素(纹理坐标v_texCoord、颜色等信息)。注意,此时的像素并不是屏幕上的像素,是不带有颜色的。接下来的片段着色器完成上色的工作。
5.FragmentShader(片段着色器):
片段着色器为片段(像素)上的操作实现了通用的可编程方法,光栅化输出的每个片段都执行一遍片段着色器,对光栅化阶段生成每个片段执行这个着色器,生成一个或多个(多重渲染)颜色值作为输出。6.Per-Fragment Operations(逐片段操作)
在此阶段,每个片段上执行如下功能:
(1)pixelOwnershipTest(像素归属测试):
这个用来确定帧缓冲区中位置(x,y)的像素是不是归当前上下文所有。例如,如果一个显示帧缓冲区窗口被另一个窗口所遮蔽,则窗口系统可以确定被遮蔽的像素不属于此opengl的上下文,从而不显示这些像素。
(2)ScissorTest(剪裁测试):
如果该片段位于剪裁区域外,则被抛弃
(3)StencilTest and DepthTest(模板和深度测试):
深度测试比较好理解,若片段着色器返回的深度小于缓冲区中的深度,则舍弃。模板测试没有用过,不清楚具体功能,猜测功能应该和名字一样,模板形状内可通过。
(4)Blending(混合):
将新生成的片段颜色值与保存在帧缓冲区的颜色值组合起来,产生新的RGBA。
(5)dithering(抖动):
不知道这个是神马作用?
最后把产生的片段放到帧缓冲区(前缓冲区或后缓冲区或FBO)中,若不是FBO,则屏幕绘制缓冲区中的片段,产生屏幕上的像素。
7.固定管线
固定管线是没有shader参与的OpenGL绘制管线,OpenGL3.0已经废除了这个功能。
代码参考
#include <iostream>
#include <GLUT/GLUT.h>
#include <OpenGL/gl.h>
void RenderScene(void) {
glClear(GL_COLOR_BUFFER_BIT);
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBegin(GL_TRIANGLES);
glColor3ub((GLubyte)255, (GLubyte)0, (GLubyte)0);
glVertex3f(0, 200, 0);
glColor3ub((GLubyte)0, (GLubyte)255, (GLubyte)0);
glVertex3f(200, -70, 0);
glColor3ub((GLubyte)0, (GLubyte)0, (GLubyte)255);
glVertex3f(-200, -70, 0);
glEnd();
glutSwapBuffers();
}
void SetupRC(void) {
glClearColor(0, 0, 0, 1);
}
void changeSize(int w, int h) {
GLfloat windowHeight, windowWidth;
if (h == 0) {
h = 1;
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
windowHeight = windowWidth = 250;
glOrtho(-windowWidth, windowWidth, -windowHeight, windowHeight, -1, 1);
}
int main(int argc, char * argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(600, 600);
glutCreateWindow("Simple");
const GLubyte * info = glGetString(GL_VERSION);
std::cout << ( const char *)info << std::endl;
glutReshapeFunc(changeSize);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;} |
|