// Triangle.cpp
// Our first OpenGL program that will just draw a triangle on the screen.
#include
// OpenGL toolkit
#include
// Shader Manager Class
#ifdef __APPLE__
#include
// OS X version of GLUT
#else
#define FREEGLUT_STATIC
#include
// Windows FreeGlut equivalent
#endif
GLBatch triangleBatch;
GLShaderManager shaderManager;
GLint myIdentityShader;
///
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
}
///
// This function does any needed initialization on the rendering context.
// This is the first opportunity to do any OpenGL related tasks.
void SetupRC()
{
// Blue background
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
shaderManager.InitializeStockShaders();
// Load up a triangle
GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f };
GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f };
triangleBatch.Begin(GL_TRIANGLES, 3);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.CopyColorData4f(vColors);
triangleBatch.End();
myIdentityShader = gltLoadShaderPairWithAttributes("ShadedIdentity.vp", "ShadedIdentity.fp", 2,
GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_COLOR, "vColor");
}
///
// Cleanup
void ShutdownRC()
{
glDeleteProgram(myIdentityShader);
}
///
// Called to draw scene
void RenderScene(void)
{
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glUseProgram(myIdentityShader);
triangleBatch.Draw();
// Perform the buffer swap to display back buffer
glutSwapBuffers();
}
///
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(800, 600);
glutCreateWindow("Shaded Triangle");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
#version 130
in vec4 vColor;
in vec4 vVertex;
out vec4 vVaryingColor;
void main(void)
{
vVaryingColor = vColor;
gl_Position = vVertex;
}#version 130
out vec4 vFragColor;
in vec4 vVaryingColor;
void main(void)
{
vFragColor = vVaryingColor;
}
这里只解析之前未学习过的知识点,如果对源码理解起来吃力,可以回顾上一篇文章的基础知识。http://blog.csdn.net/perseverancep/article/details/72885104,在文章后面解析着色器的源码。
一、源码解析
1、全局变量
GLint myIdentityShader; //加载着色器函数(gltLoadShaderPairWithAttributes)的返回值声明
2、函数解析
1)void SetupRC() //程序一次性渲染设置
// 三角形的顶点坐标
GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f };
//三组渲染颜色(RGBA)按顺序是红绿蓝
GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f };
//设置图元批次
triangleBatch.Begin(GL_TRIANGLES, 3); //开始进行图元的批次设置,有3个顶点
triangleBatch.CopyVertexData3f(vVerts); //把顶点坐标拷贝给图元批次
triangleBatch.CopyColorData4f(vColors);//把要渲染的颜色拷贝给图元批次
triangleBatch.End();//结束图元批次的设置
//加载和初始化着色器。传入顶点和片段程序文件(文章后面解析);“2”是顶点程序中包含属性的个数;表明是一个带有顶点和颜色属性的着色器
myIdentityShader = gltLoadShaderPairWithAttributes("ShadedIdentity.vp", "ShadedIdentity.fp", 2,
GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_COLOR, "vColor");
2)void ShutdownRC() //关闭渲染设置
glDeleteProgram(myIdentityShader);//当不在使用着色器时,需要使用此函数进行删除。
3)void RenderScene(void)//实时渲染
glUseProgram(myIdentityShader); //使用此函数选定使用的着色器
triangleBatch.Draw();//选定着色器后把图元批次提交,进行渲染绘制。
客户端的应用程序的代码解析也是这么多,很简单。下面解析服务端的着色器应用的源码。(回顾上一篇文章的基本着色器架构图)。
二、着色器应用程序源码解析
ShadedIdentity.vp:
#version 130
in vec4 vColor;
in vec4 vVertex;
out vec4 vVaryingColor;
void main(void)
{
vVaryingColor = vColor;
gl_Position = vVertex;
}
ShadedIdentity.fp
#version 130
out vec4 vFragColor;
in vec4 vVaryingColor;
void main(void)
{
vFragColor = vVaryingColor;
}
1、顶点程序ShadedIdentity.vp
#version 130 //指明着色器要求的OpenGL着色语言的最低版本为3.3
in vec4 vColor;//顶点颜色属性值(4分量的向量)(输入值)
in vec4 vVertex;//顶点位置属性值(4分量的向量)(输入值)
out vec4 vVaryingColor;//将被传递到片段着色器的颜色值(声明输出)
void main(void)
{
//给要输出的颜色赋值
vVaryingColor = vColor;
//gl_Position是一个预定义的内建4分量向量,它包含顶点着色器要求的一个输出。
//注意:输入gl_Position的值是几何图形阶段用来装配图元的。既然我们没有进行任何附加的变换,顶点将只映射到所有3个坐标范围都在+/-1.0之间的笛卡尔坐标上。
gl_Position = vVertex;
}
2、片段程序ShadedIdentity.fp
out vec4 vFragColor;//将要进行光栅化的片段颜色(输出值)(光栅化:像素栅格化,可查看计算机图形学理解)
in vec4 vVaryingColor;//从顶点阶段得到的颜色(输入值)
void main(void)
{
vFragColor = vVaryingColor; //对片段进行颜色插值
}
三、小结
到这里通过源码简单的学习理解了一下,基本着色器架构。通过编写顶点和片段程序,在应用中加载这两个着色器程序文件,使用自己定义的着色器渲染和绘制图元。后面继续学习理解着色器程序的统一值寻找和设定的知识。
还有一点要注意:
三角形的颜色为什么是3种颜色过渡的效果,带着这个问题回看一下源码并且回顾一下上一篇文章的对应蓝宝书6.1.3节(p169-p171)“声明输出”部分,
在客户端的程序中,设定了要渲染的颜色值,即三种颜色值的分量,分别是红、绿和蓝。
GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f };
....
triangleBatch.CopyColorData4f(vColors);//把要渲染的颜色拷贝给图元批次
在服务端片段程序中
in vec4 vVaryingColor;
vVaryingColor变量将成为将要传送到片段着色器的顶点的颜色值。默认情况下,变量的声明方式如下:
smooth out vec4 vVaryingColor;
这代表了这个颜色参数将在两个着色器阶段之间以一种透视正确的方法进行补值,这是默认设置(即为声明smooth时,默认的形式)。所以在颜色赋值进行渲染时,实际上是三种颜色之间进行插值,所以测试图形的三角形的颜色是3中图形的过渡效果。
当声明成为flat out vec4 vVaryingColor时,flat关键词代表不进行插值,那么图形就是完全的蓝色。即使用了vColors最后一个分量(蓝色向量)。
关键词可以回顾一下:http://blog.csdn.net/perseverancep/article/details/72885104
最有一点有趣的是我自己做的测试:
在顶点程序中做如下声明:
flat in vec4 vVertex
也就是在in vec4 vVertex前面加上了flat关键词,三角形的颜色变成了白色,同学可以自己理解一下了。
扩展:
上面所注意的是当使用flat限定符修饰时,不进行插值,默认约定是使用为图元的最后一个向量值的值,即为蓝颜色。我们可以使用
Provoking Vertex约定:void glProvokingVertex(GLenum provokMode)
provokMode的合法值为GL_FIRST_VERTEX_CONVENTION和GL_LAST_VERTEX_CONVENTIONS(默认值)
我们在项目中添加如下代码
当按下空格键可以切换这个约定,而三角形则在实心蓝色和实心红色之间来回切换。void KeyPressFunc(unsigned char key, int x, int y) { if(key == 32) { nToggle++; if(nToggle %2 == 0) { glProvokingVertex(GL_LAST_VERTEX_CONVENTION); glutSetWindowTitle("Provoking Vertex - Last Vertex - Press Space Bars"); } else { glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); glutSetWindowTitle("Provoking Vertex - First Vertex - Press Space Bars"); } glutPostRedisplay(); } }