figuras - opengl ejemplos c++
Marco sencillo para OpenGL Shaders en C/C++ (4)
Solo quería probar algunos sombreadores en una imagen plana. Resulta que escribir un programa en C, que solo toma una imagen como una textura y se aplica, digamos un desenfoque gaussiano, como un sombreador de fragmentos no es tan fácil: tiene que inicializar OpenGL que son como 100 líneas de código, luego entendiendo los GLBuffers, etc. También para comunicarse con el sistema de ventanas uno tiene que usar GLUT que es otro marco.
Resulta que el compositor Fx de Nvidia es agradable para jugar con sombreadores ... Pero aún me gustaría tener un programa C o C ++ simple que simplemente aplica un sombreador de fragmentos dado a una imagen y muestra el resultado. ¿Alguien tiene un ejemplo o hay un marco?
Aunque no es su objetivo, puede obtener algo de la muestra del sombreador OpenGL ES 2.0 de Noel Llopis: http://www.mobileorchard.com/getting-started-with-opengl-es-20-on-the-iphone-3gs/
En primer lugar, evitaría utilizar exceso de recursos, tiene errores, no se ha actualizado en aproximadamente una década, y su diseño realmente no encaja muy bien con lo que la mayoría de la gente quiere hoy (por ejemplo, aunque puede usarlo para animaciones, en realidad está destinado principalmente a producir una visualización estática). Señalé una serie de alternativas al exceso en una respuesta anterior .
Eso (en su mayoría) deja el código para compilar, vincular y usar sombreadores. He escrito una clase pequeña que encuentro útil para este propósito:
class shader_prog {
GLuint vertex_shader, fragment_shader, prog;
template <int N>
GLuint compile(GLuint type, char const *(&source)[N]) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, N, source, NULL);
glCompileShader(shader);
GLint compiled;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
std::string log(length, '' '');
glGetShaderInfoLog(shader, length, &length, &log[0]);
throw std::logic_error(log);
return false;
}
return shader;
}
public:
template <int N, int M>
shader_prog(GLchar const *(&v_source)[N], GLchar const *(&f_source)[M]) {
vertex_shader = compile(GL_VERTEX_SHADER, v_source);
fragment_shader = compile(GL_FRAGMENT_SHADER, f_source);
prog = glCreateProgram();
glAttachShader(prog, vertex_shader);
glAttachShader(prog, fragment_shader);
glLinkProgram(prog);
}
operator GLuint() { return prog; }
void operator()() { glUseProgram(prog); }
~shader_prog() {
glDeleteProgram(prog);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
}
};
Para una demostración simple, un par de sombreadores "pass-through" (simplemente imiten la tubería de funcionalidad fija):
const GLchar *vertex_shader[] = {
"void main(void) {/n",
" gl_Position = ftransform();/n",
" gl_FrontColor = gl_Color;/n",
"}"
};
const GLchar *color_shader[] = {
"void main() {/n",
" gl_FragColor = gl_Color;/n",
"}"
};
Que usarías algo como:
void draw() {
// compile and link the specified shaders:
static shader_prog prog(vertex_shader, color_shader);
// Use the compiled shaders:
prog();
// Draw something:
glBegin(GL_TRIANGLES);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, 0.0f, -1.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 0.0f, -1.0f);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3d(0.0, -1.0, -1.0);
glEnd();
}
Si vas a utilizar, por ejemplo, varios sombreadores de fragmentos diferentes en el transcurso del dibujo de tu escena, simplemente defines un objeto estático para cada uno y luego ejecutas prog1();
, prog2();
, etc., antes de dibujar los objetos que desea sombrear con cada sombreador. P.ej,
void draw() {
static shader_prog wall_shader("wall_vertex", "wall_frag");
static shader_prog skin_shader("skin_vertex", "skin_frag");
wall_shader();
draw_walls();
skin_shader();
draw_skin();
}
Editar: Como @rotoglup señala correctamente, este uso de variables static
retrasa la destrucción hasta después de que se destruye el contexto OpenGL, por lo que cuando los destructores intentan utilizar glDeleteProgram
/ glDeleteShader
, los resultados son impredecibles (en el mejor de los casos).
Si bien esto puede ser excusable en un programa de demostración, es decididamente indeseable en el uso real. Al mismo tiempo, generalmente no desea volver a compilar su (s) sombreador (es) cada vez que ingrese las funciones que los utilizan.
Para evitar ambos problemas, generalmente desea crear sus objetos de sombreado como miembros de una instancia de clase cuya duración está, a su vez, vinculada a la vida útil de lo que va a sombrear:
class some_character_type {
shader_prog skin_shader;
public:
// ...
};
Esto compilará / vinculará el programa de sombreado una vez cuando crees un personaje de ese tipo, y lo destruirá cuando destruyas ese personaje.
Por supuesto, en algunos casos, esto tampoco es exactamente deseable. Solo por ejemplo, considere una versión en 3D de los antiguos juegos "matar a muchos objetivos" como Galaga o Centipede. Para juegos como este, estás creando y destruyendo muchos objetivos esencialmente idénticos con relativa rapidez. Dado un gran número de objetivos esencialmente idénticos, es probable que desee utilizar algo como shared_ptr<shader_prog>
para crear una sola instancia del sombreador que se comparte entre todas las instancias de un tipo de objetivo en particular. Dado que reutilizas los mismos tipos de objetivos muchas veces, es posible que quieras ir un poco más allá de eso, así que mantienes los mismos sombreados durante todo el juego, no solo cuando se muestra un tipo particular de objetivo.
En cualquier caso, nos estamos desviando un poco de aquí. El punto es que compilar y vincular sombreadores es un proceso bastante costoso, por lo que normalmente desea administrar su tiempo de vida para evitar crearlos y destruirlos mucho más a menudo de lo realmente necesario (aunque eso no quiere decir que sea crítico crearlos todos en el principio del juego y solo destruirlos al final, tampoco).
Este tutorial puede ser útil (tenga en cuenta que contiene material GLSL además del antiguo material Cg).
Tenga en cuenta que consideraría escribir sombreadores para implementar cosas no gráficas, de tipo GPGPU, para ser un enfoque obsoleto en estos días. OpenCL o CUDA son claramente el camino a seguir en el futuro.
Estuve en una posición similar hace un año y medio. Rápidamente encontré un tutorial sencillo y un código fuente para usar GLSL ... pero tenía que hacer funcionar GLUT y GLEW, y creo que terminé compilando al menos uno de esos. Como usaba Windows (y Windows es algo así como un caso especial no estándar que raramente recibe un tratamiento completo por parte de proyectos abiertos), también implicó un proceso ridículo en el que se esperaba copiar y pegar manualmente los archivos DLL y de cabecera ubicaciones. Siempre es un dolor y he perdido una buena parte de mi vida haciendo ese tipo de cosas, pero avancé a tientas el proceso según las indicaciones y al final funcionó, como suele ser el caso.
De todos modos, el ejemplo de shader más conveniente usando GLSL que puedo encontrar ahora es este - http://www.lighthouse3d.com/opengl/glsl/index.php?minimal
No modifica específicamente una textura como desees ... pero en mi experiencia, una vez que obtienes un código como este compilando y corriendo, la experiencia será más placentera y rápidamente progresarás y serás capaz de empalmar piezas de otros tutoriales si necesario. Puedo decir que una vez que obtuve un ejemplo en ejecución, utilicé ese mismo marco para resolver rápidamente muchos problemas en el hogar y el trabajo.
Lamento decir que hace uso de GLUT y GLEW. Si obtiene una mejor respuesta a esta pregunta, yo también me convertiré en un fanático instantáneo de cualquier sitio que ofrezca el código. Buena suerte.