triangle texture descargar c++ opengl glsl

descargar - texture in opengl c++



Cómo enviar prácticamente sombreadores GLSL con su software C++ (10)

El problema es que es difícil editar, depurar y mantener sombreadores GLSL directamente en una cadena.

Es extraño que esta frase haya sido totalmente ignorada por todas las "respuestas" hasta el momento, mientras que el tema recurrente de esas respuestas ha sido: "No se puede resolver el problema, solo lidiar con eso".

La respuesta para facilitar su edición, al cargarlos directamente desde una cadena, es simple. Considere la siguiente cadena literal:

const char* gonFrag1 = R"(#version 330 // Shader code goes here // and newlines are fine, too!)";

Todos los demás comentarios son correctos en la medida de lo posible. De hecho, como dicen, la mejor seguridad disponible es la oscuridad, ya que GL puede ser interceptada. Pero para mantener honestas a las personas honestas y para poner un poco de bloqueo en el camino del daño accidental al programa, puedes hacer lo que se indica arriba en C ++, y aún así mantener fácilmente tu código.

Por supuesto, si usted quisiera proteger el sombreador más revolucionario del mundo contra el robo, la oscuridad podría ser llevada a extremos bastante efectivos. Pero esa es otra pregunta para otro hilo.

Durante la inicialización de OpenGL, se supone que el programa debe hacer algo como:

<Get Shader Source Code> <Create Shader> <Attach Source Code To Shader> <Compile Shader>

Obtener el código fuente podría ser tan simple como ponerlo en una cadena como: (Ejemplo tomado de SuperBible, 6ta Edición )

static const char * vs_source[] = { "#version 420 core /n" " /n" "void main(void) /n" "{ /n" " gl_Position = vec4(0.0, 0.0, 0.0, 1.0); /n" "} /n" };

El problema es que es difícil editar, depurar y mantener sombreadores GLSL directamente en una cadena. Así que obtener el código fuente en una cadena de un archivo es más fácil para el desarrollo:

std::ifstream vertexShaderFile("vertex.glsl"); std::ostringstream vertexBuffer; vertexBuffer << vertexShaderFile.rdbuf(); std::string vertexBufferStr = vertexBuffer.str(); // Warning: safe only until vertexBufferStr is destroyed or modified const GLchar *vertexSource = vertexBufferStr.c_str();

El problema ahora es cómo enviar los sombreadores con su programa? De hecho, el envío del código fuente con su aplicación puede ser un problema. OpenGL admite "sombreadores binarios precompilados", pero el Open Wiki indica que:

Los formatos binarios del programa no están destinados a ser transmitidos. No es razonable esperar que diferentes proveedores de hardware acepten los mismos formatos binarios. No es razonable esperar que diferentes hardware del mismo proveedor acepten los mismos formatos binarios. [...]

¿Cómo enviar sombreadores GLSL prácticamente con su software C ++?


Como una alternativa para mantener sombreadores GLSL directamente en una cadena, sugeriría considerar esta biblioteca que estoy desarrollando: ShaderBoiler (Apache-2.0).

Está en versión alfa y tiene algunas limitaciones que pueden restringir su uso.

El concepto principal es escribir en construcciones de C ++ similares al código GLSL, que construirían un gráfico de computación a partir del cual se genera el código GLSL final.

Por ejemplo, consideremos el siguiente código C ++

#include <shaderboiler.h> #include <iostream> void main() { using namespace sb; context ctx; vec3 AlbedoColor = ctx.uniform<vec3>("AlbedoColor"); vec3 AmbientLightColor = ctx.uniform<vec3>("AmbientLightColor"); vec3 DirectLightColor = ctx.uniform<vec3>("DirectLightColor"); vec3 LightPosition = ctx.uniform<vec3>("LightPosition"); vec3 normal = ctx.in<vec3>("normal"); vec3 position = ctx.in<vec3>("position"); vec4& color = ctx.out<vec4>("color"); vec3 normalized_normal = normalize(normal); vec3 fragmentToLight = LightPosition - position; Float squaredDistance = dot(fragmentToLight, fragmentToLight); vec3 normalized_fragmentToLight = fragmentToLight / sqrt(squaredDistance); Float NdotL = dot(normal, normalized_fragmentToLight); vec3 DiffuseTerm = max(NdotL, 0.0) * DirectLightColor / squaredDistance; color = vec4(AlbedoColor * (AmbientLightColor + DiffuseTerm), 1.0); std::cout << ctx.genShader(); }

La salida a la consola será:

