给图形上色
对于图形的上色有两种基本的方法:
直接使用glColor~() 系列的函数 和
使用shader脚本。下文将简要介绍下这两种方法。
1.直接使用glColor~() 系列的函数
方法:在相应的坐标顶点使用该函数,如下程序所示
<span style="white-space:pre"> </span>glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f,0.0f);//set the red
glVertex3f(-1.0f,-1.0f,0.0f);
glColor3f(0.0f,1.0f,0.0f);//set the green
glVertex3f(1.0f,-1.0f,0.0f);
glColor3f(0.0f,0.0f,1.0f);//set the blue
glVertex3f(0.0f,1.0f,0.0f);
glEnd();
需要说明的是,每一个glColor~() 函数都是从开始使用处生效,在出现另一个glColor~() 函数时失效
调试运行结果:如图1所示
图1
小结:
这种方法在程序理解和写法上比较简单,但传输效率比较低;在多顶点的情况下,程序比较冗长;
2.使用shader脚本
方法:通过将颜色写在frangment(片元)脚本上,来实现图形着色
首先,编写读取shader脚本的程序。该程序包括如下:
(1)利用glCreateShader() 函数创建 vertex shader (顶点着色器)和 fragment shader(片元着色器)
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
(2)利用getline() 函数读取 vertex shader 和 fragment shader
<span style="white-space:pre"> </span>std::string VertexShaderCode;
std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
if(VertexShaderStream.is_open())
{
std::string Line = "";
while(getline(VertexShaderStream, Line))
VertexShaderCode += "\n" + Line;
VertexShaderStream.close();
}
else//Open vertex shader source code failed
{
printf("Failed to open %s. Are you in the right directory ? \n", vertex_file_path);
return ;
}
std::string FragmentShaderCode;
std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
if(FragmentShaderStream.is_open())
{
std::string Line = "";
while(getline(FragmentShaderStream, Line))
FragmentShaderCode += "\n" + Line;
FragmentShaderStream.close();
}
else
{
printf("Failed to open %s. Are you in the right directory ? \n", fragment_file_path);
return ;
}
<span style="white-space:pre"> </span>GLint Result = GL_FALSE;
int InfoLogLength;
printf("Compiling shader : %s\n", vertex_file_path);
char const * VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
glCompileShader(VertexShaderID);
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 )
{
std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
printf("%s\n", &VertexShaderErrorMessage[0]);
}
(3-2)同样,使用glCompileShader() 函数编译fragment shader,并根据其日志判断是否存在代码语法错误
<span style="white-space:pre"> </span>printf("Compiling shader : %s\n", fragment_file_path);
char const * FragmentSourcePointer = FragmentShaderCode.c_str();
glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
glCompileShader(FragmentShaderID);
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 )
{
std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
printf("%s\n", &FragmentShaderErrorMessage[0]);
}
<span style="white-space:pre"> </span>printf("Linking program\n");
m_uiProgramID = glCreateProgram();
glAttachShader(m_uiProgramID, VertexShaderID);
glAttachShader(m_uiProgramID, FragmentShaderID);
glLinkProgram(m_uiProgramID);
(5)再次检查脚本,并释放相关shader
<span style="white-space:pre"> </span>glGetProgramiv(m_uiProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(m_uiProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 )
{
std::vector<char> ProgramErrorMessage(InfoLogLength+1);
glGetProgramInfoLog(m_uiProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
printf("%s\n", &ProgramErrorMessage[0]);
}
glDetachShader(m_uiProgramID, VertexShaderID);
glDetachShader(m_uiProgramID, FragmentShaderID);
//Delete the vertex and fragment shader
glDeleteShader(VertexShaderID);
glDeleteShader(FragmentShaderID);
(1)关于vertex shader:基本包括,OpenGL版本声、定义存储顶点变量、传输顶点数据
#version 330 core
layout(location = 0) in vec3 vertexPosition_modelspace;
void main(){
gl_Position = vec4(vertexPosition_modelspace,1);
}
(2)关于fragment shader:基本包括,OpenGL版本声明、定义存储颜色变量、设定颜色数据
#version 330 core
out vec3 color;
void main()
{
color = vec3(1,0,0);
}
glUseProgram(m_uiProgramID);
运行调试结果:如图2所示
图2
小结:
使用shader来进行着色,充分地利用GPU进行上色,极大地提高了编码的灵活性;但是在程序的理解上比较难,写法上也比较繁琐;需要说的是,对于读取shader的程序只要在一组shader(包括vertex shader 和 fragment shader)中,只要调用一次。