【OpenGL】向Shader中传递数据

传递顶点属性信息


之前讲过,vertex shader会被每个顶点调用,通常一个顶点会包含很多信息,例如顶点坐标、顶点法向量、纹理坐标等等,我们称这些信息为顶点的属性。在之前的OpenGL版本里,每个属性都对应了一个特定的通道,我们使用glVertex,glTexCoord,,glNormal(或者通过访问指针函数glVertexPointer, glTexCoordPointer, orglNormalPointer)等函数来访问和设置它们。随后,shader会自己通过内置变量gl_Vertex 和 gl_Normal来访问这些属性。但在OpenGL3.0版本里,这些都被弃用了。在后续版本里,甚至都被移除了。而现在,这些通过generic vertex attributes,图形顶点属性,来提供(通常和顶点缓冲对象互相协作来完成)。我们可以根据需要为每个顶点定义任意数量的(其实是在0到GL_MAX_VERTEX_ATTRIBS – 1之间)属性。OpenGL会为这些属性定义一个索引,我们只要根据这些索引来访问就可以了。

在一个vertex shader里,一个顶点属性是由GLSL 标识符 “in” 来定义的。例如,下面的代码里定义了两个vec3的属性,VertexPosition和VertexColor。

  1. #version 400
  2. in vec3VertexPosition;
  3. in vec3 VertexColor;
  4. out vec3 Color;
  5. void main()
  6. {
  7. Color = VertexColor;
  8. gl_Position = vec4(VertexPosition,1.0);
  9. }


注意到这里还有一个类型为vec3的输出 Color,它将传递给下一层处理的shader中,这里也就是我们的fragment shader(fragment可以理解为具有很多属性的像素)。这里,我们还是使用之前那个简单的fragment shader。

  1. #version 400
  2. in vec3 Color;
  3. out vec4 FragColor;
  4. void main() {
  5. FragColor = vec4(Color, 1.0);
  6. }


当然,vertexshader真正的数据还需要我们从OpenGL程序里传入。这就有了两种向Shader传递顶点属性的方法。

在这之前,我们首先要创建一个VAO,vertex array object,顶点数组对象。主要想要仔细了解这个是干嘛的,还请自行查阅资料,还是很多滴。这里,我们只要知道,它包含了我们的缓冲区和输入的顶点属性之间的对应关系就可以了。在一个OpenGL程序里,我们可以使用多个VAO,并在它们之间切换。现在我们只使用一个。

  1. GLuint VertexArrayID;
  2. glGenVertexArrays(1,&VertexArrayID);
  3. glBindVertexArray(VertexArrayID);



最后一句话表明,接下来我们所做的所有绑定操作,例如建立顶点属性和输入之间的对应关系,都是针对这个VAO的。

下面我们需要为要传输的数据生成对应的缓冲区。这些缓冲区随后会在绘制函数中,通过顶点属性索引传递给我们的shader。

因为在本例中,我们有两个属性值,因此需要建立两个缓冲区。

首先定义缓冲区的数据,也就是我们的顶点信息。我们现在需要画一个三角形,因此只需要三个点。

  1. // An array of 3vectors which represents 3 vertices
  2. float positionData[]= {
  3. -0.8f, -0.8f, 0.0f,
  4. 0.8f, -0.8f, 0.0f,
  5. 0.0f, 0.8f, 0.0f };


然后创建一个缓冲区,并用上面的数据为其填充。

  1. // This will identifyour vertex buffer
  2. GLuint vertexbuffer;
  3. // Generate 1 buffer,put the resulting identifier in vertexbuffer
  4. glGenBuffers(1, &vertexbuffer);
  5. // The followingcommands will talk about our 'vertexbuffer' buffer
  6. glBindBuffer(GL_ARRAY_BUFFER,vertexbuffer);
  7. // Give our verticesto OpenGL.
  8. glBufferData(GL_ARRAY_BUFFER,sizeof(positionData), positionData, GL_STATIC_DRAW);