uniform vec3 AlbedoColor; uniform vec3 AmbientLightColor; uniform vec3 LightPosition; uniform vec3 DirectLightColor; in vec3 normal; in vec3 position; out vec4 color; void main(void) { vec3 sb_b = LightPosition - position; float sb_a = dot(sb_b, sb_b); color = vec4(AlbedoColor * (AmbientLightColor + max(dot(normal, sb_b / sqrt(sb_a)), 0.0000000) * DirectLightColor / sb_a), 1.000000); }

La cadena creada con el código GLSL se puede usar con la API de OpenGL para crear un sombreador.


Con c ++ 11, también puede usar la nueva función de literales de cadena sin formato. Coloque este código fuente en un archivo separado llamado shader.vs :

R"( #version 420 core void main(void) { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); } )"

y luego importarlo como una cadena como esta:

const std::string vs_source = #include "shader.vs" ;

La ventaja es que es fácil de mantener y depurar, y obtienes los números de línea correctos en caso de errores del compilador de sombreadores OpenGL. Y aún no necesita enviar sombreadores por separado.

La única desventaja que puedo ver son las líneas adicionales en la parte superior e inferior del archivo ( R") y )" ) y la sintaxis que es un poco extraña para convertir la cadena en código C ++.


Mi sugerencia sería hacer que la incorporación de shader en tu binario sea parte de tu proceso de compilación. Uso CMake en mi código para escanear una carpeta de archivos fuente de sombreado y luego generar un encabezado con una enumeración de todos los sombreadores disponibles:

#pragma once enum ShaderResource { LIT_VS, LIT_FS, // ... NO_SHADER }; const std::string & getShaderPath(ShaderResource shader);

Del mismo modo, CMake crea un archivo CPP que, dado un recurso, devuelve la ruta del archivo al sombreador.

