Se ha sugerido en comentarios repetidamente, pero nadie sintió la necesidad de dar una respuesta adecuada, por lo que, en aras de la exhaustividad, una solución directa y común a este problema podría ser usar una textura como tabla de búsqueda, específicamente una textura 1D que contiene todos los valores de su función para el posible rango de entrada (es decir, / ). Esto tiene varias ventajas:[0,360)[0,2π)
- Utiliza coordenadas normalizadas, es decir, accede a la textura mapeando sus ángulos de a . Esto significa que su sombreador no tiene que preocuparse por la cantidad específica de valores . Puede ajustar su tamaño de acuerdo con la memoria / velocidad frente a la compensación de calidad que desee (y especialmente en hardware antiguo / incrustado, de todos modos es posible que desee una potencia de dos como tamaño de textura).[0,360][0,1]
- Obtiene el beneficio adicional de no tener que hacer su ajuste de intervalo en forma de bucle (aunque, de todos modos, no necesitaría bucles y podría usar una operación de módulo). Solo utilícelo
GL_REPEAT
como modo de ajuste para la textura y comenzará automáticamente al principio nuevamente cuando acceda con argumentos> 1 (y de manera similar para argumentos negativos).
- Y también obtienes el beneficio de interpolar linealmente entre dos valores en la matriz básicamente de forma gratuita (o digamos casi gratis) al usar
GL_LINEAR
como filtro de textura, de esta manera obtienes valores que ni siquiera almacenaste. Por supuesto, la interpolación lineal no es 100% precisa para las funciones trigonométricas, pero ciertamente es mejor que ninguna interpolación.
- Puede almacenar más de un valor en la textura utilizando una textura RGBA (o la cantidad de componentes que necesite). De esta manera, puede obtener, por ejemplo, sin y cos con una sola búsqueda de textura.
- Para sin y cos solo necesita almacenar valores en todos modos, lo que naturalmente puede ampliar desde el rango normalizado de un formato común de punto fijo de 8 bits. Sin embargo, eso podría no ser suficiente precisión para sus necesidades. Algunas personas sugirieron usar valores de punto flotante de 16 bits, ya que son más precisos que los valores de punto fijo normalizados de 8 bits habituales pero menos intensivos en memoria que los flotadores reales de 32 bits. Pero, de nuevo, tampoco sé si su implementación admite texturas de punto flotante para empezar. De lo contrario, tal vez pueda usar 2 componentes de punto fijo de 8 bits y combinarlos en un solo valor con algo como (o incluso más componentes para un grano más fino). Esto le permite beneficiarse de las texturas de múltiples componentes una vez más.[−1,1][0,1]
float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;
Por supuesto, todavía tiene que evaluarse si esta es una mejor solución, ya que el acceso a la textura tampoco es totalmente gratuito, así como cuál sería la mejor combinación de tamaño y formato de textura.
En cuanto a rellenar la textura con datos y abordar uno de sus comentarios, debe tener en cuenta que el filtrado de textura devuelve el valor exacto en el centro de texel , es decir, una coordenada de textura a la mitad del tamaño del texel. Entonces sí, debe generar valores en .5
texels , es decir, algo así en el código de la aplicación:
float texels[256];
for(unsigned int i = 0; i < 256; ++i)
texels[i] = sin((i + .5f) / 256.f) * TWO_PI);
glTexImage1D(GL_TEXTURE_1D, 0, ..., 256, 0, GL_RED, GL_FLOAT, texels);
Sin embargo, es posible que aún desee comparar el rendimiento de este enfoque con un enfoque que usa una matriz uniforme pequeña (es decir uniform float sinTable[361]
, o tal vez menos en la práctica, vigile el límite de su implementación en el tamaño de matriz uniforme) que simplemente carga con los valores respectivos usando glUniform1fv
y acceda ajustando su ángulo a usando la función y redondeándolo al valor más cercano:[0,360)mod
angle = mod(angle, 360.0);
float value = sinTable[int(((angle < 0.0) ? (angle + 360.0) : angle) + 0.5)];