这样就完成的第一个顶点属性,VertexPosition数据的前期准备工作。VertexColor的数据是类似的。

  1. float colorData[] = {
  2. 1.0f, 0.0f, 0.0f,
  3. 0.0f, 1.0f, 0.0f,
  4. 0.0f, 0.0f, 1.0f };
  5. // This will identifyour vertex buffer
  6. GLuint colorbuffer;
  7. // Generate 1 buffer,put the resulting identifier in vertexbuffer
  8. glGenBuffers(1,&colorbuffer)
  9. // The followingcommands will talk about our 'vertexbuffer' buffer
  10. glBindBuffer(GL_ARRAY_BUFFER,colorbuffer);
  11. // Give our verticesto OpenGL.
  12. glBufferData(GL_ARRAY_BUFFER,sizeof(colorData), colorData, GL_STATIC_DRAW);


现在准备好了数据(现在存储在两个buffer里),我们就得让OpenGL程序知道所要传递的数据应该给谁。前面说过,OpenGL会为顶点属性创建一个0到GL_MAX_VERTEX_ATTRIBS – 1的索引,既然如此,我们首先要定义一下索引和Shader里变量的对应关系,就好比我们在写一个目录,我们必须告诉OpenGL,页码0对应的是什么内容,而页码1又对应着什么内容,这里的内容指的就是shader中以“in”为关键字的变量。定义了索引对应关系后,在绘制函数里,我们就可以根据索引来传递数据了。

这里,根据定义这种索引对应关系的途径,可以分为下面两种。


使用glBindAttribLocation


第一种方法是通过glBindAttribLocation函数来实现索引和变量之间的对应关系。

首先,我们为shader中的每个顶点属性变量指定一个索引(一般从0开始)。

  1. // Bind index 0 tothe shader input variable "VertexPosition"
  2. glBindAttribLocation(programHandle,0, "VertexPosition");
  3. // Bind index 1 tothe shader input variable "VertexColor"
  4. glBindAttribLocation(programHandle,1, "VertexColor");


上述代码规定,shader里名字为VertexPosition的变量对应顶点属性索引为0,VertexColor对应索引为1。如果使用这种方法,你需要确保在编译程序之后、链接程序之前就调用它们。


在Shader中直接指定


另一种方法则是在shader中直接指定,这是通过GLSL的关键词layout来是实现。为了实现这样的效果,我们需要更改之前的vertex shader的内容。如下:

  1. #version 400
  2. layout(location = 0)in vec3 VertexPosition;
  3. layout(location = 1)in vec3 VertexColor;
  4. out vec3 Color;
  5. void main()
  6. {
  7. Color = VertexColor;
  8. gl_Position = vec4(VertexPosition,1.0);
  9. }


类似这样的代码layout(location = 0)指定了顶点属性VertexPosition对应了索引值0.

 

现在所有的准备工作都做完了:我们既完成了缓冲区数据的填充,也完成了顶点属性索引和shader输入变量之间的对应关系,剩下的工作就是在绘制函数中告诉OpenGL,请使用xxx buffer内的数据来为索引为x的顶点属性赋值。

  1. // 1rst attributebuffer : vertices
  2. glEnableVertexAttribArray(0);
  3. glBindBuffer(GL_ARRAY_BUFFER,vertexbuffer);
  4. glVertexAttribPointer(
  5. 0, // attribute 0. No particularreason for 0, but must match the layout in the shader.
  6. 3, // size
  7. GL_FLOAT, // type
  8. GL_FALSE, // normalized?
  9. 0, // stride
  10. (void*)0 // array buffer offset
  11. );



