#include <iostream>
#include<glew.h>
#include<freeglut.h>
#include<gl.h>
#pragma comment(lib,"glew32.lib")
#pragma comment(lib,"freeglut.lib")
#pragma comment(lib,"glut32.lib")
//#pragma comment(lib,"opengl.lib")
/*/#define BUFFER_OFFSET(offset)((GLint *)NULL+offset)用这一个会变成奇怪的颜色*/
#define BUFFER_OFFSET(bytes)((GLvoid *)(bytes))/*/这个是在书上的,这种竟然没有在附录的代码里面*/
const int NumTimesToSubdivide = 5;/*/这里是递归的次数*/
const int NumTetrahedrons = 1024; /*4^5 tetrahedrons;根据上面递归五次,所以就是4的五次方个四面体*/
const int NumTriangles = 4 * NumTetrahedrons; /*/ 4 triangles / tetrahedron每个四面体有4各面*/
const int NumVertices = 3 * NumTriangles; /*/ 3 vertices / triangle 每个面有3个顶点*/
/*@注释 *这个看书上说是在vec.h的头文件里面,可是我电脑上没有,
*我只知道,书上是说vec是甚么向量,
*vec2是连续两个float,
*vec3是连续三个float,
*vec4是连续四个float,
*我尝试自己弄这个结构体,并有构造函数初始化。
*/
struct vec3
{
GLfloat x, y, z;
vec3(){}
vec3(GLfloat hong, GLfloat lv, GLfloat lan)
:x(hong), y(lv), z(lan) {}
vec3 operator +(vec3 other)const
{
vec3 temp(x + other.x,y + other.y,z + other.z);
return temp;
}
vec3 operator/(GLint t)
{
vec3 temp(x/t, y/t, z/t);
return temp;
}
};
typedef ::vec3 point3;
typedef ::vec3 color3;/*书上说甚么opengl习惯性这样,对于我来说,觉得这样很啰嗦,明明都是同一个类型*/
point3 points[NumVertices];
color3 YanSe[NumVertices];
color3 vertex_Yase[4] =
{
color3(0.6f, 0.7f, 0.0f),
color3(0.0f, 1.0f, 0.0f),
color3(0.0f, 0.5f, 0.2f),
color3(0.4f, 0.0f, 0.0f)
/*四面体有四个三角面,所以就设置了四种颜色*/
};
int Index = 0;
//----------------------------------------------------------------------------
int colorIndex;
/*@注释*对于由a、b、c确定的一个三角面,三个点设置同一个颜色才会是纯色的,否则是渐变的代码
*/
void
triangle(const vec3& a, const vec3& b, const vec3& c)
{
YanSe[Index] = vertex_Yase[colorIndex];
points[Index++] = a;
YanSe[Index] = vertex_Yase[colorIndex];
points[Index++] = b;
YanSe[Index] = vertex_Yase[colorIndex];
points[Index++] = c;
/*@注释*下面是渐变代码
YanSe[Index] = vertex_Yase[0];
points[Index++] = a;
YanSe[Index] = vertex_Yase[1];
points[Index++] = b;
YanSe[Index] = vertex_Yase[2];
points[Index++] = c;
*/
}
//----------------------------------------------------------------------------
/*@说明*下面的colorIndex用于给
*最终的每一个四面体的
*四个三角面triangle涂上不同的纯色,
*如果要用渐变,可以去掉colorIndex
*/
void
tetra(const vec3& a, const vec3& b, const vec3& c, const vec3& d)
{
colorIndex = 0;
triangle(a, b, c);
colorIndex = 1;
triangle(a, c, d);
colorIndex = 2;
triangle(a, d, b);
colorIndex = 3;
triangle(b, d, c);
}
//----------------------------------------------------------------------------
/*@注释*递归划分*/
void
divide_tetra(const vec3& a, const vec3& b,
const vec3& c, const vec3& d, int count)
{
if (count > 0) {
vec3 v0 =(a + b) / 2;
vec3 v1 = (a + c) / 2;
vec3 v2 = (a + d) / 2;
vec3 v3 = (b + c) / 2;
vec3 v4 = (c + d) / 2;
vec3 v5 = (b + d) / 2;
divide_tetra(a, v0, v1, v2, count - 1);
divide_tetra(v0, b, v3, v5, count - 1);
divide_tetra(v1, v3, c, v4, count - 1);
divide_tetra(v2, v5, v4,d , count - 1);
}
else {
tetra(a, b, c, d); // draw tetrahedron at end of recursion
}
}
//----------------------------------------------------------------------------
/*@注释*读取文件,以字符串的形式返回*/
static char* readShaderSource(const char *file)
{
FILE *fp;
if (file == NULL)
return NULL;
fopen_s(&fp,file, "rb"); /*@注释,我这里是用的vs2013,vs2013不让用fp=fopen(file, "rb");*/
if (fp == NULL)return NULL;
fseek(fp, 0, SEEK_END);/*@注释:将fp所指向的文件位置指针移到以SEEK_END的基准位置,以0为位移量位置*/
GLsizei length = ftell(fp);
rewind(fp);/*@注释:将fp指示文件中位置的的指针置于文件的开头位置,并清楚文件爱你结束标志和错误标志*/
GLchar * shaderChan =new GLchar[length + 1];
fread(shaderChan, sizeof(char), length, fp);
shaderChan[length] = ‘\0‘;
if (shaderChan == NULL)
{
delete[]shaderChan;//删除着色器字符串
std::cerr << file << "error" << std::endl;
exit(EXIT_FAILURE);
}
fclose(fp);
return shaderChan;
}
/*@注释*创建并编译着色器*/
GLuint buildShader(const char* FileName, GLenum shaderType) {
GLchar*shaderChan = readShaderSource(FileName);//以字符串的形式读取文件
GLuint shaderHandle = glCreateShader(shaderType);
glShaderSource(shaderHandle, 1,&shaderChan, NULL);
//(const GLchar**)把着色器对象与相应的着色器字符相关联
delete[]shaderChan;//删除着色器字符串
glCompileShader(shaderHandle); //编译着色器
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
// 若失败...
std::cerr << FileName << "failed to compile:" << std::endl;
GLint logsize;
glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logsize);
char*logMsg = new char[logsize+1];
glGetShaderInfoLog(shaderHandle, logsize, NULL, logMsg);
std::cerr << logMsg << std::endl;
delete[] logMsg;
exit(EXIT_FAILURE);
}
return shaderHandle;
}
/*@注释*初始化着色器*/
GLuint InitShader(const char *vertexShaderFilename, const char *fragmentShaderFilename)
{
/*创建顶点着色器对象和片元着色器对象*/
GLuint vertexShaderHandle = buildShader(vertexShaderFilename, GL_VERTEX_SHADER);
GLuint fragmentShaderHandle = buildShader(fragmentShaderFilename, GL_FRAGMENT_SHADER);
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShaderHandle);
glAttachShader(programHandle, fragmentShaderHandle);
glLinkProgram(programHandle);
GLint linkSuccess;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
// 判断连接是否成功...
std::cerr << "shader program failed to link:" << std::endl;
GLint logsize;
glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logsize);
char*logMsg = new char[logsize];
glGetProgramInfoLog(programHandle, logsize, NULL, logMsg);
std::cerr << logMsg << std::endl;
delete[]logMsg;
exit(EXIT_FAILURE);
}
glUseProgram(programHandle);
return programHandle;
}
/*@注释*初始化GLSL*/
void
init(void)
{
/*@注释:初始化四个顶点*/
vec3 vertices[4] = {
vec3(1.0, -1.0,0.0),
vec3(-1.0,-1.0, 0.0),
vec3(0.0,1.0, 0.0),
vec3(0.0, 0.0, 0.6)
};
/*@注释:调用递归划分Subdivide the original tetrahedron*/
divide_tetra(vertices[0], vertices[1], vertices[2], vertices[3],
NumTimesToSubdivide);
/*@注释: 创建一个顶点数组对象*/
GLuint vao;
glGenVertexArrays(1, &vao);/*注释:顶点数组对象标识申请,管理着顶点属性的集合*/
glBindVertexArray(vao);/*@注释:在这里将做3个事情:1、如果参数是非0,并且是glGenvertexArrays()返回的新值,未经glBindVertexArray的
*那么他将创建一个新的顶点数组对象(这里才真正创建),并且与其名称关联起来。
*2、如果绑定包已经创建过的顶点数组对象,那么该顶点数组对象将被激活。这便于帧间切换绘制数据
*3、如果输入参数是0那么OpenGL将不再使用程序所分配的任何顶点数组对象,并且将渲染状态重设为默认值。*/
/*@注释 创建并初始化一个缓冲区对象*/
GLuint buffer;
glGenBuffers(1, &buffer);/*注释:申请顶点缓存对象标识。*/
glBindBuffer(GL_ARRAY_BUFFER, buffer);
/*注释:申请顶点缓存对象标识。
*指定顶点缓存对象的用途,GL_ARRAY_BUFFER表示顶点数据。之力glBindBuffer同样完成了3项工作:
*1、如果是第一次绑定对象(第二个参数),他是一个非0的无符号整型,那么将创建一个与名称对应的(第一个参数)新的缓存对象
*2、如果绑定到一个已经创建的缓存对象,那么她将被激活为当前使用对象。
*3、如果第二个参数是0,那么OpenGL不再为当前名称(第一个参数)应用任何缓存对象。*/
glBufferData(GL_ARRAY_BUFFER, sizeof(points) + sizeof(YanSe), NULL, GL_STATIC_DRAW);
/*注释:glBufferData( GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage )
*向缓存对象输入数据的环节:, glBufferData是真正为缓存对象分配存储空间。
*1、target目标:顶点数据(GL_ARRAY_BUFFER)、索引数据(GL_ELEMENT_ARRAY_BUFFER)、OpenGL的像素数据(GL_PIXEL_UNPACK_BUFFER)、
*从OpenGL中获取的像素数据(GL_PIXEL_PACK_BUFFER)、缓存之间复制数据(GL_COPY_READ_BUFFER/GL_COPY_WRITE_BUFFER)、
*纹理缓存中存储的纹理数据(GL_TEXTURE_BUFFER)、一致性变量(GL_UNIFORM_BUFFER)
*2、size:表示缓存数据的总量,字节数。
*3、data:是客户端应用程序的内存指针,数据的来源。要么是NULL,否则如果合法则将会有size大小的数据从客户端拷贝到服务端(显卡内存),如果data数据未初始化
*将保留size大小的内存备用。
*4、usage:用于设置分配数据之后的读取和写入方式,这关系都OpenGL对于缓存对象存储数据中的最优分配方案的管理。说白了,这个参数试图向OpenGL提供
*这堆数据的用途,是否只读,是否静态,用于绘制?拷贝?通过内置标识符的方式告诉OpenGL,OpenGL根据信息来优化内存分配,管理。*/
glBufferSubData(GL_ARRAY_BUFFER, 0,sizeof(points) ,points);
/*@注释*glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_positions), vertex_positions );
*用数据部分替换目标缓存的内容,注意在glBufferData中我们的data为NULL,所以在这里才真正初始化。
*这个接口使得我们的操作更为灵活,对数据的组织更为紧凑,因为我们只为顶点坐标数据和颜色数据分配了一块连续的空间。
*第二个参数是偏移地址,第三个参数是替换数据的大小,第四个参数是客户端内存指针,也就是数据源。
*需要注意的是不可超越glBufferData保留的内存。
*到这里关于缓存对象的操作只是最简单的部分,还有很多OpenGL接口供我们去灵活控制,优化。*/
glBufferSubData(GL_ARRAY_BUFFER, sizeof(points),sizeof(YanSe) ,YanSe);
/*@注释*glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertex_positions), sizeof(vertex_colors), vertex_colors);*/
/*@注释*载入着色器并使用所得到的着色器程序*/
GLuint program = InitShader("vshader3.glsl", "fshader3.glsl");
glUseProgram(program);
/*@注释* 初始化顶点着色器中的顶点位置属性*/
GLuint loc = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(loc);
/*@注释*参数对应于glVertexAttribPointer的index.也就是location值,与这个值相关联的定点数组将被启用*/
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0));
/*@注释* / glVertexAttribPointer(GLuint index, Glint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer)
//index:着色器中属性的位置,还记得layout(location = 0)的定义么,就是这个location.
//size:表示每个顶点该属性需要更新的分量数目,这里顶点坐标为4个float,所以就是4.值的范围可以是1,2,3,4或GL_BGRA
//type:数据类型
//normalized:使用顶点数据之前是否要进行归一化。
//stride:每组数据之间是否要进行偏移,如果是0则,数据是紧密的。
//pointer:表示缓存对象中,从开始位置开始计算数组数据的偏移值*/
GLuint se = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(se);/*@注释*启用颜色属性数组 */
glVertexAttribPointer(se, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(points)));
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);/*@注释*背景色 */
/*@注释*建立视图*/
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glEnable(GL_DEPTH_TEST);/*@注释*开启深度缓存测试*/
glEnable(GL_CULL_FACE);///*@注释*启动多边形剔除功能*/
//*@注释*这里glortho也可以为gluOrtho2D( 1.0, 1.0,1.0, 0.0);*/
glOrtho(1.0,0.0,0.0,1.0,0.0,1.0);
}
//----------------------------------------------------------------------------
void
display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, NumVertices);
glFlush();
}
//----------------------------------------------------------------------------
void
keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 033:
exit(EXIT_SUCCESS);
break;
}
}
void changeSize(int w, int h)
{
/*@注释:重新定义视口*/
glViewport(0, 0, w, h);
/*@注释:重新设置投影变换*/
if (h == 0) h = 1;/*@注释:防止高为0产生除0错误*/
float ratio = 1.0 * w / h;/*@注释:宽高比*/
glMatrixMode(GL_PROJECTION);/*@注释:当前矩阵设为投影矩阵*/
glLoadIdentity();/*@注释:清空投影矩阵*/
gluPerspective(45, ratio, 1, 1000);/*@注释:重设投影矩阵*/
/*@注释:模型视点变换*/
glMatrixMode(GL_MODELVIEW);/*@注释:当前矩阵设为模型视点矩阵*/
}
//----------------------------------------------------------------------------
int
main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE| GLUT_RGBA | GLUT_DEPTH);/*@注释:这里要GLUT_SINGLE*/
glutInitWindowPosition(100, 100);
glutInitWindowSize(540, 540);
glutInitContextVersion(3, 1);
glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
glutInitContextProfile(GLUT_CORE_PROFILE);
glutCreateWindow("Simple GLSL example");
/*@注释:窗口大小变化时的处理函数 */
glewExperimental = GL_TRUE;
glewInit();
/*@注释:检测是否支持基本的顶点着色器和片元着色器*/
if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader)
{
printf("Ready for GLSL\n");
}
else
{
printf("No GLSL support\n");
exit(1);
}
init();
glutDisplayFunc(display);
glutIdleFunc(display);
glutReshapeFunc(changeSize);/*@注释:注册窗口大小改变时的回调函数 changeSize*/
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
上面是这里代码的效果图,更改点坐标和颜色可获得其他效果图