Использование шейдеров в программе на OpenGL.

Для поддержки шейдеров в программе на С++ и OpenGL сначала необходимо загрузить из Интернет нужные файлы библиотеки, по адресу http://glew.sourceforge.net/. В результате загрузки имеется архив, к примеру с таким именем: glew-2.0.0-win32.zip. Его нужно разархивировать в отдельную папку. В этой папке, из подпапки /bin файл glew32.dll следует положить в папку C:\Windows\System32\. Файлы из папок /include и /lib следует положить в такие же папки для заголовков и библиотек в папку с Visual Studio. Вы можете положить файлы из этих папок куда угодно, но только нужно указать в настройках среде Visual Studio где искать эти файлы. В общем случае файлы заголовков и библиотек находятся по пути C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK. Дальше в программе С++ в добавок к другим заголовочным файлам и библиотекам нужно подключить такие файлы и инициализировать библиотеку glew. Последовательность действий для работы с шейдерами в программе на OpenGL выглядит так: 1 – Пункт 1 показывает действия необходимые в функции инициализации приложения; 1.1 –Инициализируем библиотеку glew; 1.2 – Определяем переменные к которым привязываем буфер вершин, цветов, текстурных координат, буфер индексов. 1.3 – Полученные переменные, которые отвечают за буферы, привязываем в внутренним переменным в шедерах; 1.4 – Загружаем из текстового файла вершинный и пиксельный шейдеры, компилируем их; 1.5 – Создаем объект Программа; привязываем переменные шейдеров к объекту Программа; привязываем к нашему приложению сам объект программа и ипользуем его; 2 – Пункт 2 показывает действия необходимые в функции рендеринга приложения; 2.1 –Передаем в шейдерв данные, например матрицы вида, и проекции, вызываем функцию рисования, и отображения контекста.


Код примера на С++.


//подключаем заголовочные файлы библиотеки glew

#include <gl/glew.h>
#pragma comment (lib, "glew32.lib")

//создаем переменную для объекта программа, и переменные
//для буфера вершин, текстурных координат, буфера цветов
//и для буфера индексов

GLuint programHandle;

GLuint vboVertex1;
GLuint vboTexCoord;
GLuint vboColor;
GLuint vboIndices;


//функция инициализации

void CMeshManager::InitMeshManager()
{

//инициализируем библиотеку glew

GLenum err = glewInit();
If(GLEW_OK != err)
{
printf(“Error initializing GLEW: %s\n”,
glewGetErrorString(err));
}


//генерируем буфер на основе переменной vboVertex1
//и связываем с ними буфер вершин pVertsArray
//указываем размер данных, в примере 726 точек формата xyz float

glGenBuffers(1, &vboVertex1);
glBindBuffer(GL_ARRAY_BUFFER, vboVertex1);
glBufferData(GL_ARRAY_BUFFER, 726 * 3 * sizeof(float), pVertsArray, GL_STATIC_DRAW);

//генерируем буфер на основе переменной vboTexCoord
//и связываем с ними буфер вершин pTexCoordArray
//указываем размер данных, в примере 726 точек формата xy float

glGenBuffers(1, &vboTexCoord);
glBindBuffer(GL_ARRAY_BUFFER, vboTexCoord);
glBufferData(GL_ARRAY_BUFFER, 726 * 2 * sizeof(float), pTexCoordArray, GL_STATIC_DRAW);

//генерируем буфер на основе переменной vboColor
//и связываем с ними буфер вершин pColorArray
//указываем размер данных, в примере 726 точек формата rgb float

glGenBuffers(1, &vboColor);
glBindBuffer(GL_ARRAY_BUFFER, vboColor);
glBufferData(GL_ARRAY_BUFFER, 726 * 3 * sizeof(float), pColorArray, GL_STATIC_DRAW);


//генерируем буфер на основе переменной vboIndices
//и связываем с ними буфер вершин pIndicesArray
//указываем размер данных, в примере 1200 полигонов (треугольников) у каждого 3 вершины

int n_num_indx = 1200 * 3;
glGenBuffers(1, &vboIndices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndices);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 1200 * 3 * sizeof(unsigned int) , pIndicesArray, GL_STATIC_DRAW);




//теперь когда переменные связали с буферами данных,
//передаем эти переменные в шейдер, передаем в шейдер
//буфер индексов

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndices);

//передаем в шейдер буфер вершин с индексом 0

glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vboVertex1);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

//передаем в шейдер буфер текстурных координат, с индексом 1

glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, vboTexCoord);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

//передаем в шейдер буфер цветов, с индексом 2

glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, vboColor);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);




//создаем переменную вершинного шейдера, загружаем из текстового файла
//программу шейдера и компилируем шейдер, делая проверку на ошибки