第一行代码表示我们需要启用索引为0的顶点属性。然后第二行表明我们要使用vertexbuffer内的数据,下面的操作都是针对这个缓冲区的。第三行代码中函数的参数比较多,第一个参数指明要操作的属性索引值,第二个参数则说明每个顶点属性需要几个数据(可以为1,2,3或4),因为vertexbuffer里现在存储了3X3=9个数据,而实际上3个是一组,每个顶点需要使用3个数据。第三个参数指定了缓冲区内每个数据的类型,这里顶点坐标使用的是浮点类型。第四个参数表明数据是否需要normalized,归一化(对于有符号整数,归一化将使数据保持在[-1, 1]范围内,对于无符号整数,则在范围[0, 1])。第五个参数是步幅,指定了两个连续的顶点属性之间的偏移量(以字节为单位)。这里我们的数据是连续的,因此数值为0。最后一个参数看似是一个指针,但实际上它并不是起到指针的作用。实际上,它表明缓冲区的开头距离第一个顶点属性之间的偏移量。这里,缓冲区里第一个顶点属性之前并没有任何额外的信息,因为我们取值为0。

colors的传递工作是一样的。

  1. // 2cond attributebuffer : vertices
  2. glEnableVertexAttribArray(1);
  3. glBindBuffer(GL_ARRAY_BUFFER,colorbuffer);
  4. glVertexAttribPointer(
  5. 1, // attribute 1. No particularreason for 0, but must match the layout in the shader.
  6. 3, // size
  7. GL_FLOAT, // type
  8. GL_FALSE, // normalized?
  9. 0, // stride
  10. (void*)0 // array buffer offset
  11. );



哇哈哈,终于所有的工作都完成了,现在就可以告诉OpenGL真正开始绘制了!这一步非常简单只需要一个函数。(在这之前请确保已经绑定了我们最开始创建的VAO,如果没有,请调用glBindVertexArray(VertexArrayID))

glDrawArrays(GL_TRIANGLES,0, 3 );


这个函数表明,OpenGL将逐步访问每个顶点属性的缓冲区,然后把数据传递到OpenGL管线里交给vertexshader。第一个参数是渲染模式,这里表明我们将使用三角形进行绘制。第二个参数是指,在开启的属性中的第一个索引,这里我们开启了两个属性,分别为0和1,因此第一个索引为0。第三个参数是指渲染所需的索引数量,这里我们使用三角形模式,因此每个三角形需要3个顶点。每个顶点需要一个索引。

除了glDrawArrays,我们还可以使用glDrawElements进行绘制,这在之后的文章会讲到。

 

你可能注意到,对于fragment shader的输出,FragColor我们没有进行任何工作,它存储了每个像素最后输出的颜色值。这里我们先不用管它,我们只要知道,它默认将会传递给后台的颜色缓冲就可以了。

 

 

传递Uniform变量


顶点属性信息给我们提供了一个和shader交互的非常好的途径,而第二个途径是就使用shader的uniform变量。想比于上一种变量类型,uniform变量很少改变,通常模型矩阵、视角矩阵、透视矩阵等都是属于这个类型。而且,这类变量在shader里是只读的,我们只能在OpenGL程序里改变它们,再传递给shader。在同一个OpenGL程序中,同一个名字的uniform变量可以同时出现了多个shader程序中,但是它们的类型必须一样。也就是说,所有的uniform变量共享一个uniform空间。

我们依然使用上面的例子,但是在vertex shader里添加一行代码:

  1. #version 400
  2. layout (location = 0)in vec3 VertexPosition;
  3. layout (location = 1)in vec3 VertexColor;
  4. out vec3 Color;
  5. uniform mat4RotationMatrix;
  6. void main()
  7. {
  8. Color = VertexColor;
  9. gl_Position = RotationMatrix * vec4(VertexPosition,1.0);
  10. }



然后在OpenGL程序里,可以这样向shader传递:

  1. glClear(GL_COLOR_BUFFER_BIT);
  2. mat4 rotationMatrix =glm::rotate(mat4(1.0f), angle,
  3. vec3(0.0f,0.0f,1.0f));
  4. GLuint location=glGetUniformLocation(programHandle, "RotationMatrix");
  5. if( location >= 0)
  6. {
  7. glUniformMatrix4fv(location, 1, GL_FALSE,&rotationMatrix[0][0]);
  8. }


