从OpenGL 1.x 到 2.x的迁移(即从固定管线到可编程管线的迁移)
来源:互联网 发布:网红淘宝 扒皮 编辑:程序博客网 时间:2024/06/04 17:57
OPENGL – THEN AND NOW
I had spent a fair amount of time on OpenGL about 10 years back, though I wouldn’t call myself an expert. Over these 10 years, I noticed OpenGL evolving and kept pace with it from the outside. Then came WebGL and wanted to get my hands dirty. That’s when I realized that I was way out of touch. As they say, the devil is in the details. All the terminology and jargon just wasn’t adding up. So I went back to basics.
Here is an attempt to summarize the evolution and status of OpenGL. It’s not meant to be an introduction to OpenGL but more for those who want to go from “then” to “now” in one page. For more details, see OpenGL – VBO, Shader, VAO.
Background
Traditionally, all graphics processing was done in the CPU which generated a bitmap (pixel image) in the frame buffer (a portion in RAM) and pushed it to the video display. Graphics Processing Unit (GPU) changed that paradigm. It was specialized hardware to do the heavy graphics computations. The GPU provided a set of “fixed” functions to do some standard operations on the graphics data, which is referred to as the Fixed Function Pipeline (FFP). Though the Fixed Function Pipelline was fast and efficient, it lacked flexibility. So GPUs introduced the Programmable Pipeline, the programmable alternative to the “hard coded” approach.
OpenGL
OpenGL 1.0 (Classic OpenGL) provided libraries to compute on the CPU and interfaced with the Fixed Function Pipeline. OpenGL 2.0 (and higher) adds Programmable Pipeline API.
OpenGL-ES (GLES)
OpenGL ES is OpenGL for Embedded Systems for mobile phones, PDAs, and video game consoles, basically for devices with limited computation capability. It consists of well-defined subsets of desktop OpenGL. Desktop graphics card drivers typically did not support the OpenGL-ES API directly. However, as of 2010 graphics card manufacturers introduced ES support in their desktop drivers and this makes the ES term in the specification confusing. OpenGL ES 2.0 is based on OpenGL 2.0 with the fixed function API removed.
WebGL
WebGL is a Programmable Pipeline API, with constructs that are semantically similar to those of the underlying OpenGL ES 2.0 API. It stays very close to the OpenGL ES 2.0 specification, with some concessions made for what developers expect with memory-managed languages such as JavaScript. See WebGL and OpenGL.
Programmable Pipeline, Shaders and GLSL
The Programmable Pipeline requires a Program
which is “equivalent” to the functions provided by the Fixed Function Pipeline. These programs are called Shaders. The programming language for the shaders used to be assembly language but as the complexity increased, high-level languages for GPU programming emerged, one of which is called OpenGL Shading Language (GLSL). Like any program, the Shader program needs to be compiled and linked. However, the Shader code is loaded to the GPU, compiled and linked at runtime using APIs provided by OpenGL.
Conclusion
So, modern OpenGL is great, except it makes learning graphics programming harder (much harder). It is generally easier to teach new graphics programmers using the Fixed Function Pipeline. Ideas behind Shaders are pretty complicated and the minimum required knowledge in basic 3D programming (vertex creation, transformation matrices, lighting etc.) is substantial. There is a lot more code to write and many more places to screw up. A classic fixed function OpenGL programmer was oblivious to most of these nasty details.
“Then” was Fixed Function Pipeline and “Now” is Programmable Pipeline. Much of what was learned then must be abandoned now. Programmability wipes out almost all of the fixed function pipeline, so the knowledge does not transfer well. To make matters worse, OpenGL has started to deprecate fixed functionality. In OpenGL 3.2, the Core Profile lacks these fixed-function concepts. The compatibility profile keeps them around.
The transition in terms of code and philosophy is detailed in OpenGL – VBO, Shader, VAO.
https://cognitivewaves.wordpress.com/2015/04/24/opengl-then-and-now/
OPENGL – VBO, SHADER, VAO
Tutorial with example
As posted in OpenGL-Then and Now, there has been a paradigm shift in rendering. This is a comprehensive look at the transition from “Then” (Immediate Mode, Fixed Functon Pipeline) to “Now” (Retained Mode, Programmable Pipeline). The simple example will incrementally transition from the legacy approach to the current paradigm.
Introduction
Example
Render
Immediate using glBegin
and glEnd
Vertex Array to specify vertex data in client memory
Vertex Buffer Object (VBO) to store vertex data in GPU memory
Shader to define the program to execute on the GPU
Shader with Vertex Buffer Object (VBO) to invoke the Programmable Pipeline
Shader with Verrtex Array Object (VAO) an advanced concept of the Programmable Pipeline
Conclusion
Introduction
A short overview of the underlying concept will help understand the basis of the transition to modern OpenGL. Graphics Processing Unit (GPU) is hardware dedicated (and optimized) for graphics rendering. Modern OpenGL aims for better utilization of the GPU. So the transition is in these broad areas.
Example
The example draws lines and triangles. Different colors are used for each of the entities so that problems can be identified easily.
- The same vertex data, coordinates and color attribute, is used for rendering in various methods.
- The Cartesian coordinate system axes are drawn in RGB at the origin
0,0,0
(a common notation to represent X-axis in red, Y-axis in Green and Z-axis in Blue). - A pyramid with its base in grey and sides in shades of yellow/orange.
- Key stroke commands are provided to rotate, pan and zoon. The list of commands are shown in the output windows.
Further, the example in order to focus on the concept keeps the necessary API calls in a sequence. It is NOT optimized for performance. The example code can be accessed on GitHub at https://github.com/cognitivewaves/OpenGL-Render.
Immediate
The simplest way to “experience” OpenGL is to draw in immediate mode using per vertex attribute specification between glBegin
and glEnd
.
static
void
drawImmediate()
{
// Draw x-axis in red
glColor3d(ac[0], ac[1], ac[2]);
glBegin(GL_LINES);
glVertex3f(av[0], av[1], av[2]);
glVertex3f(av[3], av[4], av[5]);
glEnd();
// Draw y-axis in green
glColor3d(ac[3], ac[4], ac[5]);
glBegin(GL_LINES);
glVertex3f(av[0], av[1], av[2]);
glVertex3f(av[6], av[7], av[8]);
glEnd();
// Draw z-axis in blue
glColor3d(ac[6], ac[7], ac[8]);
glBegin(GL_LINES);
glVertex3f(av[0], av[1], av[2]);
glVertex3f(av[9], av[10], av[11]);
glEnd();
// Draw pyramid
glBegin(GL_TRIANGLES);
glColor3d(pc[0], pc[1], pc[2]);
glVertex3f(pv[0], pv[1], pv[2]);
// 0
glVertex3f(pv[3], pv[4], pv[5]);
// 1
glVertex3f(pv[6], pv[7], pv[8]);
// 2
glVertex3f(pv[6], pv[7], pv[8]);
// 2
glVertex3f(pv[9], pv[10], pv[11]);
// 3
glVertex3f(pv[0], pv[1], pv[2]);
// 0
glColor3f(pc[3], pc[4], pc[5]);
glVertex3f(pv[0], pv[1], pv[2]);
// 0
glVertex3f(pv[9], pv[10], pv[11]);
// 3
glVertex3f(pv[12], pv[13], pv[14]);
// 4
glColor3f(pc[6], pc[7], pc[8]);
glVertex3f(pv[9], pv[10], pv[11]);
// 3
glVertex3f(pv[6], pv[7], pv[8]);
// 2
glVertex3f(pv[12], pv[13], pv[14]);
// 4
glColor3f(pc[9], pc[10], pc[11]);
glVertex3f(pv[6], pv[7], pv[8]);
// 2
glVertex3f(pv[3], pv[4], pv[5]);
// 1
glVertex3f(pv[12], pv[13], pv[14]);
// 4
glColor3f(pc[12], pc[13], pc[14]);
glVertex3f(pv[3], pv[4], pv[5]);
// 1
glVertex3f(pv[0], pv[1], pv[2]);
// 0
glVertex3f(pv[12], pv[13], pv[14]);
// 4
glEnd();
}
Vertex Array
A slightly better way to specify vertex data in immediate mode is using Vertex Arrays (not to be confused with Vertex Array Objects discussed later). The vertex data is stored in array format in client memory. The respective attribute association is specified with glVertexPointer
and glColorPointer
. Data is transferred to the GPU in bulk for every frame.
static
void
drawVertexArray()
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
// Set axes data
glVertexPointer(nVertexComponents, GL_FLOAT, 0, ave);
glColorPointer(nColorComponents, GL_FLOAT, 0, ace);
// Draw axes
glDrawArrays(GL_LINES, 0, nLines*nVerticesPerLine);
// Set pyramid data
glVertexPointer(nVertexComponents, GL_FLOAT, 0, pve);
glColorPointer(nColorComponents, GL_FLOAT, 0, pce);
// Draw pyramid
glDrawArrays(GL_TRIANGLES, 0, nFaces*nVerticesPerFace);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}
Extensions
APIs for VBO, Shader, VAO etc. are dependent on OpenGL extensions which have to be loaded dynamically. Though it is a nifty concept, it is not like linking to a dynamic library. Pointers to the required functions have to be loaded explicitly. There are OpenGL Loading Libraries which abstracts (and hides) the loading mechanism. But in the example, it is done manually to get a feel for it. See glext.h
and glextload.h
in the source code.
Vertex Buffer Object (VBO)
An even better way is to store the vertex data directly on the GPU. These GPU memory pools are called Vertex Buffer Objects. This is achieved by setting a current buffer with glBindBuffer
and copying the contents from client memory to the VBO with glBufferData
. The GPU can then access the data directly and will save the cost of transferring from client memory to GPU memory every frame. The attribute association is again specified with glVertexPointer
and glColorPointer
.
glVertexPointer
and glColorPointer
to specify the vertex attributes now copied in the VBOs. I say “reuse” because the 4th parameter pointer has a different meaning depending on the context. So in this case, the same APIs implicitly sets the vertex and color because a VBO is bound prior to the call.If a non-zero named buffer object is bound to the GL_ARRAY_BUFFER target (see glBindBuffer) while a vertex array is specified, pointer is treated as a byte offset into the buffer object’s data store.
static
void
drawVertexBufferObject()
{
LoadGLExtensions();
vboIds =
new
GLuint[3];
glGenBuffers(3, vboIds);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
// Set axes data
glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
// coordinates
glBufferData(GL_ARRAY_BUFFER,
sizeof
(ave), ave, GL_STATIC_DRAW);
glVertexPointer(nCoordsComponents, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vboIds[1]);
// color
glBufferData(GL_ARRAY_BUFFER,
sizeof
(ace), ace, GL_STATIC_DRAW);
glColorPointer(nColorComponents, GL_FLOAT, 0, 0);
// Draw axes
glDrawArrays(GL_LINES, 0, nLines*nVerticesPerLine);
// Set pyramid data
glBindBuffer(GL_ARRAY_BUFFER, vboIds[2]);
// coordinates
glBufferData(GL_ARRAY_BUFFER,
sizeof
(pve), pve, GL_STATIC_DRAW);
glVertexPointer(nCoordsComponents, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vboIds[3]);
// color
glBufferData(GL_ARRAY_BUFFER,
sizeof
(pce), pce, GL_STATIC_DRAW);
glColorPointer(nColorComponents, GL_FLOAT, 0, 0);
// Draw pyramid
glDrawArrays(GL_TRIANGLES, 0, nFaces*nVerticesPerFace);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
// Disable the VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Shader
Shaders are user defined programs/code written in GLSL (OpenGL Shading Language) that are executed on the GPU in the rendering pipeline. Very basic shaders are used as the focus is on the overall setup rather than GLSL.
- Vertex Shader – applies the projection and model-view matrix to each of the vertices
- Fragment Shader – applies the color attribute specified at the vertices
const
char
* vertex_shader =
"attribute vec3 aCoords;"
"attribute vec3 aColor;"
"uniform mat4 umvMat;"
"uniform mat4 upMat;"
"varying vec3 vColor;"
"void main () {"
"gl_Position = upMat * umvMat * vec4(aCoords, 1.0);"
"vColor = aColor;"
"}"
;
const
char
* fragment_shader =
"varying vec3 vColor;"
"void main () {"
"gl_FragColor = vec4 (vColor, 1.0);"
"}"
;
These programs replace the fixed functions provided in legacy OpenGL. Since these programs run on the GPU, the source written in OpenGL Shading Language (GLSL) has to be first first loaded, then complied and linked on the GPU using appropriate API.
GLuint VERTEX_ATTR_COORDS = 1;
GLuint VERTEX_ATTR_COLOR = 2;
static
void
initShaders()
{
GLuint vs = glCreateShader (GL_VERTEX_SHADER);
glShaderSource (vs, 1, &vertex_shader, NULL);
glCompileShader (vs);
GLuint fs = glCreateShader (GL_FRAGMENT_SHADER);
glShaderSource (fs, 1, &fragment_shader, NULL);
glCompileShader (fs);
program = glCreateProgram();
glAttachShader (program, fs);
glAttachShader (program, vs);
glBindAttribLocation(program, VERTEX_ATTR_COORDS,
"aCoords"
);
glBindAttribLocation(program, VERTEX_ATTR_COLOR,
"aColor"
);
glLinkProgram (program);
glUseProgram (program);
}
Shader with Vertex Buffer Object (VBO)
The shaders are loaded on the GPU, and are responsible for portions of the rendering pipeline. So it is necessary for the shader code to access the vertex data to process it. Since the shader is on the GPU, the vertex data too has to be on the GPU in VBOs. The vertex data is copied to the GPU Vertex Buffer Objects as before with glBindBuffer
and glBufferData
.
Then comes the most confusing part – associating the attributes (coordinates, color, etc.) in VBOs to be accessed by the shader variables. The magic happens in this one function glVertexAttribPointer
which creates generic vertex attributes. It establishes a relation between the bound (current) VBO to the shader attribute variable associated (using glBindAttribLocation
) with its index
. See OpenGL Terminology Demystified for a detailed explanation.
static
void
drawShaderWithVertexBufferObject()
{
LoadGLExtensions();
initShaders();
// Get the variables from the shader to which data will be passed
GLint mvloc = glGetUniformLocation(program,
"umvMat"
);
GLint ploc = glGetUniformLocation(program,
"upMat"
);
GLint vertexAttribCoords = glGetAttribLocation(program,
"aCoords"
);
GLint vertexAttribColor = glGetAttribLocation(program,
"aColor"
);
// Pass the model-view matrix to the shader
GLfloat mvMat[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mvMat);
glUniformMatrix4fv(mvloc, 1,
false
, mvMat);
// Pass the projection matrix to the shader
GLfloat pMat[16];
glGetFloatv(GL_PROJECTION_MATRIX, pMat);
glUniformMatrix4fv(ploc, 1,
false
, pMat);
vboIds =
new
GLuint[4];
glGenBuffers(4, vboIds);
// Set axes data
glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
// coordinates
glBufferData(GL_ARRAY_BUFFER,
sizeof
(ave), ave, GL_STATIC_DRAW);
glVertexAttribPointer(vertexAttribCoords, nCoordsComponents, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertexAttribCoords);
glBindBuffer(GL_ARRAY_BUFFER, vboIds[1]);
// color
glBufferData(GL_ARRAY_BUFFER,
sizeof
(ace), ace, GL_STATIC_DRAW);
glVertexAttribPointer(vertexAttribColor, nColorComponents, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertexAttribColor);
// Draw axes
glDrawArrays(GL_LINES, 0, nLines*nVerticesPerLine);
// Set pyramid data
glBindBuffer(GL_ARRAY_BUFFER, vboIds[2]);
// coordinates
glBufferData(GL_ARRAY_BUFFER,
sizeof
(pve), pve, GL_STATIC_DRAW);
glVertexAttribPointer(vertexAttribCoords, nCoordsComponents, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertexAttribCoords);
glBindBuffer(GL_ARRAY_BUFFER, vboIds[3]);
// color
glBufferData(GL_ARRAY_BUFFER,
sizeof
(pce), pce, GL_STATIC_DRAW);
glVertexAttribPointer(vertexAttribColor, nColorComponents, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertexAttribColor);
// Draw pyramid
glDrawArrays(GL_TRIANGLES, 0, nFaces*nVerticesPerFace);
// Disable the VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Shader with Vertex Array Object (VAO)
VAOs provide a way to “pre-define” vertex data and its attributes. It is like creating “objects” which hold the required states to render. In this example, two VAOs are created, one for the axes and the other for the pyramid. The code defining a VAO is the same as rendering using VBOs. Instead of binding the buffer, copying data to the buffer and creating a generic vertex attribute during render, these steps are “defined” in a VAO.
static
void
defineVAO()
{
vaoIds =
new
GLuint[2];
glGenVertexArrays(2, vaoIds);
vboIds =
new
GLuint[4];
glGenBuffers(4, vboIds);
GLint vertexAttribCoords = glGetAttribLocation(program,
"aCoords"
);
GLint vertexAttribColor = glGetAttribLocation(program,
"aColor"
);
// Bind VAO (set current) to define axes data
glBindVertexArray(vaoIds[0]);
glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
// coordinates
glBufferData(GL_ARRAY_BUFFER,
sizeof
(ave), ave, GL_STATIC_DRAW);
glVertexAttribPointer(vertexAttribCoords, nCoordsComponents, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertexAttribCoords);
glBindBuffer(GL_ARRAY_BUFFER, vboIds[1]);
// color
glBufferData(GL_ARRAY_BUFFER,
sizeof
(ace), ace, GL_STATIC_DRAW);
glVertexAttribPointer(vertexAttribColor, nColorComponents, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertexAttribColor);
// Bind VAO (set current) to define pyramid data
glBindVertexArray(vaoIds[1]);
glBindBuffer(GL_ARRAY_BUFFER, vboIds[2]);
// coordinates
glBufferData(GL_ARRAY_BUFFER,
sizeof
(pve), pve, GL_STATIC_DRAW);
glVertexAttribPointer(vertexAttribCoords, nCoordsComponents, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertexAttribCoords);
glBindBuffer(GL_ARRAY_BUFFER, vboIds[3]);
// color
glBufferData(GL_ARRAY_BUFFER,
sizeof
(pce), pce, GL_STATIC_DRAW);
glVertexAttribPointer(vertexAttribColor, nColorComponents, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertexAttribColor);
// Disable VAO
glBindVertexArray(0);
}
During render, the VAO(s) are simply bound (made current) for the GPU to access the information about the “pre-defined” VBOs and vertex attributes.
static
void
drawShaderWithVertexArrayObject()
{
LoadGLExtensions();
initShaders();
defineVAO();
// Get the variables from the shader to which data will be passed
GLint mvloc = glGetUniformLocation(program,
"umvMat"
);
GLint ploc = glGetUniformLocation(program,
"upMat"
);
// Pass the model-view matrix to the shader
GLfloat mvMat[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mvMat);
glUniformMatrix4fv(mvloc, 1,
false
, mvMat);
// Pass the projection matrix to the shader
GLfloat pMat[16];
glGetFloatv(GL_PROJECTION_MATRIX, pMat);
glUniformMatrix4fv(ploc, 1,
false
, pMat);
// Enable VAO to set axes data
glBindVertexArray(vaoIds[0]);
// Draw axes
glDrawArrays(GL_LINES, 0, nLines*nVerticesPerLine);
// Enable VAO to set pyramid data
glBindVertexArray(vaoIds[1]);
// Draw pyramid
glDrawArrays(GL_TRIANGLES, 0, nFaces*nVerticesPerFace);
// Disable VAO
glBindVertexArray(0);
}
Conclusion
Modern OpenGL is powerful but can be intimidating until you understand the nitty gritty details. Hope that the document is useful. Feel free to comment if you need any clarifications or further information.
- 从OpenGL 1.x 到 2.x的迁移(即从固定管线到可编程管线的迁移)
- opengl从固定渲染管线到可编程渲染管线
- 从固定图形管线到可编程流处理器
- 从固定图形管线到可编程流处理器
- shader 入门 《从固定图形管线到可编程流处理器》
- 固定管线和可编程管线的区别
- 从Tomcat 5.5.x到Tomcat 6.0的迁移
- 应用程序从Windows到Mac OS x的迁移
- 应用程序从Windows到Mac OS x的迁移
- 应用程序从Windows到Mac OS x的迁移
- 应用程序从Windows到Mac OS x的迁移
- OpenGL固定管线与可编程管线对比
- [OpenGL] 从顶点坐标到光栅化(渲染管线)
- 固定渲染管线与可编程渲染管线的区别
- OpenGL ES 之OpenGL ES 1.X的渲染管线
- 从JBoss Seam 2.x迁移到JavaEE 7之二:组件的有效范围Scope
- 从JBoss Seam 2.x迁移到JavaEE 7之三:对象的注入
- OpenGL(十一) 可编程管线 基础光照 的实现
- 为什么发个文章还需要审核
- 匈牙利(大视野1191,1854)
- C++ Rvalue References Explained (c++右值引用详解)
- 蓝桥杯 带分数
- gitignore无法忽略文件,忽略文件失败
- 从OpenGL 1.x 到 2.x的迁移(即从固定管线到可编程管线的迁移)
- n^n的个位数字及求和情况
- LeetCode 495. Teemo Attacking
- linux 内存优化
- Java七种排序算法教程
- Ada and Queue
- C++编程学习52个经典网站 强力推荐
- c语言中的关键字const
- IT学习者