详解第一个OpenGL程序

 

【OpenGL】详解第一个OpenGL程序

  27733人阅读  评论(37)  收藏  举报
  分类:
OpenGL 3.3+(8) 

目录(?)[+]


写在前面


OpenGL能做的事情太多了!很多程序也看起来很复杂。很多人感觉OpenGL晦涩难懂,原因大多是被OpenGL里面各种语句搞得头大,一会gen一下,一会bind一下,一会又active一下。搞到最后都不知道自己在干嘛,更有可能因为某一步的顺序错误导致最后渲染出错,又或者觉得记下这些操作的顺序是非常烦人的一件事。那么,OpenGL为什么会长成这个样子呢?这篇文章旨在通过一个最简单的OpenGL程序开始,让我们能够“看懂”它,“记住”这些操作顺序。


我们先来解释一下OpenGL为什么会涉及这么多操作顺序。这是因为,和我们现在使用的C++、C#这种面向对象的语言不同,OpenGL中的大多数函数使用了一种基于状态的方法,大多数OpenGL对象都需要在使用前把该对象绑定到context上。这里有两个新名词——OpenGL对象和Context。


Context

Context是一个非常抽象的概念,我们姑且把它理解成一个包含了所有OpenGL状态的对象。如果我们把一个Context销毁了,那么OpenGL也不复存在。


OpenGL对象

我们可以把OpenGL对象理解成一个状态的集合,它负责管理它下属的所有状态。当然,除了状态,OpenGL对象还会存储其他数据。注意。这些状态和上述context中的状态并不重合,只有在把一个OpenGL对象绑定到context上时,OpenGL对象的各种状态才会映射到context的状态。因此,这时如果我们改变了context的状态,那么也会影响这个对象,而相反地,依赖这些context状态的函数也会使用存储在这个对象上的数据。


因此,OpenGL对象的绑定既可能是为了修改该对象的状态(大多数对象需要绑定到context上才可以改变它的状态),也可能是为了让context渲染时使用它的状态。


画了一个图,仅供理解。图中灰色的方块代表各种状态,箭头表示当把一个OpenGL对象绑定到context上后,对应状态的映射。



前面提到过,OpenGL就是一个“状态机”。那些各种各样的API调用会改变这些状态,或者根据这些状态进行操作。但我们要注意的是,这只是说明了OpenGL是怎样被定义的,但硬件是否是按状态机实现的就是另一回事了。不过,这不是我们需要担心的地方。


OpenGL对象包含了下面一些类型:Buffer Objects,Vertex Array Objects,Textures,Framebuffer Objects等等。我们下面会讲到Vertex Array Objects这个对象。


这些对象都有三个相关的重要函数:

[cpp]  view plain  copy
 print ?
  1. void glGen*(GLsizei n​, GLuint *objects​);  


负责生成一个对象的name。而name就是这个对象的引用。


[cpp]  view plain  copy
 print ?
  1. void glDelete*(GLsizei n​, const GLuint *objects​);  

负责销毁一个对象。


[cpp]  view plain  copy
 print ?
  1. void glBind*(GLenum target​, GLuint object​);  

将对象绑定到context上。


关于OpenGL对象还有很多内容,这里就不讲了。可以参见官方wiki。



在开始第一个程序之前,我们还要了解一些图形名词。

  • 渲染(Rendering):计算机从模型到创建一张图像的过程。OpenGL仅仅是其中一个渲染系统。它是一个基于光栅化的系统,其他的系统还有光线追踪(但有时也会用到OpenGL)等。

  • 模型(Models)或者对象(Objects):这里两者的含义是一样的。指从几何图元——点、线、三角形中创建的东西,由顶点指定。

  • Shaders:这是一类特殊的函数,是在图形硬件上执行的。我们可以理解成,Shader是一些为图形处理单元(GPU)编译的小程序。OpenGL包含了编译工具来把我们编写的Shader源代码编译成可以在GPU上运行的代码。在OpenGL中,我们可以使用四种shader阶段。最常见的就是vertex shaders——它们可以处理顶点数据;以及fragment shaders,它们处理光栅化后生成的fragments。vertex shaders和fragment shaders是每个OpenGL程序必不可少的部分。

  • 像素(pixel):像素是我们显示器上的最小可见元素。我们系统中的像素被存储在一个帧缓存(framebuffer)中。帧缓存是一块由图形硬件管理的内存空间,用于供给给我们的显示设备。