glGetUniformLocation函数得到名字为“RotationMatrix”在shader中的位置,然后再判断该变量是否存在(如果不存在,则会返回-1)。如果存在,在通过glUniformMatrix4fv函数向其传递数据。该函数的第一个参数是该变量在shader中的位置,第二个参数是被赋值的矩阵的数目(因为uniform变量可以是一个数组)。第三个参数表明在向uniform变量赋值时该矩阵是否需要转置。因为我们使用的是glm定义的矩阵,因此不要进行转置。但如果你正在使用一个数组来实现矩阵,并且这个矩阵是按行定义的,那么你就需要设置这个参数为GL_TRUE。最后一个参数就是传递给uniform变量的数据的指针了。

 

当然,uniform变量可以是任意合法的GLSL类型的变量,包括自定义的复杂数据类型。OpenGL为每个类型提供了具有它们特征的后缀的glUniform函数,例如,为了给一个vec3类型的变量赋值,我们可以使用glUniform3f或者glUniform3fv。

对于数组类型变量,我们使用具有后缀“v”的函数进行赋值(像我们的例子一样)。除此之外,我们还可以使用[ ] 操作符访问特定的位置的变量。例如为了访问MyArray数组的第二个变量,我们可以这样做:

GLuint location =glGetUniformLocation( programHandle, "MyArray[1]" );


对于结构体,我们必须为它的成员变量逐一赋值。和一个数组类似,我们可以这样访问结构体的成员变量:

GLuint location =glGetUniformLocation( programHandle, "MyMatrices.Rotation" );


使用Uniform blocks和Uniform buffer objects


如果你的程序涉及了多个shader程序,而且它们使用同一个uniform变量,那么你就需要为每个shader程序单独管理它们。当一个程序被链接时,OpenGL会自动生成uniform的位置,因此对于不同的shader程序,同一个uniform变量的位置可能是不一样的。而uniform blocks就是为了方便我们管理shader之间共享的uniform变量。

 

通过一个uniform block对象,我们可以创建一个缓冲区用于存储所有的uniform变量,然后把这个缓冲区绑定到uniform block上。这样,当我们需要改变使用的shader程序时,只需要再重新把uniform block绑定到新的shader程序就可以了。

例如,下面就声明了一个包四个uniform变量的uniform block对象:

  1. uniform BlobSettings{
  2. vec4 InnerColor;
  3. vec4 OuterColor;
  4. float RadiusInner;
  5. float RadiusOuter;
  6. };


通过这种类型的block定义,包含的四个uniform变量同样是存在在全局空间中的,而不需要指定block变量名。而存储uniform block的缓冲区被称为uniform buffer objects。它们和我们之前遇到的顶点属性缓冲区一样,仅仅是绑定到了一段特定的位置。

 

为了示例uniform blocks和uniform buffer objects的用法,我们现在将试图完成下面的工作:利用贴图坐标绘制一个正方形(也就是两个三角形),然后利用fragment shader在正方形中央绘制一个圆形,这个圆形内部是实心的,但边缘将逐渐模糊。如下图所示:

我们使用顶点属性索引为0的属性对应顶点信息,索引为1的属性对应纹理坐标信息。vertex shader如下所示:

  1. #version 400
  2. layout (location = 0)in vec3 VertexPosition;
  3. layout (location = 1)in vec3 VertexTexCoord;
  4. out vec3 TexCoord;
  5. void main()
  6. {
  7. TexCoord = VertexTexCoord;
  8. gl_Position = vec4(VertexPosition,1.0);
  9. }
 

