A primeiro coisa a acrescentar é uma maneira mais conveniente de carregar os shader: Seria muito mais fácil para nós carregarmos um arquivo externo( ao invés de copiarmos e colocarmos a String em C em nosso código). Além disso, eles nós permitira modificar nosso código GLSL sem precisar recompilar o código C!
Primeiro, nos adicionaremos uma função para carregar um string.
é basicamente um código em C, que lera os conteúdos e alocando em buffer de tamanho no arquivo.
/** * Gravando todo o conteúdo do arquivo na memória, e passando tudo para o shaders * código fonte para OpenGL *//* Problemas: * Nós deveríamos fechar o arquivo de entrada depois do return NULL; mas estas declarações levaria ao monte de repetições * Você pode resolver usar goto ou abusar de switch/for/while + break ou fazer alguns if else bagunçados. * Melhor solução: vamos usar o identificar File: char* file_read(const FILE* input)*/char*file_read(constchar*filename){FILE*input=fopen(filename,"rb");if(input==NULL)returnNULL;if(fseek(input,0,SEEK_END)==-1)returnNULL;longsize=ftell(input);if(size==-1)returnNULL;if(fseek(input,0,SEEK_SET)==-1)returnNULL;/* Se o compilador do C: não lançar o valor de retorno do malloc´s*/char*content=(char*)malloc((size_t)size+1);if(content==NULL)returnNULL;fread(content,1,(size_t)size,input);if(ferror(input)){free(content);returnNULL;}fclose(input);content[size]='\0';returncontent;}
Poderá acontecer alguns erros com nossos shader, o programa pode parar sem nenhuma explicação com alguns erros particulares.
Então nós poderemos obter mais informações do OpenGL criando um infolog:
/** * Mostrando na tela os erros de compilação do compilador de Shader do OpenGL */voidprint_log(GLuintobject){GLintlog_length=0;if(glIsShader(object))glGetShaderiv(object,GL_INFO_LOG_LENGTH,&log_length);elseif(glIsProgram(object))glGetProgramiv(object,GL_INFO_LOG_LENGTH,&log_length);else{fprintf(stderr,"printlog: Não é um shader ou programa\n");return;}char*log=(char*)malloc(log_length);if(glIsShader(object))glGetShaderInfoLog(object,log_length,NULL,log);elseif(glIsProgram(object))glGetProgramInfoLog(object,log_length,NULL,log);fprintf(stderr,"%s",log);free(log);}
Abstraindo as diferenças entre OpenGL e GLES2editar
Quando você usar somente as funções da GLES2, seu aplicativo é quase portável para desktop e dispositivos móveis.
Existe ainda algumas questões para abordar:
A GLSL #version é diferente.
GLES2 requer alguns hints de precisão que não são compatíveis com a OpenGL 2.1
O #version precisa estar na primeira linha em alguns compiladores GLSL (por exemplo na PowerVR SGX540), assim não podemos usar a diretivas #ifdef para abstração dos shader GLSL, em vez disso, vamos preceder assim na versão em código C++:
Com novas funções utilitárias e conhecimento, nos podemos fazer outra função para carregar e depurar um shader:
/** * Compilando o shader pelo arquivo 'filename', com erros * Compile the shader from file 'filename', com tratamento de erros */GLuintcreate_shader(constchar*filename,GLenumtype){constGLchar*source=file_read(filename);if(source==NULL){fprintf(stderr,"Erro abrindo: %s: ",filename);perror("");return0;}GLuintres=glCreateShader(type);constGLchar*sources[2]={#ifdef GL_ES_VERSION_2_0"#version 100\n""#define GLES2\n",#else"#version 120\n",#endifsource};glShaderSource(res,2,sources,NULL);free((void*)source);glCompileShader(res);GLintcompile_ok=GL_FALSE;glGetShaderiv(res,GL_COMPILE_STATUS,&compile_ok);if(compile_ok==GL_FALSE){fprintf(stderr,"%s:",filename);print_log(res);glDeleteShader(res);return0;}returnres;}
Agora nós podemos compilar nossos shader simplesmente usando:
Colocar as novas funções em um arquivo separadoeditar
Nós colocaremos as novas funções no arquivo shader_utils.cpp
Note que nossa intenção ao escreve algumas das funções possíveis: O objetivo deste livro é entender como o OpenGL trabalha, não utilizar
um conjunto de ferramentas que desenvolvemos.
Usando Vertex Buffer Objects (VBO) para maior eficiênciaeditar
É uma boa pratica, guarda nossas vértices diretamente na placa gráfica, usando um Vertex Buffer Objetc (VBO).
Além disso, as "client-side array" suportadas são removidas desde do OpenGL 3.0, e é lento, por isto vamos usar os VBOs por agora, mesmo sendo uma vértice simples é bom conhecer eles, por causa que eles é muito usado em qualquer código OpenGL que você encontrar.
Nos implementaremos em dois passos:
Criando um VBO com nossos vértices.
Vincular nosso VBO para serem chamadas pela função glDrawArray
Criar uma variavel global (abaixo do #include) para gravar nosso manipulador de VBO:
GLuintvbo_triangle;
no init_resources, nós mudaremos a definição triangle_vertices , criando um (1) buffer de dados e fazendo dele o buffer atual:
Nós agora podemos puxar nossas vértices para o buffer. Nós especificaremos como os dados são organizados, e com que frequência será usado.
O GL_STATIC_DRAW indica como será a frequência do buffer, e que a GPU deve manter uma cópia do mesmo em sua memoria. É sempre possível escrever novos valores para o VBO. se os dados mudando uma vez por quadro ou mais, você poderia usar o GL_DYNAMIC_DRAW ou GL_STREAM_DRAW.
A qualquer momento podemos remover um buffer ativo como este:
glBindBuffer(GL_ARRAY_BUFFER,0);
Lembrando sempre, de desativar um buffer ativo toda vez que você quiser passar uma array C diretamente.
No onDisplay, nós adaptamos ligeiramente o código.
Nós chamaremos glBindBuffer, e modificaremos os últimos dois parâmetros pelo glVertexAttribPointer:
glBindBuffer(GL_ARRAY_BUFFER,vbo_triangle);glEnableVertexAttribArray(attribute_coord2d);/* Descreva nossas ordenação(array) de vértice para o OpenGL (ele não reconhece o formato automaticamente) */glVertexAttribPointer(attribute_coord2d,// atributo2,// numero de elementos por vértices, aqui é (x,y)GL_FLOAT,// o tipo de cada elementoGL_FALSE,// Como nossos valores estão.0,// sem dados extras em cada posição0// descolocamento do primeiro elemento);
Agora toda vez que desenharmos nossas cenas, o OpenGL automaticamente colocara nossa vértice na GPU. para cenas grandes, que tem muitos polígonos, teremos um aumento enorme de velocidade.
Alguns usuário podem não possuir uma placa gráfica que suporte o OpenGL 2.
Isto provavelmente fará que nosso programa faça termine inesperadamente ou mostre cenas incompletas.
Isto pode ser verificado usando o GLEW(depois de chamarmos com sucesso o glewInit()):
if(!GLEW_VERSION_2_0){fprintf(stderr,"Erro: Sua placa de vídeo não suporta OpenGL 2.0\n");return1;}
Note que alguns tutorais só poderão trabalhar com placas que suportem a versão próximas a 2.0, como a Intel 945GM com um suporte limitado nos shader mas com suporte oficial da OpenGL 1.4.
Se você não precisa carregar as extensões para OpenGL, e se cabeçalhos são bem recentes, você usar isto ao invés do GLEW
Nosso teste mostraram que os usuários do Windows podem ter cabeçalhos desatualizados, e faltando simbolos como o GL_VERTEX_SHADER, então vamos usar o GLEW nestes tutoriais (estaremos assim prontos para carregar as extensões).
Um usuário relatou que a utilização desta técnica, em vez de GLEW em uma Intel 945GM GPU permitido ignorar o suporte OpenGL 2.0 parcial para tutoriais simples.
pelo GLEW isto pode ser feito adicionando um suporte parcial através de glewExperimental = GL_TRUE; depois chamamos o glutInit.
Nosso programa tem uma manutenção mais facil agora, mas não é exatamente isto que queríamos!
Vamos então experimente uma pequena transparência, e mostraremos nosso triângulo com um efeito de "TV antiga".
O mod e o floor são operadores aritméticos comuns, usados para determinar se estamos dentro ou fora da linha.
Por isto uma das linhas é transparente e a outra opaca.