GLuint vertShader = glCreateShader( GL_VERTEX_SHADER );
if ( 0 == vertShader )
{
MessageBox(NULL, "Error creating vertex shader", "Info", MB_OK);
}

const GLchar* shaderCode = loadShaderAsStrig (".\\Shader\\basic_uniform.vert");
const GLchar* codeArray[] = {shaderCode};
glShaderSource (vertShader, 1, codeArray, NULL);

glCompileShader(vertShader);

GLint result;
glGetShaderiv( vertShader, GL_COMPILE_STATUS, &result);
if(GL_FALSE == result)
{
MessageBox(NULL, "Vert shader compilation failed", "Info", MB_OK);

GLint loglen;
glGetShaderiv(vertShader, GL_INFO_LOG_LENGTH, &loglen);
if(loglen > 0)
{
char * log = (char *) malloc(loglen);
GLsizei written;
glGetShaderInfoLog(vertShader, loglen, &written, log);
char szBuff[1024];
sprintf_s(szBuff, 1024, "Shader log:\n%s", log);
MessageBox(NULL, szBuff, "Info", MB_OK);
}
}


//создаем переменную пиксельного шейдера, загружаем из текстового файла
//программу шейдера и компилируем шейдер, делая проверку на ошибки

GLuint fragShader = glCreateShader( GL_FRAGMENT_SHADER );
if ( 0 == fragShader )
{
MessageBox(NULL, "Error creating frag shader", "Info", MB_OK);
}

const GLchar* shaderCode_f = loadShaderAsStrig (".\\Shader\\basic_uniform.frag");
const GLchar* codeArray_f[] = {shaderCode_f};
glShaderSource (fragShader, 1, codeArray_f, NULL);

glCompileShader(fragShader);

//GLint result;
glGetShaderiv( fragShader, GL_COMPILE_STATUS, &result);
if(GL_FALSE == result)
{
MessageBox(NULL, "Frag shader compilation failed", "Info", MB_OK);

GLint loglen;
glGetShaderiv(fragShader, GL_INFO_LOG_LENGTH, &loglen);
if(loglen > 0)
{
char * log = (char *) malloc(loglen);
GLsizei written;
glGetShaderInfoLog(fragShader, loglen, &written, log);
char szBuff[1024];
sprintf_s(szBuff, 1024, "Shader log:\n%s", log);
MessageBox(NULL, szBuff, "Info", MB_OK);
}
}




//создаем объект Программа

programHandle = glCreateProgram();
if( 0 == programHandle)
{
MessageBox(NULL, "Error creating programm object", "Info", MB_OK);
}

//к объекту Программа привязываем вершинный и пиксельный шейдер

glAttachShader(programHandle, vertShader);
glAttachShader(programHandle, fragShader);

//привязываем сам объект Программа

glLinkProgram(programHandle);

//устанавливаем объект Программа в приложении

glUseProgram(programHandle);

}



//функция берет текстовый файл с программой шейдера и загружает его

GLchar *CMeshManager::loadShaderAsStrig(char *fn)
{
FILE *fp;
char *content = NULL;

int count=0;

if (fn != NULL) {
//fp = fopen(fn,"rt");
fopen_s(&fp, fn,"rt");

if (fp != NULL) {

fseek(fp, 0, SEEK_END);
count = ftell(fp);
rewind(fp);

if (count > 0) {
content = (char *)malloc(sizeof(char) * (count+1));
count =(int) fread(content,sizeof(char),count,fp);
content[count] = '\0';
}
fclose(fp);
}
}
return content;
}



//функция рендеринга в приложении

void CMeshManager::DrawMesh()
{

//очищаем экран и буфер глубины

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);Buffer
glLoadIdentity();


//получаем значения матриц вида и проекции что бы передать их в шейдер

GLfloat mvmatrix[16], projmatrix[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mvmatrix);
glGetFloatv(GL_PROJECTION_MATRIX, projmatrix);

//передаем в шейдер матрицу вида

GLuint mViewMatrix = glGetUniformLocation(programHandle, "ViewMatrix");
if( mViewMatrix >= 0 )
{
glUniformMatrix4fv(mViewMatrix, 1, GL_FALSE, (const GLfloat*)mvmatrix);
}

//передаем в шейдер матрицу проекции

GLuint mProjMatrix = glGetUniformLocation(programHandle, "ProjMatrix");
if( mProjMatrix >= 0 )
{
glUniformMatrix4fv(mProjMatrix, 1, GL_FALSE, (const GLfloat*)projmatrix);
}


//рисуем модель

glDrawElements(GL_TRIANGLES,n_num_indx,GL_UNSIGNED_INT, 0);

//делаем отображение на экране (переключаем буферы)

SwapBuffers(hDC);

}