fragment shader如下:

  1. #version 400
  2. in vec3 TexCoord;
  3. layout (location = 0)out vec4 FragColor;
  4. uniform BlobSettings{
  5. vec4 InnerColor;
  6. vec4 OuterColor;
  7. float RadiusInner;
  8. float RadiusOuter;
  9. };
  10. void main() {
  11. float dx = TexCoord.x - 0.5;
  12. float dy = TexCoord.y - 0.5;
  13. float dist = sqrt(dx * dx + dy * dy);
  14. FragColor = mix( InnerColor, OuterColor,smoothstep( RadiusInner, RadiusOuter, dist));
  15. }

 

名为BlobSetting的uniform block对象定义了我们的模糊圆形所需要的所有参数:实心部分的颜色,外部的颜色,内部实心圆的半径,外部圆的半径。在main函数里,计算了圆形的颜色信息。首先根据纹理坐标计算距离中心(0.5, 0.5)的距离,然后使用smoothstep函数得到颜色信息,如果dist小于RadiusInner,那么将返回0,如果大于RadiuOuter那么返回1,否则将返回(0.0,1.0)之间的值。mix函数将根据smoothstep的返回值,使用InnerColor和OuterColor进行线性插值,以实现渐变的效果。

 

下面重头戏开始!在OpenGL程序里,我们需要怎么为它们赋值呢?

 

首先要得到uniform block变量的索引:

GLuint blockIndex =glGetUniformBlockIndex(programHandle, "BlobSettings");