惊鸿一瞥



我们的第一个程序(不完整)的运行结果如下:




代码如下(提示:这里可以粗略地看下中文注释,后面会更详细讲述的):

[cpp]  view plain  copy
 print ?
  1. ///  
  2. //  
  3. // triangles.cpp  
  4. //  
  5. ///  
  6.   
  7.   
  8. //--------------------------------------------------------------------  
  9. //  
  10. // 在程序一开头,我们包含了所需的头文件,  
  11. // 声明了一些全局变量(但通常是不用全局变量在做的,这里只是为了说明一些基本问题)  
  12. // 以及其他一些有用的程序结构  
  13. //  
  14.   
  15. #include   
  16. using namespace std;  
  17.   
  18. #include "vgl.h"  
  19. #include "LoadShaders.h"  
  20.   
  21. enum VAO_IDs { Triangles, NumVAOs };  
  22. enum Buffer_IDs { ArrayBuffer, NumBuffers };  
  23. enum Attrib_IDs { vPosition = 0 };  
  24.   
  25. GLuint  VAOs[NumVAOs];  
  26. GLuint  Buffers[NumBuffers];  
  27.   
  28. const GLuint NumVertices = 6;  
  29.   
  30. //---------------------------------------------------------------------  
  31. //  
  32. // init  
  33. //  
  34. // init()函数用于设置我们后面会用到的一些数据.例如顶点信息,纹理等  
  35. //  
  36.   
  37. void init(void) {  
  38.     glGenVertexArrays(NumVAOs, VAOs);  
  39.     glBindVertexArray(VAOs[Triangles]);  
  40.   
  41.     // 我们首先指定了要渲染的两个三角形的位置信息.  
  42.     GLfloat  vertices[NumVertices][2] = {  
  43.         { -0.90, -0.90 },  // Triangle 1  
  44.         {  0.85, -0.90 },  
  45.         { -0.90,  0.85 },  
  46.         {  0.90, -0.85 },  // Triangle 2  
  47.         {  0.90,  0.90 },  
  48.         { -0.85,  0.90 }  
  49.     };  
  50.   
  51.     glGenBuffers(NumBuffers, Buffers);  
  52.     glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);  
  53.     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),  
  54.                      vertices, GL_STATIC_DRAW);  
  55.   
  56.     // 然后使用了必需的vertex和fragment shaders  
  57.     ShaderInfo  shaders[] = {  
  58.             { GL_VERTEX_SHADER, "triangles.vert" },  
  59.             { GL_FRAGMENT_SHADER, "triangles.frag" },  
  60.             { GL_NONE, NULL }  
  61.     };  
  62.   
  63.     // LoadShaders()是我们自定义(这里没有给出)的一个函数,  
  64.     // 用于简化为GPU准备shaders的过程,后面会详细讲述  
  65.     GLuint program = LoadShaders(shaders);  
  66.     glUseProgram(program);  
  67.     // 最后这部分我们成为shader plumbing,  
  68.     // 我们把需要的数据和shader程序中的变量关联在一起,后面会详细讲述  
  69.     glVertexAttribPointer(vPosition, 2, GL_FLOAT,  
  70.                           GL_FALSE, 0, BUFFER_OFFSET(0));  
  71.     glEnableVertexAttribArray(vPosition);  
  72. }  
  73.   
  74. //---------------------------------------------------------------------  
  75. //  
  76. // display  
  77. //  
  78. // 这个函数是真正进行渲染的地方.它调用OpenGL的函数来请求数据进行渲染.  
  79. // 几乎所有的display函数都会进行下面的三个步骤.  
  80. //  
  81.   
  82. void display(void) {  
  83.     // 1. 调用glClear()清空窗口  
  84.     glClear(GL_COLOR_BUFFER_BIT);  
  85.   
  86.     // 2. 发起OpenGL调用来请求渲染你的对象  
  87.     glBindVertexArray(VAOs[Triangles]);  
  88.     glDrawArrays(GL_TRIANGLES, 0, NumVertices);  
  89.   
  90.     // 3. 请求将图像绘制到窗口  
  91.     glFlush();  
  92. }  
  93.   
  94. //---------------------------------------------------------------------  
  95. //  
  96. // main  
  97. //  
  98. // main()函数用于创建窗口,调用init()函数,最后进入到事件循环(event loop).  
  99. // 这里仍会看到一些以gl开头的函数,但和上面的有所不同.  
  100. // 这些函数来自第三方库,以便我们可以在不同的系统中更方便地使用OpenGL.  
  101. // 这里我们使用的是GLUT和GLEW.  
  102. //  
  103.   
  104. int main(int argc, char** argv) {  
  105.     glutInit(&argc, argv);  
  106.     glutInitDisplayMode(GLUT_RGBA);  
  107.     glutInitWindowSize(512, 512);  
  108.     glutInitContextVersion(4, 3);  
  109.     glutInitContextProfile(GLUT_CORE_PROFILE);  
  110.     glutCreateWindow(argv[0]);  
  111.   
  112.     if (glewInit()) {  
  113.         cerr << "Unable to initialize GLEW ... exiting" << endl; exit(EXIT_FAILURE);  
  114.     }  
  115.     init();  
  116.   
  117.     glutDisplayFunc(display);  
  118.   
  119.     glutMainLoop();  
  120. }  