const string & getShaderPath(ShaderResource res) { static map<ShaderResource, string> fileMap; static bool init = true; if (init) { init = false; fileMap[LIT_VS] = "C:/Users/bdavis/Git/OculusRiftExamples/source/common/Lit.vs"; // ... } return fileMap[res]; }

No sería demasiado difícil (mucho trabajo manual) hacer que el script CMake modifique su comportamiento para que en una compilación de lanzamiento en lugar de proporcionar la ruta del archivo proporcione el origen del sombreador, y en el archivo cpp almacenado el contenido del los propios sombreadores (o en el caso de un blanco de Windows o Apple los hacen parte del paquete ejecutable / recursos ejecutables).

La ventaja de este enfoque es que es mucho más fácil modificar los sombreadores sobre la marcha durante la depuración si no están integrados en el ejecutable. De hecho, mi código de búsqueda del programa GLSL realmente mira el tiempo de compilación del sombreador frente a las marcas de tiempo modificadas de los archivos fuente y volverá a cargar el sombreador si los archivos han cambiado desde la última vez que se compiló (esto todavía está en pañales, significa que pierdes los uniformes que antes estaban vinculados al sombreador, pero estoy trabajando en eso).

Esto es realmente menos un problema de sombreado que un problema genérico de ''recursos no relacionados con C ++''. El mismo problema existe con todo lo que le gustaría cargar y procesar ... imágenes de texturas, archivos de sonido, niveles, qué tiene usted.


No sé si eso funcionará, pero podrías insertar el archivo .vs en tu ejecutable con binutils como un programa como g2bin, y puedes declarar tus programas de sombreado como externos y luego acceder a ellos como recursos normales incrustados en el ejecutable. Vea qrc en Qt, o puede ver mi pequeño programa para incrustar cosas en ejecutables aquí: https://github.com/heatblazer/binutil que se invoca como un comando de precompilación para el IDE.


OpenGL admite binarios precompilados, pero no de forma portátil. A diferencia de HLSL, que se compila en un formato de código byt estándar por el compilador de Microsoft y luego se traduce en una instrucción nativa de la GPU establecida por el controlador, OpenGL no tiene ese formato. No puede usar binarios precompilados para nada más que el almacenamiento en caché de sombreadores GLSL compilados en una sola máquina para acelerar el tiempo de carga, y aun así no hay garantía de que el binario compilado funcionará si la versión del controlador cambia ... mucho menos la la GPU real en la máquina cambia.

Siempre puedes ofuscar tus sombreadores si eres realmente paranoico. La cuestión es que, a menos que estés haciendo algo verdaderamente único, a nadie le importará tu sombreador y lo digo sinceramente. Esta industria se nutre de la apertura, todos los grandes actores de la industria discuten regularmente las técnicas más novedosas y más interesantes en conferencias como GDC, SIGGRAPH, etc. De hecho, los sombreadores son tan específicos de la implementación que a menudo no hay mucho que se puede hacer desde revertirlos a la ingeniería que no podría hacer simplemente escuchando una de dichas conferencias.

Si su preocupación es que las personas modifiquen su software, entonces le sugiero que implemente una simple prueba de suma de comprobación o suma de comprobación. Muchos juegos ya hacen esto para evitar las trampas, lo lejos que quieras llevarlo depende de ti. Pero la conclusión es que los sombreadores binarios en OpenGL están destinados a reducir el tiempo de compilación del sombreador, no para la redistribución portátil.


Otra alternativa para almacenar archivos de texto glsl o archivos glsl precompilados es un generador de sombreado, que toma un árbol de sombra como entrada y emite código glsl (o hlsl, ...), que luego se compila y vincula en tiempo de ejecución ... Siguiendo este enfoque puede adaptarse más fácilmente a las capacidades que tenga el hardware gfx. También puede admitir hlsl, si tiene mucho tiempo, no necesita el idioma de sombreado CG. Si piensas en glsl / hlsl con la profundidad suficiente, verás que la transformación de los árboles de sombra en código fuente estaba en la parte posterior de las mentes de los diseñadores de lenguaje.


Simplemente hay que "almacenarlos directamente en el ejecutable" o "guardarlos en (a) archivos separados", sin nada en el medio. Si quieres un ejecutable autónomo, ponerlos en el binario es una buena idea. Tenga en cuenta que puede agregarlos como recursos o ajustar su sistema de compilación para incrustar las cadenas de sombreado de archivos de desarrollo separados en archivos fuente para facilitar el desarrollo (con la posible adición de poder cargar directamente los archivos separados en compilaciones de desarrollo).

¿Por qué crees que enviar las fuentes del sombreador sería un problema? Simplemente no hay otra manera en el GL. Los archivos binarios precompilados solo son útiles para almacenar en caché los resultados de la compilación en la máquina de destino. Con los avances rápidos de la tecnología GPU, y el cambio de arquitecturas GPU, y diferentes proveedores con ISA totalmente incompatibles, los binarios shader precompilados no tienen ningún sentido.

Tenga en cuenta que poner sus fuentes de sombreado en el ejecutable no las "protege", incluso si las encripta. Un usuario aún puede engancharse en la biblioteca GL e interceptar las fuentes que especifique para el GL. Y los depuradores GL hacen exactamente eso.

ACTUALIZACIÓN 2016

En SIGGRAPH 2016, el OpenGL Architecture Review Board lanzó la extensión GL_ARB_gl_spirv . Esto permitirá una implementación GL para usar el lenguaje intermedio binario GL_ARB_gl_spirv . Esto tiene algunos beneficios potenciales:

  1. Los sombreadores se pueden "pre-compilar" sin conexión (la compilación final para la GPU de destino aún la realiza el controlador más adelante). No tiene que enviar el código fuente del sombreador, sino solo la representación intermedia binaria.
  2. Hay un frontend de compilador estándar ( glslang ) que hace el análisis sintáctico, por lo que se pueden eliminar las diferencias entre los analizadores de diferentes implementaciones.
  3. Se pueden agregar más lenguajes de sombreado, sin la necesidad de cambiar las implementaciones de GL.
  4. Aumenta de alguna manera la portabilidad a vulkan.

Con ese esquema, GL se está volviendo más similar a D3D y Vulkan en ese sentido. Sin embargo, no cambia la imagen más grande. El bytecode SPIRV aún puede ser interceptado, desmontado y decompilado. Hace la ingeniería inversa un poco más difícil, pero no mucho en realidad. En un sombreado, por lo general no puede permitirse amplias medidas de ocultación, ya que eso reduce drásticamente el rendimiento, lo que es contrario a lo que son los sombreadores.

También tenga en cuenta que esta extensión no está disponible en este momento (otoño de 2016). Y Apple dejó de soportar GL después de 4.1, por lo que esta extensión probablemente nunca llegue a OSX.

ACTUALIZACIÓN MENOR 2017

GL_ARB_gl_spirv es ahora la característica central oficial de OpenGL 4.6 , por lo que podemos esperar una tasa de adopción creciente para esta característica, pero no cambia la imagen más grande por mucho.


También puede combinar varias fuentes de sombreado en un archivo (o cadena) usando directivas de preprocesador si no desea mantenerlas separadas. Esto también le permite evitar la repetición (por ejemplo, declaraciones compartidas): el compilador optimiza las variables no utilizadas la mayor parte del tiempo.

Ver http://www.gamedev.net/topic/651404-shaders-glsl-in-one-file-is-it-practical/


Una sugerencia:

En tu programa, coloca el sombreador en:

const char shader_code = { #include "shader_code.data" , 0x00};

En shader_code.data debe existir el código fuente del sombreador como una lista o números hexadecimales separados por comas. Estos archivos deben crearse antes de la compilación usando su código de sombreado escrito normalmente en un archivo. En Linux pondría instrucciones en Makefile para ejecutar el código:

cat shader_code.glsl | xxd -i > shader_code.data