然后为存储uniform block的缓冲区的数据分配空间,用来存储这些数据。当然,在这之前我们需要知道block空间的大小。我们可以使用glGetActiveUniformBlockiv函数来得到。

  1. GLint blockSize;
  2. glGetActiveUniformBlockiv(programHandle,blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
  3. GLubyte *blockBuffer= (GLubyte *) malloc(blockSize);

 

之后,查询block内每个变量的偏移量(这是为了根据偏移量来填充buffer)。但首先,我们需要得到每个变量的索引。

  1. // Query for theoffsets of each block variable
  2. const GLchar *names[]= { "InnerColor", "OuterColor", "RadiusInner","RadiusOuter" };
  3. GLuint indices[4];
  4. glGetUniformIndices(programHandle,4, names, indices);
  5. GLint offset[4];
  6. glGetActiveUniformsiv(programHandle,4, indices, GL_UNIFORM_OFFSET,offset);


glGetUniformIndices函数接受一个变量名字的数组(第三个参数),然后返回这些变量在数据里的索引(第四个参数)。然后我们使用这些索引,通过调用glGetActiveUniformsiv函数来得到不同变量的偏移量。


然后,在合适的偏移位置为buffer填充数据。

  1. GLfloat outerColor[]= {0.0f, 0.0f, 0.0f, 0.0f};
  2. GLfloat innerColor[]= {1.0f, 1.0f, 0.75f, 1.0f};
  3. GLfloat innerRadius =0.25f, outerRadius = 0.45f;
  4. memcpy(blockBuffer +offset[0], innerColor, 4 * sizeof(GLfloat));
  5. memcpy(blockBuffer +offset[1], outerColor, 4 * sizeof(GLfloat));
  6. memcpy(blockBuffer +offset[2], &innerRadius, sizeof(GLfloat));
  7. memcpy(blockBuffer +offset[3], &outerRadius, sizeof(GLfloat));


真正创建一个buffer,把之前在内存中存数的数据复制给它。

  1. GLuint uboHandle;
  2. glGenBuffers( 1,&uboHandle );
  3. glBindBuffer(GL_UNIFORM_BUFFER, uboHandle );
  4. glBufferData(GL_UNIFORM_BUFFER, blockSize, blockBuffer, GL_DYNAMIC_DRAW );


注意到在最后一行,我们使用GL_DYNAMIC_DRAW作为第三个参数,这是因为在渲染过程中,uniform变量通常是变化的。当然,这也完全视情况而定。


最后,把buffer绑定到uniform block上。

glBindBufferBase(GL_UNIFORM_BUFFER, blockIndex, uboHandle );


如果在之后你需要改变uniform block的数据,你可以调用glBufferSubData函数。在这之前,你必须要先绑定当前的buffer到GL_UNIFORM_BUFFER图形绑定点(通过调用glBindBuffer(GL_UNIFORM_BUFFER, uboHandle))。

为一个Uniform block使用实例名

 

一个uniform block可以有一个实例名。例如我们可以为我们的BlobSettings block使用一个实例名Blob:

  1. uniform BlobSettings{
  2. vec4 InnerColor;
  3. vec4 OuterColor;
  4. float RadiusInner;
  5. float RadiusOuter;
  6. } Blob;


这样,uniform block里的变量就会移动到Blob命名空间下,因此,我们在fragment shader的main函数里,需要这样访问它们:

FragColor = mix(Blob.InnerColor, Blob.OuterColor, smoothstep( Blob.RadiusInner,Blob.RadiusOuter, dist ));


当然,在OpenGL代码里你也需要做出相应的改变。这里,有的时候需要你使用uniform block名 BlobSetting来进行访问,但有时却需要使用实例名Blob访问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432346.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常用正则表达式大全

匹配中文字符的正则表达式: [u4e00-u9fa5]   评注:匹配中文还真是个头疼的事,有了这个表达式就好办了   匹配双字节字符(包括汉字在内):[^x00-xff]   评注:可以用来计算字符串的长度(一个双字节字符…

linux+qt+定时精度,Qt QTimer测试定时精度

调试信息输出窗口可以查看超时误差dialog.h文件#ifndef DIALOG_H#define DIALOG_H#include #include namespace Ui {class Dialog;}class QTimer;class Dialog : public QDialog{Q_OBJECTpublic:explicit Dialog(QWidget *parent 0);~Dialog();public slots:void slotOneSec()…

初始JavaScript详解【精选】

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍初始JavaScript以及部分理论知识 🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁 🍉博主收将持续更新学习记录获,友友们有任何问题可以在评论区留言 目录 ⭐…

最简单的基于FFmpeg的AVDevice例子(屏幕录制)

最简单的基于FFmpeg的AVDevice例子文章列表:最简单的基于FFmpeg的AVDevice例子(读取摄像头)最简单的基于FFmpeg的AVDevice例子(屏幕录制)FFmpeg中有一个和多媒体设备交互的类库:Li…

linux 挂载32T文件系统,linux Centos下磁盘分区及文件系统创建与挂载(示例代码)

linux Centos下磁盘分区及文件系统创建与挂载MBR(Master Boot Record)是传统的分区机制,应用于绝大多数使用BIOS的PC设备。1.MBR支持32bit和64bit系统2.MBR支持分区数量有限3.MBR只支持不超过2T的硬盘,超过2T的硬盘只能使用2T空间(使用其他方法)1.主分区…

C语言的数组初始化

http://blog.csdn.net/sibylle/article/details/2026915 一直以为 int a[256]{0};是把a的所有元素初始化为0,int a[256]{1};是把a所有的元素初始化为1.调试的时查看内存发现不是那么一回事.5.2.1 数组初始化 数组可以用一个列值来初始化,例如 …

【OpenGL】GLSL中的函数和子程序(subroutines)

这篇文章里讲一下在GLSL如何使用函数和子程序(subroutines)。在GLSL中使用函数 GLSL支持函数,它们的语法结构和C很相似。但是调用约定会有所不同。下面,我们以一个普通的ADS(ambient,diffuse,sp…

linux7 共享盘创建,使用CentOS7建立samba文件共享服务器

1、安装samba检查samba是否已经安装[rootbak1 ~]# rpm -qa | grep samba没有任何回显则表示未安装软件包 sambayum安装samba[rootbak1 ~]# yum -y install samba samba-client samba-common再次检查安装结果[rootbak1 var]# rpm -qa | grep sambasamba-common-libs-4.6.2-12.el…

批量造数据的一种方法

实现目的:一张表Table中有A、B、C三个字段,并有很多值,开发测试需要,可以执行这样的语句批量复制插入数据: insert into Table(A,B,C)select A,B,C from Table 转载于:https://www.cnblogs.com/tianqy/p/3965292.html

【Modern OpenGL】第一个三角形

>说明:跟着learnopengl的内容学习,不是纯翻译,只是自己整理记录。>强烈推荐原文,无论是内容还是排版。 [原文链接](http://learnopengl.com/#!Getting-started/OpenGL)本文地址:http://blog.csdn.net/aganlengzi…

13c语言中的文件是一种流式文件,读写时均以字符为单位.,C语言判断题部分.doc...

C语言判断题部分?第一章C语言中,字符数据用格式说明"%c"来输出,字符串数据用格式说明"%s"来输出。TC语言中,关键字及编译预处理命令用小写字母书写。T二元运算符均可以和赋值运算符构成复合的赋值运算符。TC语言中&…

UI Automation 简介

转载,源地址: http://blog.csdn.net/ffeiffei/article/details/6637418 MS UI Automation(Microsoft User Interface Automation:UIA)是随.net framework3.0一起发布的,虽然在如今这个几乎每天都有各种新名…

【OpenGL4.0】GLSL渲染语言入门与VBO、VAO使用:绘制一个三角形

以前都是用Cg的,现在改用GLSL,又要重新学,不过两种语言很多都是相通的。下面的例子是实现绘制一个三角形的简单程序。采用了VBO(veretx buffer object)、VAO(vertex array object)等OpenGL的一些…

catia linux下载64位,CATIA V5 CATSysDemon.exe缓冲区溢出漏洞

发布日期:2014-02-24更新日期:2014-02-25受影响系统:3ds catia-v5描述:--------------------------------------------------------------------------------CATIA是数字产品定义及生命周期管理使用的CAD、CAE、CAM应用集成软件包…

eclipse 中文乱码

eclipse 中页面中中文正常预览出现中文乱码 解决方法: windows->preferences->general->content types->text(展开)选择页面我的是php页面就选择phpcontent type 在窗口下面default encoding 中输入gbk 。点击update 就可以了转载于:https://www.cnblog…

【Modern OpenGL】Shader

Shaders 正如在上一篇教程中提到的,shader是在GPU中运行的小程序。如上一个教程中实现的最简单的vertex shader和fragment shader,一个shader基本上负责图形渲染流水线中的一个阶段的功能。从根本上来说,shader就是将输入转化成输出的操作。而…

c语言编译的手机软件,C语言编译工具

软件介绍C语言编译工具app是一款功能十分强大的手机编译、更换代码的软件工具,C语言编译工具app可以快速进行代码翻译、代码查找、替换等,它支持多种代码语言,欢迎下载!C语言编译工具app内容简单方便功能强大的代码编译器&#xf…

2014.9.13模拟赛【数位和乘积】

数位和乘积(digit.cpp/c/pas) 【题目描述】 一个数字的数位和乘积为其各位数字的乘积。求所有的N位数中有多少个数的数位和乘积恰好为K。请注意,这里的N位数是可以有前导零的。比如01,02视为二位数,但是他们的数位和乘积都是0。 【输入格式】…

c语言中vector函数大全,vector :: empty()函数,以及C ++ STL中的示例

C vector :: empty()函数矢量::空()是一个库函数“载体”头,它是用来检查给定的矢量是否是一个空的载体或没有,它返回一个真,如果矢量大小为0,否则返回假。注意:要使用向量,请包含标头。vector :: empty()…

【Modern OpenGL】OpenGL简介

说明:跟着learnopengl的内容学习,不是纯翻译,只是自己整理记录。 强烈推荐原文,无论是内容还是排版。 原文链接 本文地址: http://blog.csdn.net/aganlengzi/article/details/50354162 OpenGL 在真正开始我们的探索之旅时&…