Vertex Shader如下:
[plain]  view plain  copy
 print ?
  1. #version 430 core  
  2. layout(location = 0) in vec4 vPosition;  
  3. void  
  4. main()  
  5.  {  
  6.      gl_Position = vPosition;  
  7. }  

Fragment Shader如下:
[plain]  view plain  copy
 print ?
  1. #version 430 core  
  2. out vec4 fColor;  
  3. void  
  4. main()  
  5. {  
  6. fColor = vec4(0.0, 0.0, 1.0, 1.0);  
  7. }  




OpenGL的语法


这里插播一段语法解释。从上面可以看出,OpenGL里面的函数长得都有一个特点,都是由“gl”开头的,然后紧跟一个或多个大写字母(例如,glBindVertexArray())。而且可以告诉,所有的OpenGL函数都长这样。在上面的程序里面还有一些函数是“glut”开头的,这是来自OpenGL实用工具(OpenGL Utility Toolkit)——GLUT。这是一个非常流行的跨平台工具,可以用于打开窗口、管理输入等操作。龙书用的GLUT版本是Freeglut,是原始GLUT的一个变种。GLUT已经不再更新了。。。Sad。。。同样,还有一个函数,glewInit(),它来自GLEW库。GLUT和GLEW就是龙书所用的两个库了。

和OpenGL函数的命名规范类似,在display()函数里见到的GL_COLOR_BUFFER_BIT这样的常量,也是OpenGL定义的。它们由GL_开头,实用下划线来分割字符。它们的定义就是通过OpenGL头文件(glcorearb.h和glewt.h)里面的#define指令定义的。

OpenGL为了跨平台还自己定义了一系列数据类型,如GLfloat。而且,因为OpenGL是一个“C”语言库,它不使用函数重载来解决不同类型的数据问题,而是使用函数命名规范来组织不同的函数。例如,后面我们会碰到一个函数叫glUniform*(),这个函数有很多形式,例如,glUniform2f()和glUniform3fv。这些函数名字后面的后缀——2f和3fv,提供了函数的参数信息。例如,2f中的2表示有两个数据将会传递给函数,f表示这两个参数的类型是GLfloat。而3fv中最后的v,则是vector的简写,表明这三个GLfloat将以vector的形式传递给函数,而不是三个独立的参数。

