因为之前在用c++写ray tracing(在CPU上跑),后来因为在CPU上跑太慢了,于是又上cuda,但是用cuda实现完成到30%,各种不能理解的错误,我这个cuda新手也是被折腾的惨,代码调试也很苦难。于是打算后面用compute shader来实现ray tracing。所以最近在狂学opengl和glsl。目前是glsl新手,希望各位多多指教。
不得不承认opengl的API设计比较复杂,至少我是这么感觉,花了一些时间来理它的脉络。
一定要清楚的一点是,在opengl中,可编程渲染管线的编程结构和固定渲染管线方式下的code结构其实差别蛮大的。
假设我们要使用glsl,也就是要使用可编程渲染管线来实现如下效果:
使用可编程渲染管线的coding方式,应该怎么做呢?
首先,我们要知道,可编程,主要是指,我们可以编写自己的顶点着色器和片段着色器,而这两个着色器是分别对应着渲染管线中的两个阶段,我们现在可以操纵这两个阶段,所以叫做可编程渲染管线。OK。
顶点着色器里面可以定义很多的顶点属性,比如顶点的坐标、颜色、法线等等,这些属性下的数据叫做vertex attribute data.这个很重要。和vertex attribute data挂钩的又出现了传说中的VAO、VBO。
哇,要理解好多东西!确实如此!
这两个又是什么鬼??-_-....
VAO:vertex array object.
VBO:vertex buffer object.
这两个有啥区别?分别是干嘛的?
首先要明确的是,VAO,VBO都和vertex shader有关系,你如果要在opengl中使用glsl来实现你想要的效果,那么你必定会接触到这两个东西,也就是说,你要使用和VAO,VBO相关的函数。而在固定渲染管线中不会用到这些函数,这里就是一个界限!这一点很重要。
要在opengl中使用glsl来画图,需要这样来组织代码(理解并记住这个结构很重要):
initialization:
for each batch:
generate,store,and bind a VAO,
bind all the buffers needed for a draw call.
unbind the VAO.
main loop/whenever you render:
for each batch
bind VAO
glDrawArrays(...);or gl DrawElements(...);
unbind VAO.
问:VAO、VBO和shader的关系是啥?
答:VAO stores data about vertex attribute locations。很好理解,VAO保存的是数据的location。当你调用glVertexAttribPointer(...)的时候,VAO就记住了当前VBO绑定的是啥东西,当在执行渲染的时候,VAO就会从VBO中获取 vertex attribute data。即便当前VBO没被绑定!
其实,当我们在调用glUseProgram()这个函数的时候,就表明告诉opengl,我现在已经从固定渲染管线切换到可编程渲染管线了,我要用glsl来搞事情。不能同时既搞fixed又搞programable。
还要注意的是,一次只能将一个target绑定到VAO!你要使用谁,你就绑定谁,把它调到前台来drawarray。
关于VBO。
VBO是一个buffer object。它作为vertex attribute data的数据来源。我们可以通过以下方法来让一个VBO做为vertex attribute data的数据源:
首先,一定要将这个VBO绑定到GL_ARRAY_BUFFER上,然后,调用glVertexAttribPointer(index,....)(如果对这些函数不理解,可以看我之前的文章),调用这个函数,就指定,vertex shader中layout 中index索引处的data的数据来自绑定到GL_ARRAY_BUFFER上的VBO。当这个函数被调用的时候,这个关系就被确定下来了.。假设在这句函数后面再调用glBindBuffer(0),这时是不是我们的数据源就被切断了呢(因为之前是glBindBuffer(VBO),现在这个VBO变成0了)?不是得,源头还是不变!要改变这个,必须要调用glVertexAttribPointer(index,....),理解这个很重要。
VAO和VBO的区别:VAO stores all of the state needed to supply data.VBO才是真正的存储着数据。
好了,按照上面结构,我们用glsl实现上图的效果,代码如下:
void InitGL(int argc,char* argv[])
{
glutInit(&argc,argv);
glEnable(GL_DEBUG_OUTPUT);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE ); //设置显示方式,RGB、双缓冲
glutInitWindowPosition(100,100); //位置
glutInitWindowSize(WIN_WIDTH,WIN_HEIGHT);//窗口大小
glutCreateWindow("GLSL FUN!"); //创建窗口,设置标题
glutDisplayFunc(renderScene); // 当绘制窗口时调用myDisplay
glutIdleFunc(renderScene);
glutReshapeFunc(changeSize);
glutKeyboardFunc(processNormalKeys);
glClearColor(0.0,0.0,0.0,0.0);//清除颜色设置为白色,这样glclear后面clear color就用白色clear
glewInit();
projection = glm::perspective( glm::radians(50.0f),(float)1024/1024,1.0f,100.0f);
}
void InitPosArrayandColorArray()
{
float dx = 1.0f/(float)WIN_WIDTH;
float dy = 1.0f/(float)WIN_HEIGHT;
int index=0;
for(int i = 0; i < WIN_WIDTH; i++)
for(int j = 0; j < WIN_HEIGHT; j++)
{
float screenPosX = (float)i * dx * 2 - 1;
float screenPosY = (float)j * dy * 2 - 1;
int add1 = index + 1;
int add2 = index + 2;
positionData[ index ] = screenPosX;
positionData[ add1 ] = screenPosY;
positionData[ add2 ] = .0f;
colorData[ index ] = screenPosX;
colorData[ add1 ] = screenPosY;
colorData[ add2 ] = .2f;
index+=3;
}
}
}
void InitBuffer()
{
GLuint vboHandles[2];
glGenBuffers(2,vboHandles);
GLuint positionBufferHandle = vboHandles[0];
GLuint colorBufferHandle = vboHandles[1];
//
glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
glBufferData(GL_ARRAY_BUFFER, TOTAL_POINTS_NUM * sizeof(float), positionData, GL_STATIC_DRAW);
//
glBindBuffer(GL_ARRAY_BUFFER,colorBufferHandle);
glBufferData(GL_ARRAY_BUFFER, TOTAL_POINTS_NUM * sizeof(float), colorData, GL_STATIC_DRAW);
//VAO
glGenVertexArrays(1,&vaoHandle);
glBindVertexArray(vaoHandle);
//
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//
glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(GLubyte*)NULL);
glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(GLubyte*)NULL);
//
glBindVertexArray(0);
}
void renderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vaoHandle);
glDrawArrays(GL_POINTS,0,TOTAL_POINTS_NUM);
glBindVertexArray(0);
glutSwapBuffers();
}
int main(int argc,char* argv[])
{
//1.
InitGL(argc,argv);
InitPosArrayandColorArray();
//2.steshader其实也属于initScene的一部分,只是下个阶段而已
setShaders();
//after linking,use the following steps to assign data to uniform block in the frag shader
// InitUniformBlockBuffer();
InitBuffer();
// LogGLVersion();
//4.
glutMainLoop(); //消息循环
//
//check gl version
return 0;
}
虽然效果很简单,但是理解材才是最重要的。
下面是着色器的代码:
顶点着色器:
#version 430
layout (location=0) in vec3 VertexPosiotion;
layout (location=1) in vec3 VertexColor;
out vec3 Color;
void main()
{
Color = VertexColor;
gl_Position = vec4(VertexPosiotion,1.0);
}
片单着色器:
#version 430
in vec3 Color;
layout (location=0) out vec4 FragColor;
void main()
{
gl_FragColor = vec4(Color,1.0);
}
如果是使用固定管线的话,就很简单了,伪代码如下:
glPointSize(1.0f);
glBegin(GL_POINTS);
for(i=0; i < winwidth;i++)
for(int j = 0; j < winheight ; j++)
{
glcolor(....);
glvertex3f(x,y,0);
}
glend();