一些例子中没有使用OpenGL定义的数据类型,直接使用了float这样的变量。这可能会造成在不同平台上不兼容的问题



在三维的世界里,所有的故事都是从顶点开始的。虽然题目是“详解第一个程序”,但目的是为了让大家理解最基础的顶点是怎么一步步传递到GLSL中的。


重点内容开始!


传递顶点数据:你会怎么做



那么,现在的问题是,如果是你,你会怎么把顶点和它相关的信息,例如纹理坐标、法线等,传递给GLSL呢?一般人都会想到多维数组。我们下面把它称为顶点流(Vertex Stream)。(什么?!你不是这么想的?!没关系,OpenGL是这么想的就好。。。)

我们负责创建这个顶点流,然后只需要告诉OpenGL怎样解读它就可以了。

为了渲染一个对象,我们必须使用一个shader program。而这个program会定义一系列顶点属性,例如上述Vertex Shader中的vPosition一行。这些属性决定了我们需要传递哪些顶点数据。每一个属性对应了一个数组,并且这些数据的维度都必须相等,即是一一对应的关系。

比如我们想要渲染3个顶点,我们会定义下面的数据:
[plain]  view plain  copy
 print ?
  1. { {1, 1, 1}, {0, 0, 0}, {0, 0, 1} }  

这些顶点的顺序是非常重要的,OpenGL将会根据这些顺序渲染网格。我们可以直接使用上述这种数据来直接渲染,也可以使用索引(indices)来指定顺序,这样可以重复使用同一个顶点。

例如,我们使用下面的索引列表:
[plain]  view plain  copy
 print ?
  1. {2, 1, 0, 2, 1, 2}  

那么OpenGL将会渲染6个顶点:
[plain]  view plain  copy
 print ?
  1. { {0, 0, 1}, {0, 0, 0}, {1, 1, 1}, {0, 0, 1}, {0, 0, 0}, {0, 0, 1} }  


现在,我们还想传递一个新的顶点属性,即每个顶点的纹理坐标,那么新的纹理数组可能长这样:
[plain]  view plain  copy
 print ?
  1. { {0, 0}, {0.5, 0}, {0, 1} }  

注意,纹理数据的维度大小一定要和上面的坐标数组大小一致,而其他顶点属性数组的维度也要满足这个条件。这是非常容易理解的。

那么,合并后的顶点属性列表就是:
[plain]  view plain  copy
 print ?
  1. [{0, 0, 1}, {0, 1}], [{0, 0, 0}, {0.5, 0}], [{1, 1, 1}, {0, 0}], [{0, 0, 1}, {0, 1}], [{0, 0, 0}, {0.5, 0}], [{0, 0, 1}, {0, 1}] }  



OpenGL的做法:VAO和VBO



OpenGL使用了VAO来实现上述管理顶点数据的数据作用,以及VBO来存放真正的顶点属性数据。

VAO(Vertex Array Object)



我们这里遇到了第一种OpenGL对象——VAO(Vertex Array Object)。前面说到OpenGL对象是状态的集合,那么VAO就是所有顶点数据的状态集合。它存储了顶点数据的格式以及顶点数据数据所需的缓存对象的引用。前面提过,OpenGL对象都有三个非常重要的函数,而VAO对应的就是glGenVertexArrays、glDeleteVertexArrays和glBindVertexArray​。

VAO负责管理顶点属性,而这些顶点属性从0到GL_MAX_VERTEX_ATTRIBS​ - 1 被编号。这些属性在Vertex Shader里的表现就是类似下面的语句:
[plain]  view plain  copy
 print ?
  1. layout(location = 0) in vec4 vPosition;  

上述顶点属性vPosition被编号为0。

每个属性可以被enable或者disable,被disable的属性是不会传递给shader的,即便在shader里定义了这些属性,它们读出的值也会是一个常量,而非真正的数据。一个新建的VAO的所有属性访问都是disable的。而开启一个属性是通过下面的函数:
[cpp]  view plain  copy
 print ?
  1. void glEnableVertexAttribArray​(GLuint index​);  

与其对应的是 glDisableVertexAttribArray​  函数。

而为了使用上述函数来改变VAO的状态,我们首先需要把VAO绑定到当前的context上。



VBO(Vertex Buffer Object)



VBO是一种Buffer Object,即它也是一个OpenGl对象。VBO是顶点数组数据真正所在的地方。

为了指定一个属性数据的格式和来源,我们需要告诉OpenGL,编号为0的属性使用哪个VBO,编号为1的属性使用哪个VBO等等。为了实现它,我们可以这么做。

首先,我们要知道,任何VBO都需要先绑定到GL_ARRAY_BUFFER​才可以对它进行操作。绑定后,我们可以调用下面的函数之一:
[cpp]  view plain  copy
 print ?
  1. void glVertexAttribPointer​( GLuint index​, GLint size​, GLenum type​,  
  2.    GLboolean normalized​, GLsizei stride​, const void *offset​);  
  3.  void glVertexAttribIPointer​( GLuint index​, GLint size​, GLenum type​,  
  4.    GLsizei stride​, const void *offset​ );  
  5.  void glVertexAttribLPointer​( GLuint index​, GLint size​, GLenum type​,  
  6.    GLsizei stride​, const void *offset​ );  

它们的作用大同小异,就是告诉OpenGl,编号为index的属性使用当前绑定在 GL_ARRAY_BUFFER​的VBO。为了更好理解,我们举例:
[cpp]  view plain  copy
 print ?
  1. glBindBuffer(GL_ARRAY_BUFFER, buf1);  
  2. glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);  
  3. glBindBuffer(GL_ARRAY_BUFFER, 0);  

上面第一行代码将buf1绑定到了 GL_ARRAY_BUFFER​上。第二行意味着,编号为0的属性将使用buf1的数据,因为当前绑定到 GL_ARRAY_BUFFER​上的是buf1。第三行将缓存对象0绑定到了 GL_ARRAY_BUFFER​上,这不会对顶点属性有任何影响,只有 glVertexAttribPointer​函数可以影响它们!

这个过程就像一个中介人的作用,而中介人就是GL_ARRAY_BUFFER​。我们可以这么想,glBindBuffer​ 设置了一个全局变量,然后glVertexAttribPointer读取了这个全局变量并把它存储在VAO中,这个全局变量就是GL_ARRAY_BUFFER。当调用完glVertexAttribPointer后,顶点属性已经知道了数据来源就是buf1,它们之间就会直接联系,而不需要在通过GL_ARRAY_BUFFER



写在最后


写OpenGL的博客心好累。。。。。。

虽然说了这么多,大家可能也没怎么看懂,但重点其实就是“状态机”。大家记住这一点也算没白费。


参考资料:
  • OpenGL Programming Guide 8th Edition 
  • OpenGL Wiki






24
 
0
 
 

我的同类文章

OpenGL 3.3+(8)
  • 【OpenGL】理解一些基本问题2014-09-30阅读11604
  • 【OpenGL】关于OpenGL中Bind函数的理解2013-05-25阅读5053
  • 【OpenGL】向Shader中传递数据2013-04-21阅读11927
  • 【OpenGL】初识OpenGL4.02013-04-20阅读8617
  • 【OpenGL】使用随机采样实现soft shadow2013-05-27阅读3518
  • 【OpenGL】GLSL中的函数和子程序(subroutines)2013-05-25阅读8688
  • 【OpenGL】Shader概述2013-04-20阅读4661
  • 【OpenGL】OpenGL绘图的一点理解2013-04-15阅读2795
猜你在找
顾荣:开源大数据存储系统Alluxio(原Tachyon)的原理分析与案例简介
Ceph—分布式存储系统的另一个选择
Android之数据存储
Android核心技术——Android数据存储
Android中的数据存储
OpenGL二第一个OpenGL程序绘制三角形
OpenGL 编写第一个OpenGL程序
OpenGL系统设计-2 第一个OpenGL程序
Qt编译OpenGL程序遇到的问题
调用opengl程序代码
查看评论
20楼  miaoyuqiao2016-12-14 04:59发表 [回复]
写得很好啊,不过我觉得给初学者看的话,还是先区分一下可编程管线和固定管线,还有shader是什么。
19楼  sinat_365737092016-11-01 01:32发表 [回复]
哦哦.明白了
18楼  NuinLu2016-10-06 23:49发表 [回复]
写的很好,我买了你的书,佩服博主图形学功底
17楼  wocpp2016-04-24 00:46发表 [回复]
看完之后,豁然开朗,神清气爽。学习中,回头遇到不懂的还来这里请教,赞赞赞。之前我也经常说一句话:如果要你来设计,你会怎么做。这句话帮了我不少忙。哈哈哈哈
16楼  olpf11282016-03-25 09:57发表 [回复]
你好!可以请教你个问题吗?opengl程序中怎么从控制台输入数据,并传给相应的参数?比如:void DDALine(int x0,int y0,int x1,int y1)--> glutDisplayFunc(DDALine);
Re:  wocpp2016-04-24 01:22发表 [回复]
回复olpf1128:要是我的话,就定义几个枚举,然后枚举值和函数指针映射。参数穿枚举值,运行时通过映射设置函数指针。
一般也就是这么做的吧。只不过区别在于如何让别人知道枚举的定义和映射的关系,及映射和函数指针的映射关系生成方法?
Re:  olpf11282016-05-11 11:15发表 [回复]
回复wocpp: 哦哦 ,谢谢!后来我用的是通过另一个无形参的函数调用DDAline(x0,y0,x1,y1),在这个函数中给DDAline()传参就行。你觉得这种方法怎样?
15楼  baidu_341000332016-02-27 17:54发表 [回复]
我做了一个文档,不懂的可以下载来看看
链接: http://pan.baidu.com/s/1bklAzW 密码: y663
14楼  chenchen51872015-10-16 09:44发表 [回复]
楼主 这个程序代码要求显卡支持opengl4.3 ,可我的电脑只支持到opengl4.0 ,运行程序时总是提醒 shaders指针错误 修改这个glutInitContextVersion(3, 3); 还是不行 楼主 知道 怎么解吗
13楼  yijianguxin6262015-10-13 22:10发表 [回复]
你好,很想学习OpenGL,上网下载了示例代码,lib文件包含了很多freeglut和glew等的信息,但是不知道如何在本地配置开发环境,win10+vs2015,还望指点,谢谢
12楼  yijianguxin6262015-10-13 22:09发表 [回复]
你好,很想学习OpenGL,上网下载了示例代码,lib文件包含了很多freeglut和glew等的信息,但是不知道如何在本地配置开发环境,win10+vs2015,还望指点,谢谢
11楼  中年風雨2015-10-10 12:06发表 [回复]
我调试时候,为什么glutInitContextVersion(4, 3);如果设定3.1版本之后的参数,程序就无法运行?是什么运行库有问题吗?
Re:  hoodlum19802015-10-13 23:08发表 [回复]
回复pardream:这取决于你的 windows 系统自带的 opengl 的版本,就是那个 opengl32.dll 。
比如说我装的win xp sp2 系统,自带的opengl 是 3.1.0;
而我的 win7 系统,自带的 opengl 好像是 4.2.xxx 。
Re:  中年風雨2015-12-06 00:45发表 [回复]
回复hoodlum1980:懂了,谢谢。
10楼  元始天尊20152015-07-29 08:56发表 [回复]
不错,谢谢分享
9楼  破哲2015-06-24 19:08发表 [回复]
讲的实在是好,真是详细。
8楼  AGUAAGUA2015-06-09 00:06发表 [回复]
我总觉得状态机设计是个对像我这种新手特别不友好的设计,debug起来非常头疼!代码一长就不知道那个地方bind了那个地方active了。
Re:  妈妈说女孩子要自立自强2015-06-09 08:27发表 [回复]
回复AGUAAGUA:额OpenGL刚开始学得的时候的确很麻烦,还是多写吧,慢慢就习惯啦
7楼  chenxun20092015-04-08 10:41发表 [回复]
#version 430 core
layout(location = 0) in vec4 vPosition;
void
main()
{
gl_Position = vPosition;
}
博主,你好,这个代码应该放在那儿啊?
直接放在triangles.cpp中吗???
Re:  中年風雨2015-10-10 12:04发表 [回复]
回复chenxun2009: 放在triangles.vert中,书中不是有说明吗?能让程序运行时找到本文件即可。
Re:  SlimTracy2015-06-03 15:10发表 [回复]
回复chenxun2009:shader 写在外部,可以写在文件中,读出来的还是字符串。
6楼  jihai36162015-04-07 22:03发表 [回复]
版主运行是不会出错吗?
triangles.obj : error LNK2019: 无法解析的外部符号 _LoadShaders,该符号在函数 "void __cdecl init(void)" (?init@@YAXXZ) 中被引用
这是什么问题?望解答一下,是缺少什么文件吗?还是配置有问题?
Re:  妈妈说女孩子要自立自强2015-04-08 10:33发表 [回复]
回复jihai3616:这里没给出来而已,书里面的源代码是有的,自己加进去就可以了~
Re:  jihai36162015-04-08 11:53发表 [回复]
回复candycat1992:指的是triangles.vert和triangles.frag两个着色器吗?那是加到哪里?
Re:  妈妈说女孩子要自立自强2015-04-08 12:05发表 [回复]
回复jihai3616:缺少的是LoadShaders这个程序吧,没有cpp文件。
Re:  jihai36162015-04-08 12:44发表 [回复]
回复candycat1992:非常感谢,已解决,好厉害
Re:  u0106966142015-06-18 16:46发表 [回复]
能说下如何解决的吗?
Re:  u0106966142015-06-18 19:35发表 [回复]
已解决,LoadShaders.cpp文件虽然在当前目录中,但没加入到工程中,所以出问题,将其加入到工程中即可解决了。
5楼  Jenf_Mu2015-03-26 15:37发表 [回复]
提示找不到文件 "triangles.vert"
这个文件在哪,那本书的所有代码我都下载了,我用everything都搜不到
Re:  hoodlum19802015-10-13 23:14发表 [回复]
回复Jenf_Mu:这是 shader 源码,就是一个文本文件(名字和后缀任意取,反正我们只关心文件的内容)。你自己用记事本创建一个新文件,然后问问博主这个文件的内容,从别人那里复制下就可以了。
不过对于这种入门的第一课,vertex shader 的内容通常就是,假设顶点属性1是顶点颜色,
设置一个输出变量,把属性1(顶点颜色)赋值给这个输出就完了。

后面的 fragment shader 再把这个输出设置成该像素的 color。这样就是一个插值后的颜色渐变过渡的三角形。
Re:  妈妈说女孩子要自立自强2015-03-28 16:09发表 [回复]
回复Jenf_Mu:书里直接给了代码了,没有文件,非常简单的代码。
4楼  Ssalg2015-02-26 22:47发表 [回复]
深度晕乎中哈哈。。。。受益匪浅
3楼  Decimalism2015-01-25 11:12发表 [回复]
#include "vgl.h" 
#include "LoadShaders.h" 
请教博主,这两个文件是在哪的?
Re:  妈妈说女孩子要自立自强2015-01-25 17:16发表 [回复]
回复Decimalism:你可以去下载OpenGL Programming Guide 8th Edition的源代码,里面有相关代码
2楼  x4161044432014-12-28 20:35发表 [回复]
还是被绕的有点糊涂
Re:  妈妈说女孩子要自立自强2014-12-29 12:22发表 [回复]
回复x416104443:其实这篇在草稿箱里写很久了。。。因为自己也总是被绕晕,不知道怎么写。可能学一段时间再回过头看会觉得好一点吧~
1楼  h1372865102014-12-01 20:48发表 [回复]
最近在看android里面的opengl,不过你这个确实看不懂啊,不过记住了状态机,哈哈哈
发表评论
  • 用 户 名:
  • yz2010
  • 评论内容:
      
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
核心技术类目
全部主题  Hadoop  AWS  移动游戏  Java  Android  iOS  Swift  智能硬件  Docker  OpenStack  VPN  Spark  ERP  IE10  Eclipse  CRM  JavaScript  数据库  Ubuntu  NFC  WAP  jQuery  BI  HTML5  Spring  Apache  .NET  API  HTML  SDK  IIS  Fedora  XML  LBS  Unity  Splashtop  UML  components  Windows Mobile  Rails  QEMU  KDE  Cassandra  CloudStack  FTC  coremail  OPhone  CouchBase  云计算  iOS6  Rackspace  Web App  SpringSide  Maemo Compuware  大数据  aptech  Perl  Tornado  Ruby  Hibernate  ThinkPHP  HBase  Pure  Solr  Angular  Cloud Foundry  Redis  Scala  Django  Bootstrap
    个人资料
    详解第一个OpenGL程序_第1张图片 
    妈妈说女孩子要自立自强
     
    4
    • 访问:1731844次
    • 积分:14084
    • 等级: 
    • 排名:第717名
    • 原创:110篇
    • 转载:20篇
    • 译文:21篇
    • 评论:1968条
    关于我
    我叫乐乐,程序媛一枚,就读于上海交通大学软件学院,研究生,数字媒体方向。喜欢用计算机来绘制各种五彩缤纷的画面~欢迎访问我的 独立博客和 作品集 :)

    邮件:lelefeng1992 # gmail DOT com
    PS:为防止垃圾邮件,请自行转换为正确格式哦~

    我的书
    详解第一个OpenGL程序_第2张图片

    项目源码+彩图+勘误

    ---------------------

    亚马逊

    当当

    京东

    我的Shadertoy
    博客专栏
    详解第一个OpenGL程序_第3张图片 ShaderToy

    文章:5篇

    阅读:68828
    详解第一个OpenGL程序_第4张图片 OpenGL 4.0+

    文章:2篇

    阅读:39345
    Unity Shaders

    文章:44篇

    阅读:633488
    文章分类
  • Unity3D(16)
  • ShaderToy(6)
  • Unity Shaders(56)
  • Shader实战(1)
  • NPR(5)
  • 图形学(2)
  • 我的书(1)
  • OpenGL 3.3+(9)
  • 图像处理(1)
  • 闲谈(5)
  • 游戏周边(2)
  • Android(19)
  • Web前端(4)
  • 网络(11)
  • 编译原理(12)
  • Ubuntu(3)
  • 汇编基础教程(7)
  • 面向对象(6)
  • 读书笔记(4)
  • 面试/笔试(0)
    文章存档
  • 2016年08月(2)
  • 2016年05月(3)
  • 2016年04月(1)
  • 2016年01月(2)
  • 2015年12月(2)
    展开
    阅读排行
  • 【Unity技巧】四元数(Quaternion)和旋转(122747)
  • 【Unity技巧】Unity中的优化技术(69204)
  • 【Unity Shader实战】卡通风格的Shader(一)(40999)
  • 【Unity技巧】调整画质(贴图)质量(39675)
  • 【Unity Shaders】初探Surface Shader背后的机制(38132)
  • [Android编程心得] Camera(OpenCV)自动对焦和触摸对焦的实现(35926)
  • 【Unity Shaders】Transparency —— 使用alpha通道创建透明效果(35661)
  • 【ShaderToy】开篇(34423)
  • 【Unity Shaders】Vertex & Fragment Shader入门(34113)
  • 【Unity技巧】使用单例模式Singleton(31247)

你可能感兴趣的:(OpenGL)